Repository navigation
feat: Apus certificate authority - #460
Draft
raphael-goetz wants to merge 6 commits into
Draft
raphael-goetz wants to merge 6 commits into
raphael-goetz wants to merge 6 commits into
Conversation
…wrapper Unrelated pre-existing breakage found while starting the Apus CA work: origin/main did not compile (nor did its tests) because the tucana 0.0.82 -> 0.0.83 bump (20ec661) wrapped node success values in a new NodeValueExecutionResult { value: oneof { RawValue, SubFlowFunctionResult } } message, but this file still built/matched the old NodeParameterNodeExecutionResult shape directly. Fixed to compile so the branch has a working baseline to build the certificate authority on top of.
rcgen issues the certificates the certificate authority hands out (action and server leaf certs plus the ephemeral/loaded CA); time backs its expiry arithmetic. x509-parser and rustls are dev-only, to parse issued certificates back and verify they chain to the CA in tests.
Aquila's side of Apus mutual TLS: CertificateAuthority issues short-lived action client certificates (URI SAN code0://action/<id>[?projects=][&replicas=], the identity Taurus checks HELLO.action against) and Taurus server certificates, backed by rcgen. identity_uri/is_valid_action_identifier in src/ca/identity.rs are a deliberate, documented copy of apus-protocol::tls's identity_uri/parse_identity (not a runtime dependency on the sibling apus repo - see the rationale in identity.rs's module doc). load() generates an ephemeral, loudly-logged self-signed CA when no CA path is configured in development, and hard-errors in staging/production instead of ever running an un-provisioned CA outside development. Also: gofmt cleanup of the result_trim.rs fix from the previous commit (dropped a now-unused import, matched this repo's formatting).
Adds config.apus: enabled, ca_cert_path/ca_key_path (both or neither), action/server certificate lifetimes (default 24h / 90d), and output_dir for the interim file bridge. Rendered in the startup config display - only paths, no key material is ever held in this config value so there's nothing to filter.
Adds a local, hand-written aquila.apus.CertificateService (no .proto, no tonic-build step - the message types and the tonic server dispatcher are written by hand to the same shape codegen would produce, mirroring tucana's generated module_service_server for the boilerplate). Its one RPC, IssueCertificate, authenticates the caller's existing JWT the same way every other Aquila RPC does, then: - Action subject: requires has_action(token, identifier), issues a client certificate via CertificateAuthority::issue_action_certificate (replicas comes from the service configuration, not the request - projects is taken from the request as-is, flagged as a follow-up since there's no existing authoritative source to validate it against). - Server subject: requires has_runtime(token, identifier) (Taurus authenticates as "taurus"), issues a server certificate. Authorization is two-phase so failures map to the right status: no has_service match at all -> Unauthenticated (wrong secret, or the identifier isn't provisioned); a real identity requesting the wrong subject kind -> PermissionDenied. Adds tonic-prost as a direct dependency (for ProstCodec, used by the hand-written server dispatcher).
Loads CertificateAuthority once at startup in both startup::dynamic_mode and startup::static_mode, gated on config.apus.enabled (panics with a clear message on load failure, matching this module's existing fail-fast style for other startup dependencies). Threads it through as Option<Arc<CertificateAuthority>> into AquilaDynamicServer/AquilaStaticServer, which register the CertificateService via tonic's add_optional_service (not behind the Sagittarius readiness interceptor - issuing a certificate has no Sagittarius dependency) in every Server::builder() branch, health and non-health, both modes. Also introduces StaticServerDependencies, mirroring dynamic_server's existing DynamicServerDependencies pattern, since AquilaStaticServer::new had grown past clippy::too_many_arguments once apus_ca was added.
GitLab Pipeline ActionGeneral informationLink to pipeline: https://gitlab.com/code0-tech/development/aquila/-/pipelines/3014405849 Status: Passed Job summariesdocs:previewDocumentation preview available at https://code0-tech.gitlab.io/-/development/telescopium/-/jobs/17643443570/artifacts/out/index.html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
src/ca) usingrcgen, replacing thebench/tls.shhack in theapusPOC: issues short-lived action client certs (URI SANcode0://action/<id>) and Taurus server certsCertificateServiceexposing issuance, gated on JWT auth (has_action/has_runtime), wired into both dynamic and static server startupapus-protocolwas evaluated but not added as a dependency — it has nothing CA-side reusable (issuance cratercgenis only a dev-dependency there);identity_uriwas reimplemented locally to match byte-for-byteorigin/maindidn't actually build (result_trim.rswasn't updated for tucana 0.0.83'sNodeValueExecutionResultwrapper) — fixed in its own commit, happy to split into a separate PR if preferredKnown gaps (flagged, not fixed here)
projectson an action cert request isn't validated against what the action is actually authorized forapus-client/taurus calls it yet)Test plan
cargo buildcargo test— 89/89 (61 pre-existing + 28 new)cargo clippy --all-targets— no new warningscargo fmt --check— clean except one pre-existing unrelated file🤖 Generated with Claude Code