SSH CAs

Certificates in the context of SSH is used to authenticate users (clients) in a more systematic way than simply adding public keys to a authorized_keys file, which doesn't scale and is a terrible security practice. They are pain to add when more than one machine is involved, and equally difficult to remove when a user no longer should have access to a device.

Using certificates avoids the need to distribute public keys, both user and host (server). For the user put the hosts public key in the ~/.ssh/known_hosts file, e.g.,

@cert-authority *.avassa.io ecdsa-sha2-nistp256 AAAAE2VjZHN...

and on each host (/etc/ssh/sshd_config) add the CA public key and the hosts private key and certificate, e.g.,

# Path to the CA public key for verifying user certificates
TrustedUserCAKeys /etc/ssh/ssh_user_key.pub

# Path to this host's private key and certificate
HostKey /etc/ssh/ssh_host_ecdsa_key
HostCertificate /etc/ssh/ssh_host_ecdsa_key-cert.pub

Client certificates should be short lived, typically only valid for a work day. That way access is automatically removed and there is no risk of users having access to machines long after they have quit. It is a good idea to use a sign in process where a user certificate for the day is generated.

However, it is also possible to have a list of revoked certificates, e.g., in the /etc/ssh/sshd_config

# File containing public keys of revoked certificates
RevokedKeys /etc/ssh/revoked_keys

See also https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.certkeys

OTPs

An alternative SSH authentication scheme is to use OTPs. This service can be used to generate and validate OTPs as well as generating SSH certificates.

To setup SSH with OTPs first configure PAM to use an external program to authenticate, ie pam_exec.so, secondly configure ssh to use PAM and challenge-response authentication, e.g., in /etc/ssh/sshd_config

ChallengeResponseAuthentication yes
UsePAM yes
PasswordAuthentication no

And in /etc/pam.d/sshd comment out common-auth and add call to custom callback

# Standard Un*x authentication.
#@include common-auth
auth requisite pam_exec.so quiet expose_authtok log=/var/log/otp-ssh.log /usr/local/bin/otp-validate.sh
auth optional pam_unix.so not_set_pass use_first_pass nodelay

The /usr/local/bin/otp-validate.sh should:

  1. read the OTP from stdin
  2. invoke the strongbox validate-otp action with the OTP
  3. check the returned username matches PAM_USER
  4. check that the returned ip matches the host
  5. optionally verify that the role-name is the expected

A few things to note.

  • It is important that the otp-validate.sh program validates the certificate of the strongbox server. It should be validate against the root certificate returned by get-api-ca-cert.
  • If connectivity to the strongbox server is down, then it will not be possible to login to the host using OTP. It might be a good idea to allow root to login using ssh keys in order to debug.

Create a new ssh ca

SecurityaccessToken
Request
query Parameters
validate
string <enumeration>

Validate the request but do not actually perform the requested operation

Value: "true"
Request Body schema:
One of:
key-type
string <enumeration>
Default: "ecdsa"
  • rsa
  • ecdsa
  • ed25519
key-curve
string <enumeration>
Default: "nistp256"
  • nistp256
  • nistp384
  • nistp521
key-size
integer <uint16>
Default: 2048
name
string <name>
to (object) or sites (object) or deployments (object)
Responses
201

Created

400

Bad Request

401

Unauthorized

403

Forbidden

404

Not Found

409

Conflict (instance exists)

503

Service Unavailable (strongbox sealed)

post/v1/config/strongbox/ssh/ca
Request samples
name: ca-1
key-type: ecdsa
key-curve: nistp256
key-size: 2048
distribute:
  to: none

Retrieve the configuration of all ssh cas

SecurityaccessToken
Request
query Parameters
fields
string

Retrieve only requested fields from the resource

See section fields

validate
string <enumeration>

Validate the request but do not actually perform the requested operation

Value: "true"
keys
string <enumeration>

Retrieve only the keys for the list

Value: "true"
count
string <enumeration>

Retrieve only the number of elements in the list

Value: "true"
Responses
200

OK

400

Bad Request

401

Unauthorized

403

Forbidden

404

Not Found

412

Precondition Failed

503

Service Unavailable (strongbox sealed)

get/v1/config/strongbox/ssh/ca
Response samples
- name: ca-1
  key-type: ecdsa
  key-curve: nistp256
  key-size: 2048
  distribute:
    to: none
  

Update an ssh ca

SecurityaccessToken
Request
path Parameters
ssh-ca-name
required
string <name>

name of ssh-ca

query Parameters
validate
string <enumeration>

Validate the request but do not actually perform the requested operation

Value: "true"
Request Body schema:
One of:
key-type
string <enumeration>
Default: "ecdsa"
  • rsa
  • ecdsa
  • ed25519
key-curve
string <enumeration>
Default: "nistp256"
  • nistp256
  • nistp384
  • nistp521
key-size
integer <uint16>
Default: 2048
name
string <name>
to (object) or sites (object) or deployments (object)
Responses
204

No Content

400

Bad Request

401

Unauthorized

403

Forbidden

404

Not Found

412

Precondition Failed

503

Service Unavailable (strongbox sealed)

patch/v1/config/strongbox/ssh/ca/{ssh-ca-name}
Request samples
name: ca-1
key-type: ecdsa
key-curve: nistp256
key-size: 2048
distribute:
  to: none

Delete an ssh ca

SecurityaccessToken
Request
path Parameters
ssh-ca-name
required
string <name>

name of ssh-ca

query Parameters
validate
string <enumeration>

Validate the request but do not actually perform the requested operation

Value: "true"
Responses
204

No Content

400

Bad Request

401

Unauthorized

403

Forbidden

404

Not Found

412

Precondition Failed

503

Service Unavailable (strongbox sealed)

delete/v1/config/strongbox/ssh/ca/{ssh-ca-name}

Replace or create a new ssh ca

SecurityaccessToken
Request
path Parameters
ssh-ca-name
required
string <name>

name of ssh-ca

query Parameters
validate
string <enumeration>

Validate the request but do not actually perform the requested operation

Value: "true"
Request Body schema:
One of:
key-type
string <enumeration>
Default: "ecdsa"
  • rsa
  • ecdsa
  • ed25519
key-curve
string <enumeration>
Default: "nistp256"
  • nistp256
  • nistp384
  • nistp521
key-size
integer <uint16>
Default: 2048
name
string <name>
to (object) or sites (object) or deployments (object)
Responses
201

Created

204

No Content

400

Bad Request

401

Unauthorized

403

Forbidden

404

Not Found

412

Precondition Failed

503

Service Unavailable (strongbox sealed)

put/v1/config/strongbox/ssh/ca/{ssh-ca-name}
Request samples
name: ca-1
key-type: ecdsa
key-curve: nistp256
key-size: 2048
distribute:
  to: none

Retrieve the configuration of an ssh ca

SecurityaccessToken
Request
path Parameters
ssh-ca-name
required
string <name>

name of ssh-ca

query Parameters
fields
string

Retrieve only requested fields from the resource

See section fields

validate
string <enumeration>

Validate the request but do not actually perform the requested operation

Value: "true"
Responses
200

OK

400

Bad Request

401

Unauthorized

403

Forbidden

404

Not Found

412

Precondition Failed

503

Service Unavailable (strongbox sealed)

get/v1/config/strongbox/ssh/ca/{ssh-ca-name}
Response samples
name: ca-1
key-type: ecdsa
key-curve: nistp256
key-size: 2048
distribute:
  to: none

Retrieve the state of all ssh cas

SecurityaccessToken
Request
query Parameters
fields
string

Retrieve only requested fields from the resource

See section fields

site
string

Send the request to the specfifed site

content
string <enumeration>

Filter descendant nodes in the response

Enum: "config" "nonconfig"
keys
string <enumeration>

Retrieve only the keys for the list

Value: "true"
count
string <enumeration>

Retrieve only the number of elements in the list

Value: "true"
Responses
200

OK

400

Bad Request

401

Unauthorized

403

Forbidden

404

Not Found

503

Service Unavailable (strongbox sealed)

get/v1/state/strongbox/ssh/ca
Response samples
- name: ca-1
  key-type: ecdsa
  key-curve: nistp256
  key-size: 2048
  public-key64: |
    ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHSLaSvbPs7OwB1E6eTvUlvKP+zt1K9GnuYtPvkmuaN/poh3AXcF2mx/213GEvwiUrn893Och8+izAXdo9NyNGc= strongbox
  serial: 0
  distribute:
    to: none
  distribution-status:
    to: none
  

Retrieve the state of an ssh ca

SecurityaccessToken
Request
path Parameters
ssh-ca-name
required
string <name>

name of ssh-ca

query Parameters
fields
string

Retrieve only requested fields from the resource

See section fields

site
string

Send the request to the specfifed site

content
string <enumeration>

Filter descendant nodes in the response

Enum: "config" "nonconfig"
Responses
200

OK

400

Bad Request

401

Unauthorized

403

Forbidden

404

Not Found

503

Service Unavailable (strongbox sealed)

get/v1/state/strongbox/ssh/ca/{ssh-ca-name}
Response samples
name: ca-1
key-type: ecdsa
key-curve: nistp256
key-size: 2048
public-key64: |
  ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHSLaSvbPs7OwB1E6eTvUlvKP+zt1K9GnuYtPvkmuaN/poh3AXcF2mx/213GEvwiUrn893Och8+izAXdo9NyNGc= strongbox
serial: 0
distribute:
  to: none
distribution-status:
  to: none

Invoke the issue-cert operation

SecurityaccessToken
Request
path Parameters
ssh-ca-name
required
string <name>

name of ssh-ca

Request Body schema:
public-key
string
ttl
string <duration>

A duration in years, days, hours, minutes and seconds.

Format is [<digits>y][<digits>d][<digits>m][<digits>s].

Examples: 1y2d5h, 5h or 10m30s

valid-principals
required
Array of strings
cert-type
string <enumeration>
Default: "user"
  • user
  • host
key-id
string

key id is a free-form text field that is filled in by the CA at the time of signing; the intention is that the contents of this field are used to identify the identity principal in log messages.

critical-options
Array of strings
extensions
Array of strings

For example no-presence-required, permit-X11-forwarding, permit-agent-forwarding, permit-port-forwarding, permit-pty, permit-user-rc

Responses
200

OK

400

Bad Request

401

Unauthorized

403

Forbidden

404

Not Found

503

Service Unavailable (strongbox sealed)

post/v1/state/strongbox/ssh/ca/{ssh-ca-name}/issue-cert
Request samples
public-key: |
  ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDzGm8UaHf1vbDr4J4OYMivONjA9GHjEw
  il8RER57cIrh2OIObGixCiKlqUUUrAdjqa7z1VUb0Xfsn3wu5+0VY5F/XNai7MvTNappSx
  QDN0vRzLzDMrdkFskscYVcw/Cfp/xo36nXl4IJLrOB/F6CZRsgP1Mq3YH3tEO7uU71uLTd
  1kSYh7w/2g4ujJ4X10XMaLG3+UfTGPjWj/YXsSHKYtGctUDt0U+7AjmM9jz4Ult1XXHHvU
  3rRm5fXaNbEsIZxEX/R7Gf090GmRNuJeKD7sCFT2trgepOOJqCYqUZZPbDNbO5ElM2VlK/
  1AAzDgWPSMuZmSw1ibg3OyZsQcoHTr jb@tio
ttl: 12h
valid-principals:
  - ubuntu
cert-type: user
key-id: admin-ssh
critical-options: []
extensions:
  - permit-X11-forwarding
  - permit-pty
Response samples
cert: |
  ecdsa-sha2-nistp521-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHA1MjEtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgX26CCgmZ1ZuH3WjnfKapLAdXkQvw3TL7YAqGZ0vKGzAAAAAIbmlzdHA1MjEAAACFBACkktLRt9W7CntAF0jAenkEdRswTyBy2lNSTA3z2iEeQsG/++vD6afN6j7ODOvbl95ykNXaC4st1DjpFmIcl4j0LQAKDle9X2DpPkOfKdmtUHu9kQODnq9Q8ByI8CXrVzSftTnnxI8ABnPI6PJzC7mzeCmtz9SyuHyAXOzaPQIY7nfVEwAAAAAAAAACAAAAAQAAABh1c2VycGFzcy1hZG1pbkB0ZWxjby5jb20AAAAKAAAABnVidW50dQAAAAAAAAAAAAAAAGHe1VIAAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQR0i2kr2z7OzsAdROnk71Jbyj/s7dSvRp7mLT75Jrmjf6aIdwF3Bdpsf9tdxhL8IlK5/PdznIfPoswF3aPTcjRnAAAAZAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAASQAAACAip89yj0Ba2NnDVi+5MyEyf4vHSnS1laEXoKU9ToHC9AAAACEA2aK07GT771qnINmS58xMSQJgHDbz13ihHFaqSTuigBU= userpass-admin@telco.com
public-key: |
  ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACkktLRt9W7CntAF0jAenkEdRswTyBy2lNSTA3z2iEeQsG/++vD6afN6j7ODOvbl95ykNXaC4st1DjpFmIcl4j0LQAKDle9X2DpPkOfKdmtUHu9kQODnq9Q8ByI8CXrVzSftTnnxI8ABnPI6PJzC7mzeCmtz9SyuHyAXOzaPQIY7nfVEw== ubuntu
private-key: |
  -----BEGIN OPENSSH PRIVATE KEY-----
  b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
  1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQApJLS0bfVuwp7QBdIwHp5BHUbME8g
  ctpTUkwN89ohHkLBv/vrw+mnzeo+zgzr25fecpDV2guLLdQ46RZiHJeI9C0ACg5XvV9g6T
  5DnynZrVB7vZEDg56vUPAciPAl61c0n7U558SPAAZzyOjycwu5s3gprc/Usrh8gFzs2j0C
  GO531RMAAAEAD4hlXw+IZV8AAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
  AAAIUEAKSS0tG31bsKe0AXSMB6eQR1GzBPIHLaU1JMDfPaIR5Cwb/768Ppp83qPs4M69uX
  3nKQ1doLiy3UOOkWYhyXiPQtAAoOV71fYOk+Q58p2a1Qe72RA4Oer1DwHIjwJetXNJ+1Oe
  fEjwAGc8jo8nMLubN4Ka3P1LK4fIBc7No9Ahjud9UTAAAAQRixUumLl0HFS3r19GQQJmJl
  +ZyhYXUcG+B8C9zs6yM+BQ2fG3g3FHJM9fkQ/+/8QbHqD8pU3oWZwg3aitp0uZ+mAAAAAA
  ECAw==
  -----END OPENSSH PRIVATE KEY-----
ca-public-key: |
  ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHSLaSvbPs7OwB1E6eTvUlvKP+zt1K9GnuYtPvkmuaN/poh3AXcF2mx/213GEvwiUrn893Och8+izAXdo9NyNGc= strongbox
serial: 2
expires: 2022-01-27T09:57:48.000000Z