Connect SSH for people and machines¶
Sign people in to sshd with opkssh and machines with OpenBAO-signed certificates. sluis never signs an SSH key.
Before you start¶
- opkssh refuses ES384, the issuer default. Pin
signing_algon the opkssh client row. - opkssh compares only the last
:segment of a group name. Pingroups_delimiter: "."and writeauth_idlines with dots. - Each server must reach the issuer's discovery document and JWKS on every verification.
These steps use opkssh v0.16.0.
People: opkssh¶
clients:
opkssh:
kind: public
redirects:
- http://localhost:3000/login-callback
- http://localhost:10001/login-callback
- http://localhost:11110/login-callback
requires: [devel:build-worker:operator, devel:router:operator]
signing_alg: ES256 # or RS256
groups_delimiter: "."
display_name: opkssh
Declare the public client row. See signing algorithm per audience and groups delimiter. Remove the delimiter once opkssh stops splitting on every :.
On each client machine, add the provider to ~/.opk/config.yml. opkssh login --create-config writes a skeleton.
default_provider: sluis
providers:
- alias: sluis
issuer: https://access.example
client_id: opkssh
scopes: openid email profile groups
redirect_uris:
- http://localhost:3000/login-callback
- http://localhost:10001/login-callback
- http://localhost:11110/login-callback
The ID token carries groups for any scope, and email only with the email scope.
On each server, install opkssh, which creates the low-privilege opksshuser, then wire it into sshd.
# /etc/ssh/sshd_config
AuthorizedKeysCommand /usr/local/bin/opkssh verify %u %k %t
AuthorizedKeysCommandUser opksshuser
# /etc/opk/providers — issuer, client_id, expiration policy
https://access.example opkssh 24h
# /etc/opk/auth_id — principal identity issuer
ops oidc:groups:devel.build-worker.operator https://access.example
Reload sshd. 24h matches the issuer's session limit. Add auth_id lines by hand or with sudo opkssh add ops oidc:groups:devel.build-worker.operator sluis.
Host groups¶
Name groups <scope>:<thing>:<role> (naming); a host kind is the thing.
groups:
devel:build-worker:user: { members: [[email protected]] }
devel:build-worker:operator: { members: [[email protected]] }
devel:build-worker:admin: { members: [[email protected]] }
Without a vocabulary, groups are flat: repeat one on each auth_id line it should reach.
ops oidc:groups:devel.build-worker.user https://access.example
ops oidc:groups:devel.build-worker.operator https://access.example
ops oidc:groups:devel.build-worker.admin https://access.example
root oidc:groups:devel.build-worker.admin https://access.example
With a declared vocabulary such as roles: { user: [], operator: [user], admin: [operator] }, an admin also holds the lower roles. Two lines suffice.
ops oidc:groups:devel.build-worker.user https://access.example
root oidc:groups:devel.build-worker.admin https://access.example
Machines: OpenBAO-signed certificates¶
A job has no browser. sluisctl bao exchanges the job's token and logs in to OpenBAO (OpenBAO, GitHub Actions, Kubernetes cluster). For an interactive session:
sluisctl bao --address https://openbao.example:8200 ssh -mode=ca -ns=staging -role=user [email protected]
For scp, git, CI and Ansible, sign an existing public key:
ssh-keygen -t ed25519 -f id_ci -N ''
sluisctl bao --address https://openbao.example:8200 write -ns=staging -field=signed_key \
ssh/sign/user valid_principals=ci public_key=@id_ci.pub > id_ci-cert.pub
scp -i id_ci [email protected]:backup.tar.gz . # ssh reads id_ci-cert.pub beside id_ci
The role's ttl, max_ttl and allowed_users set lifetime and OS accounts, not a flag (manager side).
A job that runs only on GitHub Actions can use opkssh login github, with https://token.actions.githubusercontent.com github oidc in /etc/opk/providers and auth_id lines keyed on repo:<owner>/<repo>:ref:<ref>. Use OpenBAO when in-cluster runners need the same access.
Hosts: host certificates from OpenBAO's SSH CA¶
See SSH hosts.
Verify¶
opkssh login
ssh [email protected]
Read a machine certificate with ssh-keygen -L -f id_ci-cert.pub.
Who owns what¶
| Piece | Owner |
|---|---|
| the issuer, the opkssh client row, every internal group | sluis |
| the SSH CA, its roles, host auth methods, the host renewal agent | the secret store's owners |
opkssh and sshd wiring, /etc/opk/*, HostCertificate, known_hosts |
each host's owner |