Skip to main content

Deterministic TLS bundle swap CLI: ingest any cert format, deploy to k8s/ssh/local/proxmox with validation, evidence, and ArgoCD-safe swaps.

Project description

certswap

Deterministic TLS bundle swap CLI.

Take a TLS bundle in any common format (PFX, PEM bundle, separate files, PKCS#7, zip/tar archive), validate it, and deploy it to a target — a Kubernetes secret, an SSH-reachable VM, a local directory, or a Proxmox host — with a structured evidence trail per swap.

Built for the operational reality no other tool covers: a CA or customer hands you a bundle out-of-band and it has to land safely on GitOps-managed or mixed infrastructure. ACME clients automate issuance-driven renewal; cert-manager owns the in-cluster path; certswap handles everything that arrives by hand — with an ArgoCD-safe swap that existing tooling only documents as a manual procedure.

Not related to the archived pivotal-cf/certswap (a Go tool for swapping system trust stores).

Install

Requires Python 3.12+ and uv.

uv tool install certswap        # from PyPI (first release pending)
uv tool install git+https://github.com/MWest2020/certswap   # latest main

For local development:

git clone git@github.com:MWest2020/certswap.git
cd certswap
uv sync --dev
uv run ruff check . && uv run mypy src && uv run pytest

Commands

certswap inspect <bundle>                                # show bundle contents (key optional)
certswap plan local   <bundle> --dest <dir>
certswap apply local  <bundle> --dest <dir>
certswap verify local --dest <dir>
certswap plan ssh     <bundle> --host <h> --cert-dest /etc/nginx/tls/x.pem --key-dest /etc/nginx/tls/x.key
certswap apply ssh    <bundle> --host <h> --cert-dest ... --key-dest ... --reload "nginx -s reload"
certswap plan k8s     <bundle> --namespace ns --secret tls --context homelab --ingress app
certswap apply k8s    <bundle> --namespace ns --secret tls --argocd-app my-app
certswap apply proxmox <bundle> --host pve-node
certswap upcoming --within-days 60

Drivers: local, ssh, k8s (with optional ArgoCD coordination), proxmox. All commands accept --json for machine-readable output; apply writes an evidence dir under ~/.certswap/evidence/; deployments are tracked in ~/.certswap/state.json for upcoming.

What it does

Concretely, this is one command per target for the parts of TLS-renewal that normally cost an afternoon and a postmortem:

  • Customers / CAs deliver bundles in random formats — inspect and ingest normalise PFX (incl. Sectigo's RC2-40-CBC legacy MAC, via openssl pkcs12 -legacy shell-out), PEM bundles, PKCS#7, archives, and separate-file layouts to one canonical CertBundle. inspect also accepts cert-only CA deliveries (no private key yet) — deployment commands keep requiring the key.
  • Leaf-only bundles get AIA-walked into a complete chain (--fetch-intermediates).
  • Key↔cert mismatches and SAN/host mismatches are caught at plan, not at deployment.
  • On Kubernetes, apply k8s deletes the cert-manager Certificate that produced the secret and strips the cert-manager.io/cluster-issuer annotation, then replaces the kubernetes.io/tls secret in place (a single PUT — no window in which the secret is absent). With --argocd-app, the Application's automated-sync policy is saved to an annotation and disabled, RespectIgnoreDifferences=true and ignoreDifferences entries are patched in (idempotently), and after the swap the saved policy is restored with selfHeal forced off — because selfHeal ignores ignoreDifferences. prune and the rest of the policy come back exactly as they were; an app that wasn't auto-syncing stays that way.
  • Applications owned by an ApplicationSet or a parent app (app-of-apps) are detected and block the plan: the owning controller would revert certswap's patches, so the ignoreDifferences belongs in git there. --argocd-force-managed overrides with a warning.
  • On a VM, apply ssh uses your ~/.ssh/config, scp-uploads to a random /tmp/ file, atomic-mv's into place, chmod/chown's, runs your reload command, post-checks, and restores backups on failure.
  • On Proxmox, apply proxmox is apply ssh with the pveproxy defaults baked in.
  • Every apply writes both evidence.json (pydantic-modeled, for machines) and evidence.md (for the next human who'll renew this cert a year from now).

Exit codes

Code Meaning
0 Success
10 Validation failed (plan blocked, host/key mismatch)
20 Target drift
30 Ingest failed (bad format, wrong password)
40 Remote unreachable
50 Apply failed mid-flight
60 Verify failed post-apply

License

EUPL-1.2.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

certswap-0.2.0.tar.gz (47.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

certswap-0.2.0-py3-none-any.whl (72.6 kB view details)

Uploaded Python 3

File details

Details for the file certswap-0.2.0.tar.gz.

File metadata

  • Download URL: certswap-0.2.0.tar.gz
  • Upload date:
  • Size: 47.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for certswap-0.2.0.tar.gz
Algorithm Hash digest
SHA256 966524c1e6acc3008ee2c87190f16a1e8315d57c305f84658391c7e9b8fd7a32
MD5 171ac98dbfdbdd30e6c2e169576a5431
BLAKE2b-256 b2c7bb5e0232315ff5c5b2015f1534ce4ae406966900ec567b800994d7c61cbb

See more details on using hashes here.

Provenance

The following attestation bundles were made for certswap-0.2.0.tar.gz:

Publisher: release.yml on MWest2020/certswap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file certswap-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: certswap-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 72.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for certswap-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 36fe12c7f1a5ff5e6b28f3693692d31dfc5ca17824e19c02439208ff1a3a2f48
MD5 a818beb84483c90d3ce26b6252ca8910
BLAKE2b-256 9803acd97d8f7c7e984e8af45f87edbb486c64213b543bbfe4a9a85da2f67e09

See more details on using hashes here.

Provenance

The following attestation bundles were made for certswap-0.2.0-py3-none-any.whl:

Publisher: release.yml on MWest2020/certswap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page