Skip to content

0.12.0

Latest

Choose a tag to compare

@github-actions github-actions released this 30 Oct 12:31
· 6 commits to master since this release
7cf0c5b

Highlights

This is an interim release with security fixes from a review of the code base. Upgrading the server is recommended. The v1 API is unchanged, and older sshi clients keep working with this server.

Security fixes

  • signapi: certificates without principals are never issued. sshd accepts such a certificate for any account when the CA is trusted through cert-authority in authorized_keys without principals=. A user could get one by requesting principals that match nothing, and users without principals got one by default. These requests now get a 403.
  • signapi: the CA key upload endpoints (/v1/ca, /v1/ca/response) require a completed login. With an OIDC backend configured, a pending (unauthenticated) token was enough to upload a CA key when none was loaded.
  • signapi: each JWT is decoded into its own claims object, so data from one request cannot leak into another.
  • authldap: empty passwords are refused before binding (an unauthenticated bind could succeed and allow logging in as any user). User and directory values are escaped in userSearchFilter and groupSearchFilter.
  • authfile: empty passwords are refused, plain-text passwords are compared in constant time, and response times no longer reveal which user names exist.
  • authoidc: PKCE, a nonce, and single-use state bound to each login. Principals containing commas or control characters are dropped, because sshd splits certificate principals on commas when matching principals="...". Fixed a data race that could crash the server, and a pending-request leak that could lock out all logins.
  • keysigner: the server does not connect to agent sockets that another local user could have planted.
  • server: HTTP timeouts, TLS 1.2 minimum, OIDC authorization codes redacted from request logs, and the TCP peer logged as peer_address.
  • sshi: the CA private key and credentials are no longer written to debug output. Only http(s) URLs are opened for federated login, and the ad-hoc agent socket lives in a private directory.
  • dependencies: unmaintained modules were replaced (go-jose v4, go-oidc v3, go-ldap v3, resty v2). govulncheck reports no reachable vulnerabilities.

Upgrade notes

Review these before deploying the server:

  • Certificates without principals are refused (403). Users whose backend yields no principals, or who request principals that match nothing, no longer get a certificate.
  • authfile criticalOptions now applies. The documented camelCase key criticalOptions was silently ignored before (only criticaloptions worked). Restrictions such as force-command or source-address written with the documented spelling will now be put in the certificates. Review auth_users.yaml.
  • authfile users without a password can no longer log in. The server logs a warning for such entries at startup.
  • OIDC requireVerifiedEmail defaults to true. When the email claim is mapped to the subject or to principals, the identity provider must return email_verified: true. Set requireVerifiedEmail: false only if your provider does not send the claim and addresses cannot be chosen by users.
  • OIDC principals with commas or control characters are dropped. Other backends only log a warning.
  • TLS 1.2 or newer is required when the server terminates TLS itself.
  • Agent socket ownership is checked. A socket owned by another non-root user in a world-writable directory (such as /tmp) is refused. Sockets in private directories and sockets owned by root work as before.
  • Packages: new installs create /etc/ssh-inscribe/server_config.yaml as 0640 root:sshi; existing files are left untouched, so check their permissions (the file can contain tokenSigningKey, pkcs11Pin and the OIDC clientSecret). /var/lib/ssh-inscribe is now 0770, and the systemd unit has additional hardening (UMask=0077, waits for network-online.target).
  • LDAP startTLS=false in serverURL still enables StartTLS as before, but now logs a warning.
  • Building from source requires Go 1.26.4 or newer. The vendor directory was removed, and the Go packages moved under internal/ and cmd/.

Changelog

  • 8a61102: style: remove unnecessary type conversions (@scop)
  • 8b5f2f5: style: use strings.ReplaceAll instead of .Replace(... -1) (@scop)
  • b8a7ccf: perf: preallocate various slices (@scop)
  • 457f2ee: fix(signapi): invalid ca private key error message typo (@scop)
  • 6ea9e24: feat(cliclient): inhibit max-principals-per-cert filename completion (@scop)
  • a0ae40e: chore!: quality: restructure project to better align with the best practices (@aakso)
  • 0c9c355: chore: update dependencies (@aakso)
  • b0d25e1: chore: go mod tidy (@scop)
  • a642ba4: chore(ci): generate GitHub artifact attestations (@scop)
  • 6a59234: chore(goreleaser): hush deprecation warnings, 2.8+ now required (@scop)
  • ade7767: chore(release): include Sigstore bundle in assets (@scop)
  • 0f6af4d: chore: upgrade golangci-lint to v2, address trivial findings (@scop)
  • d106b9a: test: simplify authfile temp file bookkeeping (@scop)
  • 0d43079: fix: check errors on connection and file closes and removes (@scop)
  • 360ce60: refactor(config): migrate to github.com/go-viper/mapstructure/v2 (@scop)
  • e918857: Merge remote-tracking branch 'origin/chore/golangci2' into security/review-hardening (@aakso)
  • 81ff90a: Merge remote-tracking branch 'origin/refactor/go-viper-mapstructure' into security/review-hardening (@aakso)
  • e4b38f1: chore(deps)!: drop vendoring, require Go 1.26.4, update dependencies (@aakso)
  • 05ab2ac: fix(signapi): decode each JWT into its own claims object (@aakso)
  • dc6f69c: fix(signapi)!: never issue certificates without principals (@aakso)
  • b13fa8c: fix(authldap): refuse empty passwords, escape search filter values (@aakso)
  • 0b1e149: fix(authfile): refuse empty passwords, avoid timing side channels (@aakso)
  • 772adfd: fix(authfile)!: honour the documented criticalOptions key (@aakso)
  • efbb424: fix(signapi): require a completed login for CA key endpoints (@aakso)
  • c88b7b5: fix(authoidc): harden the authorization code flow (@aakso)
  • a5f8b34: fix(server): add HTTP timeouts, TLS 1.2 floor and log hygiene (@aakso)
  • 57bf4a4: fix(keysigner): do not connect to agent sockets planted by other users (@aakso)
  • 5fcddef: fix(sshi): client side hardening (@aakso)
  • c52b54d: fix(authoidc): drop principals sshd could split into several (@aakso)
  • a5cff2e: fix(packaging): private server config, conservative unit hardening (@aakso)
  • 7320aeb: ci: pin actions, least privilege, govulncheck, dependabot (@aakso)
  • 94af847: refactor: replace github.com/pkg/errors with the standard library (@aakso)
  • c9986b6: chore(deps): replace unmaintained helper modules (@aakso)
  • b26741f: refactor: modernize code base, strict linting with no exclusions (@aakso)
  • 8489340: refactor(server): replace deprecated NameToCertificate, add server tests (@aakso)
  • 7b25c8d: test(ui): end-to-end client tests against an in-process server (@aakso)
  • 33d2c4d: test: cover remaining fixes and rewritten code (@aakso)
  • a4bcf57: test(signapi): only unexpired HS256 tokens signed with the server key are accepted (@aakso)
  • c3ff229: docs: add AGENTS.md and TODO.md, update README (@aakso)
  • 7091dc5: build: install golangci-lint into .bin so it can lint for GOOS=windows (@aakso)
  • 1b4b518: fix(sshi): make Client.Close idempotent (@aakso)
  • c12aa57: fix(config): keep the list merge semantics of mapstructure v1 (@aakso)
  • e22cfca: fix(packaging): drop ProtectClock from the unit (@aakso)
  • f4f42f1: fix(filteringagent): guard the added keys map (@aakso)
  • 713f4a5: fix(sshi): do not hang on exit while agent connections are open (@aakso)
  • ccdeeae: fix(authoidc): complete a pending login only once (@aakso)
  • 8dfef54: fix(authldap): warn when startTLS=false in serverURL enables StartTLS (@aakso)
  • 35f0bb3: fix(keysigner): close the previous agent connection on reconnect (@aakso)
  • 33f4893: test: isolate tests from each other (@aakso)
  • df41247: test(authldap): integration tests against OpenLDAP 2.4.57 and 389 DS (@aakso)
  • 250ed95: fix(sshi): redact credentials and tokens from --debug output (@aakso)
  • 0100e33: fix(signapi): 400 for undecryptable CA challenges, bound principal batch size (@aakso)
  • c8db1d2: docs(todo): record LDAP identity check, socket check limits, AD coverage (@aakso)
  • c89ba9b: fix(config): overlay single values written in place of lists too (@aakso)
  • 6923657: fix(sshi): do not log errors for agent connections closed on Windows (@aakso)
  • 9c5020d: test(signapi): concurrent sign requests keep each user's identity (@aakso)
  • d9ed6b3: test(signapi): forged tokens must not leak into other requests (@aakso)
  • 781e767: docs(todo): drop a deployment-specific note from the signing follow-up (@aakso)
  • 1cf8589: build: let golangci-lint cover vet and formatting checks (@aakso)
  • 97df3f7: ci: allow the CI workflow to be run manually (@aakso)

Downloads can be verified against GitHub artifact attestations.
apt and dnf/yum package repositories are available at Packagecloud.