CCAT CA — Provisioner set and reference tables#

This page is reference: lookup tables for the CCAT step-ca provisioner set, the SSH access tiers, the Ansible role tags exposed by ca_trust and hsm_host, and the lifetime flags accepted by step ca provisioner update. For the why behind these numbers, see CCAT Certificate Authority — Architecture and Design. For the operational procedures (adding, rotating, removing), see CA provisioner management.

The CCAT provisioner set#

Provisioners are the entry points through which clients request certs from step-ca. Each provisioner has a type (OIDC, JWK, ACME, SSHPOP, …), an authentication mechanism, and a set of claims that govern what kinds of certs it can issue and with what lifetimes. They live in ca.json inside the step-ca-data volume.

CCAT runs six provisioners. They are installed by the script step-ca/provisioners-add.sh.

Name

Type

Purpose

Default lifetime

Max lifetime

CCAT-GitHub

OIDC

Interactive SSH user certs via Dex + GitHub (team-gated)

16 h

16 h

prod-services

JWK

Production x509 / TLS certs

90 d (2160 h)

90 d

staging-services

JWK

Staging x509 / TLS certs

30 d (720 h)

30 d

service-accounts

JWK

SSH certs for automated services (Jenkins, ccat_transfer/bbcp, CI)

24 h

24 h

acme

ACME

Auto-TLS for internal services via ACME protocol

90 d

90 d

sshpop

SSHPOP

SSH host cert auto-renewal (host re-proves by signing with old cert)

7 d (168 h)

7 d

For the rationale behind each lifetime, see CCAT Certificate Authority — Architecture and Design § “Why these lifetimes” and § “What SSHPOP is and why it’s clever”.

SSH access tiers#

Tier

Primary auth

Backup auth

Linux account

Sudo

1 — Admin

step ssh login (Dex → GitHub → step-ca cert)

Nitrokey FIDO2 key in static authorized_keys

Personal user, wheel

Yes

2 — Staff

step ssh login → cert

(none — off by design)

Personal user

No, unless opted in case-by-case

3 — Break-glass

(none — not reachable via SSH)

iLO console + password

breakglass local user

Yes

The narrative for what each tier means and how the tiers compose is in CCAT Certificate Authority — Architecture and Design § “SSH access tiers — narrative”.

Ansible role: ca_trust — distribute public trust material#

Applied to: all managed hosts (input_ccat, input_staging, ccat, eventually travel_hosts).

Responsibilities:

  • Copy root_ca.crt into the RHEL (/etc/pki/ca-trust/source/anchors/) or Debian (/usr/local/share/ca-certificates/) trust store and run update-ca-trust / update-ca-certificates.

  • Copy ssh_user_ca.pub to /etc/ssh/trusted_user_ca_keys and set TrustedUserCAKeys in sshd_config.

  • Register ssh_host_ca.pub in /etc/ssh/ssh_known_hosts with a @cert-authority *.data.ccat.uni-koeln.de line.

  • Render /etc/ssh/auth_principals/<user> for every user in group_vars/all/users.yml with a github: field, and set AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u in sshd_config. This closes the loop between the GitHub-team authorization gate (enforced at Dex) and which Linux user each certificate is permitted to become on the target host.

Source files for the CA artifacts live in ansible/roles/ca_trust/files/. The role is a safe no-op until those files exist — it guards every task on file presence on the controller and warns if nothing is found. This means the role can be merged and wired into playbook_setup_vms.yml before the ceremony without affecting any running host. Principal-file rendering is gated on ssh_user_ca.pub being present for the same reason: a principals file without a trusted user CA is meaningless.

Tags#

Run the full role:

cd ansible
ansible-playbook playbook_setup_vms.yml --tags ca_trust

Sub-tag

What it does

ca_trust_x509

Just the system x509 trust store

ca_trust_ssh

SSH user CA + host CA + principal files + sshd_config

ca_trust_principals

Only the auth_principals/%u rendering and sshd_config line (useful when adding a new operator to users.yml and you want to push their mapping without touching anything else)

How the github: field becomes a working cert path#

The full chain, top to bottom:

  1. github.com: you add the operator to ccatobs/datacenter.

  2. users.yml (this repo): the same operator has a Linux user entry with github: <their-github-login>.

  3. ca_trust run: make play-input-ccat T=ca_trust (or the full playbook) renders /etc/ssh/auth_principals/<linux-user> on every target, containing exactly their GitHub login on one line, and ensures sshd_config has AuthorizedPrincipalsFile.

  4. Operator runs step ssh login: step-cli talks to Dex, Dex checks the GitHub team, step-ca issues a cert whose first principal is their GitHub login (via step-ca/ssh-user-template.tpl).

  5. Operator runs ssh <linux-user>@target: sshd on target sees the cert, reads /etc/ssh/auth_principals/<linux-user>, matches the cert’s principal list against that file, accepts. Login succeeds.

If any step in the chain is missing, the attempt fails cleanly:

  • Step 1 missing → Dex rejects at auth time

  • Step 2 missing → no principals file → sshd falls back to the default “principal must equal username” rule

  • Step 3 not run → principals file doesn’t exist → same default fallback

  • Step 4 cert expired → step-cli offers a key, sshd falls back to the key-based path

  • Step 5 mismatch → permission denied

The chain is designed so a missing link never accidentally grants access: the failure mode is always “cleanly denied,” never “silently promoted.”

Onboarding a new operator#

  1. Confirm they’re in ccatobs/datacenter on github.com.

  2. Add a new user entry to group_vars/all/users.yml:

    - user: alice
      uid: 1002
      comment: "Alice Example"
      groups: "docker,ccat_deploy"
      password: "{{ vault_user_alice_password }}"
      github: alice-github
    
  3. Add vault_user_alice_password to the Ansible vault.

  4. Run make play-input-ccat T=users,ca_trust_principals to create the Linux user and drop their principals file on all prod hosts. (Or drop T= to apply everything.)

  5. Tell them to run step ca bootstrap + step ssh login, then ssh alice@<host>. The full client onboarding procedure lives in Client setup — SSH with step-ca certificates.

Off-boarding#

Fastest path: remove them from the ccatobs/datacenter GitHub team. Any new step ssh login fails at Dex. Their current cert expires within 16h. That’s usually enough.

Belt-and-braces path: also delete the github: field from their user entry in users.yml and re-run make play-input-ccat T=ca_trust_principals. The role explicitly scrubs the stale auth_principals/<user> file on the next run, so the cert path is closed even if GitHub membership somehow persists. Add them to users_removed if you also want their Linux account deleted.

Ansible role: hsm_host — prepare input-b for the HSM#

Applied to: input-b only (inside the - hosts: input-b play in playbook_setup_vms.yml).

Responsibilities:

  • Install opensc and opensc-tools packages on the host.

  • Deploy 99-nitrokey-hsm.rules udev rule granting the plugdev group access to the Nitrokey HSM 2 device node.

  • Run pkcs11-tool --list-slots and either warn (default) or fail (when _hsm_enforce_verify: true in host_vars) if the HSM is not detected.

Run standalone:

cd ansible
ansible-playbook playbook_setup_vms.yml --tags hsm_host -l input-b

Sub-tag

What it does

hsm_host_pkg

Install opensc and opensc-tools packages

hsm_host_udev

Deploy 99-nitrokey-hsm.rules udev rule

hsm_host_verify

Run pkcs11-tool --list-slots to confirm HSM is visible

To make verification strict once commissioning is done (so that an accidentally-unplugged HSM fails the playbook loudly), create ansible/host_vars/input-b/hsm.yml with:

_hsm_enforce_verify: true

Lifetime flags for step ca provisioner update#

When updating an existing provisioner’s lifetimes (see CA provisioner management § “Updating lifetimes on existing provisioners”), pass only the flags you want to change:

Flag

Applies to

Example

--x509-default-dur

x509 certs

2160h (90 days)

--x509-max-dur

x509 certs

4320h (180 days)

--ssh-user-default-dur

SSH user certs

16h

--ssh-user-max-dur

SSH user certs

24h

--ssh-host-default-dur

SSH host certs

168h (7 days)

--ssh-host-max-dur

SSH host certs

336h (14 days)

All durations are passed as Go time.Duration strings — h for hours, m for minutes. d and w are not supported.

Cross-references#