diff --git a/docs/architecture.md b/docs/architecture.md index 5ce344e..6907a86 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -9,11 +9,13 @@ New to certificates? Read the [Concepts Guide](concepts.md) first. ### Design Principles 1. **Private Key Isolation** — Agents generate ECDSA P-256 keys locally and submit CSRs only. Private keys never touch the control plane. Server-side keygen available via `CERTCTL_KEYGEN_MODE=server` for demo only. -2. **GUI as Primary Interface** — The web dashboard is the operational control plane, not a secondary viewer. Every backend feature ships with its corresponding GUI surface. -3. **Decoupled Operations** — Agents operate autonomously; the control plane coordinates but doesn't block agent function -4. **Audit-First** — Complete traceability of all issuance, deployment, and rotation events -5. **Connector Architecture** — Pluggable issuers, targets, and notifiers for extensibility -6. **Self-Hosted** — No cloud lock-in; run with Docker Compose, Kubernetes, or bare metal +2. **Pull-Only Deployment** — The server never initiates outbound connections to agents or targets. Agents poll for work. For network appliances and agentless targets, a proxy agent in the same network zone executes deployments via the target's API. This keeps the control plane firewalled off and limits credential scope to the proxy agent's zone. +3. **Sub-CA Capable** — The Local CA can operate as a subordinate CA under an enterprise root (e.g., ADCS). Load a pre-signed CA cert+key from disk and all issued certs chain to the enterprise trust hierarchy. Self-signed mode remains the default for development/demos. +4. **GUI as Primary Interface** — The web dashboard is the operational control plane, not a secondary viewer. Every backend feature ships with its corresponding GUI surface. +5. **Decoupled Operations** — Agents operate autonomously; the control plane coordinates but doesn't block agent function +6. **Audit-First** — Complete traceability of all issuance, deployment, and rotation events +7. **Connector Architecture** — Pluggable issuers, targets, and notifiers for extensibility +8. **Self-Hosted** — No cloud lock-in; run with Docker Compose, Kubernetes, or bare metal ## System Components @@ -50,8 +52,8 @@ flowchart TB T1["NGINX\n(file write + reload)"] T4["Apache httpd\n(file write + reload)"] T5["HAProxy\n(combined PEM + reload)"] - T2["F5 BIG-IP\n(iControl REST, planned)"] - T3["IIS\n(WinRM, planned)"] + T2["F5 BIG-IP\n(proxy agent + iControl REST, planned)"] + T3["IIS\n(agent-local PowerShell, planned)"] end DASH --> API @@ -312,10 +314,10 @@ The agent deploys certificates using target connectors. Each connector knows how - **NGINX**: Writes cert/chain/key files to disk, validates config with `nginx -t`, reloads with `nginx -s reload` or `systemctl reload nginx` - **Apache httpd**: Writes separate cert/chain/key files, validates with `apachectl configtest`, graceful reload - **HAProxy**: Builds a combined PEM file (cert + chain + key), optionally validates config, reloads via systemctl or signal -- **F5 BIG-IP** (planned): Calls the F5 iControl REST API to upload certificate and update SSL profile bindings -- **IIS** (planned): Uses WinRM to import PFX into the Windows certificate store and bind it to an IIS site +- **F5 BIG-IP** (planned): A proxy agent in the same network zone calls the iControl REST API to upload certificate and update SSL profile bindings. The server assigns the work; the proxy agent executes it. +- **IIS** (planned, dual-mode): (1) Agent-local (recommended) — a Windows agent on the IIS box runs PowerShell `Import-PfxCertificate` + `Set-WebBinding` directly. (2) Proxy agent WinRM — for agentless IIS targets, a nearby Windows agent reaches the IIS box via WinRM. -The agent handles both the certificate (public) and the private key (read from local key store at `CERTCTL_KEY_DIR`). The control plane never sees the private key. +The agent handles both the certificate (public) and the private key (read from local key store at `CERTCTL_KEY_DIR`). The control plane never sees the private key and never initiates outbound connections to agents or targets (pull-only model). ### 4. Automatic Renewal @@ -437,7 +439,7 @@ type Connector interface { The `DeploymentRequest` struct carries the full material needed by the target system: the signed certificate, the CA chain, the agent-generated private key, target-specific configuration, and arbitrary metadata. The key field is populated by the agent from its local key store (`CERTCTL_KEY_DIR`) — it never originates from the control plane. -Built-in targets: **NGINX** (writes cert/chain/key files, validates with `nginx -t`, reloads), **Apache httpd** (writes cert/chain/key files, validates with `apachectl configtest`, graceful reload), **HAProxy** (combined PEM file with cert+chain+key, validates config, reloads via systemctl/signal), **F5 BIG-IP** (interface only — iControl REST flow mapped, implementation planned V2), **IIS** (interface only — WinRM/PowerShell flow mapped, implementation planned V2). +Built-in targets: **NGINX** (writes cert/chain/key files, validates with `nginx -t`, reloads), **Apache httpd** (writes cert/chain/key files, validates with `apachectl configtest`, graceful reload), **HAProxy** (combined PEM file with cert+chain+key, validates config, reloads via systemctl/signal), **F5 BIG-IP** (interface only — proxy agent + iControl REST, planned V2), **IIS** (interface only — dual-mode: agent-local PowerShell primary + proxy agent WinRM for agentless targets, planned V2). **Planned (V3):** Kubernetes cert-manager external issuer, Kubernetes Secrets, AWS ALB/CloudFront, AWS IAM Roles Anywhere, Azure Key Vault, Azure Managed Identity, Palo Alto, FortiGate, Citrix ADC. @@ -501,7 +503,7 @@ The control plane only handles public material: certificates, chains, and CSRs. - **API clients → Server**: API key in `Authorization: Bearer` header, or `none` for demo mode - **Agent → Server**: API key registered at agent creation, included in all requests - **Server → Issuers**: ACME account key, or connector-specific credentials -- **Agent → Targets**: SSH keys, API tokens, WinRM credentials (stored locally on agent) +- **Agent → Targets**: API tokens, WinRM credentials (stored locally on agent or proxy agent — never on server). Credential scope is limited to the agent's network zone. ### Audit Trail diff --git a/docs/concepts.md b/docs/concepts.md index c648918..e155f1a 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -30,6 +30,8 @@ A CA is the trusted third party that signs your certificates. When a CA signs a Common CAs include Let's Encrypt (free, automated), DigiCert, Sectigo, and your organization's internal/private CA. Each issues certificates through different protocols and APIs. +certctl includes a built-in **Local CA** that can operate in two modes: self-signed (default, for development and demos) or as a **subordinate CA** under an enterprise root like Active Directory Certificate Services (ADCS). In sub-CA mode, you load a CA certificate and key signed by your enterprise root, and all certificates certctl issues automatically chain to the enterprise trust hierarchy — no manual trust configuration needed on clients that already trust your enterprise root. + ### ACME Protocol ACME (Automatic Certificate Management Environment) is the protocol Let's Encrypt created for automated certificate issuance. Instead of filling out forms and waiting for emails, ACME lets software request, validate, and receive certificates programmatically. The server proves domain ownership by responding to challenges — placing a specific file on the web server (HTTP-01) or creating a DNS record (DNS-01). @@ -62,7 +64,7 @@ The control plane never touches private keys. It coordinates the certificate lif ### Agents -Agents are lightweight processes that run on or near your infrastructure. They do the actual work: generating private keys, creating Certificate Signing Requests (CSRs), receiving signed certificates, and deploying them to servers. An agent might run on the same machine as your NGINX server, or on a management host that has SSH access to your web servers. +Agents are lightweight processes that run on or near your infrastructure. They do the actual work: generating private keys, creating Certificate Signing Requests (CSRs), receiving signed certificates, and deploying them to target systems. An agent typically runs on the same machine as the target (e.g., your NGINX or IIS server), deploying certificates locally. For network appliances where you can't install an agent, a proxy agent in the same network zone handles deployment via the appliance's API. The flow looks like this: @@ -76,11 +78,13 @@ The flow looks like this: At no point does the private key leave the agent. This is a fundamental security property. -Agents also report **metadata** about themselves — their operating system, CPU architecture, IP address, hostname, and version — with every heartbeat. This gives ops teams fleet-wide visibility (e.g., "how many agents are running on ARM?", "which agents are still on v1.0.0?") and is the foundation for future features like dynamic device grouping, where policies can be scoped to specific agent criteria like OS type or network subnet. +Agents also report **metadata** about themselves — their operating system, CPU architecture, IP address, hostname, and version — with every heartbeat. This gives ops teams fleet-wide visibility (e.g., "how many agents are running on ARM?", "which agents are still on v1.0.0?") and powers **agent groups** — dynamic device grouping where policies can be scoped to specific agent criteria like OS type, architecture, or network subnet. ### Deployment Targets -Targets are the systems where certificates actually get installed — NGINX web servers, Apache httpd servers, HAProxy load balancers, F5 BIG-IP appliances, Microsoft IIS servers. Each target type has a **connector** that knows how to deploy certificates to that specific system (e.g., writing files and reloading NGINX or Apache config, building a combined PEM for HAProxy, calling the F5 REST API, running PowerShell commands on IIS via WinRM). +Targets are the systems where certificates actually get installed — NGINX web servers, Apache httpd servers, HAProxy load balancers, F5 BIG-IP appliances, Microsoft IIS servers. Each target type has a **connector** that knows how to deploy certificates to that specific system (e.g., writing files and reloading NGINX or Apache config, building a combined PEM for HAProxy). + +For targets where an agent runs directly on the machine (NGINX, Apache, HAProxy, IIS), the agent deploys certificates locally — no remote access needed. For network appliances where you can't install an agent (F5 BIG-IP, Palo Alto, etc.), a **proxy agent** in the same network zone picks up the deployment job and calls the appliance's API. The server never initiates outbound connections to any target. ## The Certificate Lifecycle diff --git a/docs/connectors.md b/docs/connectors.md index c90a542..26a8d2e 100644 --- a/docs/connectors.md +++ b/docs/connectors.md @@ -6,11 +6,11 @@ Connectors extend certctl to integrate with external systems for certificate iss Three types of connectors: -1. **Issuer Connector** — Obtains certificates from CAs (Local CA, ACME implemented; step-ca, ADCS, OpenSSL planned V2; DigiCert, Entrust, GlobalSign, EJBCA, Vault PKI, Google CAS planned V3) -2. **Target Connector** — Deploys certificates to infrastructure (NGINX, Apache httpd, HAProxy implemented; F5, IIS interface only; AWS ALB, Azure Key Vault, Palo Alto, FortiGate, Citrix ADC, Kubernetes Secrets planned V3) +1. **Issuer Connector** — Obtains certificates from CAs (Local CA with sub-CA support, ACME implemented; step-ca, OpenSSL planned V2; DigiCert, Entrust, GlobalSign, EJBCA, Vault PKI, Google CAS planned V3) +2. **Target Connector** — Deploys certificates to infrastructure (NGINX, Apache httpd, HAProxy implemented; F5 via proxy agent, IIS dual-mode interface only; AWS ALB, Azure Key Vault, Palo Alto, FortiGate, Citrix ADC, Kubernetes Secrets planned V3) 3. **Notifier Connector** — Sends alerts about certificate events (Email, Webhooks; Slack, Teams, PagerDuty, OpsGenie planned V2) -All connectors accept JSON configuration at initialization, support config validation, and are registered in the service layer. Issuer connectors run on the control plane; target connectors run on agents. +All connectors accept JSON configuration at initialization, support config validation, and are registered in the service layer. Issuer connectors run on the control plane; target connectors run on agents. For network appliances where agents can't be installed, a **proxy agent** in the same network zone handles deployment — the server never initiates outbound connections. ## Issuer Connector @@ -81,15 +81,19 @@ type OrderStatus struct { ### Built-in: Local CA -The Local CA issuer generates self-signed certificates using Go's `crypto/x509` library. It creates a CA on first use (in memory), issues certificates with proper serial numbers, validity periods, SANs, and key usage extensions. +The Local CA issuer signs certificates using Go's `crypto/x509` library. It supports two modes: -This issuer is designed for development and demos only — certificates are self-signed and not trusted by browsers. +**Self-signed mode (default):** Creates a CA on first use (in memory), issues certificates with proper serial numbers, validity periods, SANs, and key usage extensions. Designed for development and demos — certificates are self-signed and not trusted by browsers. + +**Sub-CA mode (planned M12):** Loads a CA certificate and private key from disk (`CERTCTL_CA_CERT_PATH` + `CERTCTL_CA_KEY_PATH`). The CA cert is signed by an upstream CA (e.g., ADCS), so all issued certificates chain to the enterprise root trust hierarchy. Clients that already trust the enterprise root automatically trust certctl-issued certs. If the paths are not set, falls back to self-signed mode. Configuration: ```json { "ca_common_name": "CertCtl Local CA", - "validity_days": 90 + "validity_days": 90, + "ca_cert_path": "/etc/certctl/ca/ca.pem", + "ca_key_path": "/etc/certctl/ca/ca-key.pem" } ``` @@ -126,10 +130,11 @@ The following issuer connectors are planned for V2: - **step-ca** — Smallstep's private CA and ACME server. Would allow certctl to issue certificates from a self-hosted step-ca instance via its ACME or provisioner APIs. - **OpenSSL / Custom CA** — Support for external CAs that use OpenSSL-based signing workflows, including custom script hooks for organizations with existing CA tooling. -- **ADCS (Active Directory Certificate Services)** — Microsoft's enterprise CA. Would allow certctl to request certificates from an existing ADCS infrastructure, useful for organizations that need lifecycle management around their Windows PKI. - **Vault PKI** — HashiCorp Vault's PKI secrets engine for organizations using Vault as their internal CA. - **DigiCert** — Commercial CA integration via DigiCert's REST API. +Note: ADCS (Active Directory Certificate Services) integration is handled via the **sub-CA mode** of the Local CA issuer, not as a separate connector. certctl operates as a subordinate CA with its signing certificate issued by ADCS, so all certctl-issued certs chain to the enterprise ADCS root. See the Local CA section above. + ### Building a Custom Issuer Here's the structure for a HashiCorp Vault PKI issuer: @@ -318,7 +323,9 @@ Location: `internal/connector/target/haproxy/haproxy.go` ### Planned: F5 BIG-IP (V2, Interface Only) -The F5 BIG-IP target connector interface is built with the iControl REST flow mapped out, but the actual API calls are not yet implemented. The planned flow is: authenticate via `POST /mgmt/shared/authn/login`, upload cert PEM via `POST /mgmt/tm/ltm/certificate`, update the SSL profile via `PATCH /mgmt/tm/ltm/profile/client-ssl/{profile}`, and validate deployment by checking profile status. Implementation is planned for V2. +The F5 BIG-IP target connector interface is built with the iControl REST flow mapped out, but the actual API calls are not yet implemented. F5 appliances can't run agents directly, so this connector uses the **proxy agent pattern**: a designated agent in the same network zone picks up F5 deployment jobs and calls the iControl REST API. The server assigns the work; the proxy agent executes it. + +The planned flow is: authenticate via `POST /mgmt/shared/authn/login`, upload cert PEM via `POST /mgmt/tm/ltm/certificate`, update the SSL profile via `PATCH /mgmt/tm/ltm/profile/client-ssl/{profile}`, and validate deployment by checking profile status. Implementation is planned for V2. Configuration (defined, not yet functional): ```json @@ -331,24 +338,33 @@ Configuration (defined, not yet functional): } ``` +Note: F5 credentials are stored on the proxy agent, not on the control plane server. This limits the credential blast radius to the proxy agent's network zone. + Location: `internal/connector/target/f5/f5.go` -### Planned: IIS (V2, Interface Only) +### Planned: IIS (V2, Interface Only, Dual-Mode) -The IIS target connector interface is built with the WinRM/PowerShell flow mapped out, but the actual remote execution is not yet implemented. The planned flow is: transfer a PFX bundle to the Windows server via WinRM, run `Import-PfxCertificate` to install it into the certificate store, and run `Set-WebBinding` to bind the certificate to the IIS site. Implementation is planned for V2. +The IIS target connector supports two deployment modes: + +**Agent-local (recommended):** A Windows agent runs directly on the IIS server and deploys certificates using PowerShell — `Import-PfxCertificate` to install into the certificate store and `Set-WebBinding` to bind to the IIS site. This is the preferred approach: no remote access needed, no credential management, same pull-based model as NGINX/Apache/HAProxy. + +**Proxy agent WinRM (for agentless targets):** For Windows servers where you don't want to install an agent, a nearby Windows agent acts as a proxy and reaches the IIS box via WinRM. The proxy agent picks up the deployment job, transfers the PFX bundle over WinRM, and runs the PowerShell commands remotely. WinRM credentials are stored on the proxy agent, not on the control plane. Configuration (defined, not yet functional): ```json { - "host": "iis-server.internal.example.com", - "username": "Administrator", - "password": "...", + "mode": "local", "site_name": "Default Web Site", "cert_store": "WebHosting", - "use_https": true + "winrm_host": "", + "winrm_username": "", + "winrm_password": "", + "winrm_use_https": true } ``` +When `mode` is `"local"`, the `winrm_*` fields are ignored. When `mode` is `"proxy"`, the agent connects to the remote IIS server via WinRM using the provided credentials. + Location: `internal/connector/target/iis/iis.go` ## Notifier Connector diff --git a/docs/demo-advanced.md b/docs/demo-advanced.md index 117da19..5e98334 100644 --- a/docs/demo-advanced.md +++ b/docs/demo-advanced.md @@ -116,7 +116,7 @@ You should see: The result is a structurally valid X.509 certificate — browsers won't trust it (no root CA in their trust store), but it exercises the exact same code paths that a production ACME or Vault issuer would. -**Why pluggable issuers:** Different organizations use different CAs. Some use Let's Encrypt (ACME protocol), some use step-ca or internal PKI (Vault, ADCS), some use commercial CAs (DigiCert, Entrust, GlobalSign), and some have custom OpenSSL-based workflows. The connector interface means certctl doesn't care — it calls `IssueCertificate()` and gets back a signed cert regardless of the backend. V1 ships with Local CA and ACME (HTTP-01); step-ca, ADCS, OpenSSL/custom CA are planned for V2; DigiCert, Vault PKI, Entrust, GlobalSign, Google CAS, and EJBCA are planned for V3. +**Why pluggable issuers:** Different organizations use different CAs. Some use Let's Encrypt (ACME protocol), some use step-ca or internal PKI (Vault), some use commercial CAs (DigiCert, Entrust, GlobalSign), and some have custom OpenSSL-based workflows. For enterprises with ADCS, certctl can operate as a sub-CA — all issued certs chain to the enterprise root. The connector interface means certctl doesn't care — it calls `IssueCertificate()` and gets back a signed cert regardless of the backend. V1 ships with Local CA (self-signed or sub-CA) and ACME (HTTP-01); step-ca, OpenSSL/custom CA are planned for V2; DigiCert, Vault PKI, Entrust, GlobalSign, Google CAS, and EJBCA are planned for V3. ```mermaid flowchart TD @@ -127,11 +127,10 @@ flowchart TD D["GetOrderStatus(orderID)"] end - A --> E["Local CA\n(crypto/x509)"] + A --> E["Local CA\n(self-signed or sub-CA)"] A --> F["ACME\n(Let's Encrypt)"] A --> G["step-ca\n(planned V2)"] A --> H["OpenSSL / Custom CA\n(planned V2)"] - A --> I["ADCS\n(planned V2)"] A --> J["DigiCert API\n(planned V2.3)"] A --> K["Vault PKI\n(planned V3)"] A --> L["Entrust / GlobalSign\n(planned V3)"] @@ -308,13 +307,13 @@ sequenceDiagram TC->>T: Run: nginx -t (validate config) TC->>T: Run: systemctl reload nginx TC-->>A: {success: true, deployed_at: "..."} - else F5 Target - TC->>T: POST /mgmt/tm/sys/crypto/cert (upload cert) - TC->>T: PUT /mgmt/tm/ltm/virtual (bind to virtual server) + else F5 Target (via proxy agent) + TC->>T: iControl REST: POST /mgmt/tm/sys/crypto/cert + TC->>T: iControl REST: PUT /mgmt/tm/ltm/virtual TC-->>A: {success: true, deployed_at: "..."} - else IIS Target - TC->>T: WinRM: Import-PfxCertificate - TC->>T: WinRM: Set-WebBinding -SslFlags + else IIS Target (agent-local) + TC->>T: PowerShell: Import-PfxCertificate + TC->>T: PowerShell: Set-WebBinding -SslFlags TC-->>A: {success: true, deployed_at: "..."} end