Skip to content

Feat/ssh certs - #452

Draft
patelspratik wants to merge 9 commits into
mainfrom
feat/ssh-certs
Draft

Feat/ssh certs#452
patelspratik wants to merge 9 commits into
mainfrom
feat/ssh-certs

Conversation

@patelspratik

Copy link
Copy Markdown
Contributor

No description provided.

Foundation for certificate-based SSH auth. Generates fresh ed25519
keypairs per renewal, caches the (private key, certificate) pair on
disk for the certificate's validity window, and writes atomically
(0600 private key). Files live under ~/.brev/ssh-certs/<envID>{,-cert.pub}
so a single IdentityFile directive loads both key and cert (OpenSSH
-cert.pub convention).

Independent of the IssueEnvironmentSSHCertificate RPC so the rest of
the feature can build and test before the generated connect client is
published. Includes EnvironmentCertEligible() mirroring dev-plane's
label constants (sshprovider=certauth).
resolveWorkspaceSSH already fetches the user's SSHAccess (carrying
port_id + linux_user) and the environment labels from the Environment
connect API during refresh, but discarded port_id after resolving the
network port. Retain port_id and the sshprovider=certauth label on the
workspace so the SSH config generator can emit a per-environment
certificate-fetch entry. Fields stay zero-valued for environments that
fall back to static-key auth.
Headless, non-interactive mode invoked by the ssh config's Match exec
hook. Reuses the existing platform credential (no login prompt), checks
the on-disk cert cache, mints a fresh ephemeral ed25519 keypair +
short-lived certificate via a CertIssuer, and atomically writes them to
the --out-key path (+ -cert.pub). On any failure it writes nothing and
returns non-zero so ssh drops the Match IdentityFile and falls back to
the static brev.pem.

CertIssuer is an interface; today a stub returns ErrCertIssuanceUnavailable
(the real connect-RPC issuer drops in once the buf module publishes
IssueEnvironmentSSHCertificate). Flags are hidden since this is an
implementation detail of the ssh config, not a user-facing mode.
For each cert-eligible workspace (sshprovider=certauth label + port_id),
the SSH config generator now prepends a Match host <alias> exec block
before the existing Host block. The Match block runs 'brev shell
--cert-only' to mint a short-lived cert and carries the cert
IdentityFile; the Host block keeps the static brev.pem IdentityFile.

OpenSSH accumulates IdentityFile across Match and Host blocks when the
exec succeeds (cert tried first, static as fallback), and drops the
Match block's IdentityFile when the exec fails (mint error, CA down,
or the not-yet-wired issuer stub), so ssh falls back to the static key.
Verified empirically against the ssh binary.

Ineligible workspaces (no certauth label, or missing port_id) and the
WSL config (Windows paths/binary, deferred) get no Match block and
keep the static key only.
Cut ~260 lines while preserving behavior and coverage:

- Delete the Store type in pkg/sshcert (dead code: production uses the
  free functions HasValidCertAt/WriteFiles with explicit paths, since
  --cert-only receives --out-key). Removed its methods and tests.
- Delete fakeIssuer from certonly_test.go (unused; tests use certIssuerFunc).
- Replace makeCertMatchEntry's text/template with a direct Sprintf: the
  template was a constant string and the error branches guarded against
  impossible parse failures of that constant.
- Drop validateCertOnly's 'flags set without --cert-only' branch: the
  flags are hidden and only set by the generated config, so the inverse
  scenario can't occur in practice.
- Consolidate TestEnvironmentCertEligible and TestSafeFilename into
  table-driven form; merge redundant assertions.
- Trim verbose per-function comments that restated the package doc.
Replace the placeholder stubCertIssuer with rpcCertIssuer, which calls
dev-plane's EnvironmentService.IssueEnvironmentSSHCertificate via the
authenticated connect client (register.NewEnvironmentServiceClient).
The store satisfies externalnode.TokenProvider via GetAccessToken, so
the existing platform credential is reused with no new login.

CertIssuer remains an interface so runCertOnly stays unit-testable; add
tests for rpcCertIssuer verifying request field mapping and error
propagation. Remove the now-unused stub + ErrCertIssuanceUnavailable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant