Skip to content

The self-signed gateway ​

Signs certificates with a local CA it generates itself. No account, no credentials, no network.

Implemented by certpilot-gateway-selfsigned, against the provider.v1 contract in certpilot-gateway-sdk.


What it is for ​

Two things, and one of them is not "development".

Evaluating CertPilot. It needs nothing external, so a first run can issue a real certificate in the time a container takes to start. This is the CA behind Evaluate CertPilot.

Names no public CA will ever sign. .internal, .local, a bare hostname, an RFC 1918 address. Those are never going to a public CA, and the estate holding them still expires, still needs rotating, and is exactly the estate that gets forgotten about. A certificate from here is a real certificate with a real expiry, renewed and deployed like any other.

It is not a PKI. No offline root, no HSM, no revocation, no CRL, no path length planning. If you need those you need Vault or a public CA, and a gateway for it.


What it supports ​

Key typesRSA, ECDSA, Ed25519
CSRsHonoured. Supply one and it signs your key, which never leaves the host
RevocationNo — reports supports_revocation=false
GetCAInfoNo — reports supports_ca_info=false
DescribeProfileNo — answers Unimplemented, a supported answer under the v0.3.0 contract

Those are reported honestly rather than stubbed, so the core knows not to offer a revoke button that would do nothing. A certificate issued here can still be deleted from the inventory; see Delete — not a substitute for revoking.


Connecting an account ​

make dev registers this gateway and a CA account for it. The container evaluation registers the gateway only, so there the account is a step you take — which is where the two differ and why Evaluate CertPilot spells it out.

These examples carry -b "$JAR", a cookie jar from signing in. There is no anonymous mode, so a command without a credential is a 401. See Authentication for the one-liner that fills it, or substitute -H "Authorization: Bearer $TOKEN".

bash
curl -b "$JAR" -X POST localhost:8080/api/v1/ca-accounts \
  -H 'Content-Type: application/json' -d '{
    "name": "selfsigned-dev",
    "provider_type": "selfsigned",
    "gateway_addr": "localhost:9091",
    "server_name": "localhost",
    "config": {"validity_days": 90}
  }'

server_name is the name the gateway's own certificate is checked against, and it is not always the same as gateway_addr. On a compose network the address is the service name while the certificate still says localhost, because that is what --generate-dev-certs writes into the SAN. Getting it wrong fails hostname verification after a fifteen-second dial timeout, which reads like the gateway being down.

Key
validity_daysLifetime to request. The gateway's own default is 365

The security boundary ​

The CA key is generated by the gateway and kept by the gateway. Nothing signs it, nothing else trusts it, and there is no ceremony around it — which is exactly why this is not a PKI.

The core-to-gateway channel is mutually authenticated TLS 1.3 by default, the same as every other gateway. The quickstart does not turn that off to make the demo shorter: a product that disables its own transport security for a demo is making an argument it does not believe.

Supply a CSR and the private key never reaches CertPilot at all. The record says so, in key_custody.


Limitations ​

  • Nothing trusts it. Browsers, operating systems and your own services will reject these certificates unless you distribute the CA yourself.
  • No revocation. There is no CRL and no OCSP responder, so a certificate issued here cannot be withdrawn — only allowed to expire. The core does not offer revocation for these accounts, because the gateway says it cannot.
  • No issuer inventory. GetCAInfo is unimplemented, so the CA behind these certificates does not appear in the CA hierarchy view.
  • Renewal lifetime. A renewed certificate comes back with the gateway's own 365-day default rather than the validity_days the account asked for. That is #102, and it is a core-and-contract defect rather than one specific to this gateway.

Running and checking it ​

bash
make run-gateway-selfsigned    # :9091
-port         9091
-insecure     serve without TLS. Loopback only
-tls-cert / -tls-key / -tls-ca

Released as ghcr.io/certpilot/gateway-selfsigned on linux/amd64 and linux/arm64, tagged in step with the Go module, so go run github.com/certpilot/certpilot-gateway-selfsigned/cmd@v0.4.0 runs the code the image contains.

This is the one gateway that can be exercised end to end with no external dependency, so CI runs the full issuance path against it on every pull request — with and without a CSR — rather than only the contract checks:

bash
go run github.com/certpilot/certpilot-gateway-sdk/cmd/conformance@v0.4.0 \
    -addr localhost:9091 -insecure -domain test.example.com

That -domain is what turns the issuance checks from skipped into run. See compatibility for what each released gateway currently passes, and writing a gateway to implement the contract for a CA that has none.