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.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.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.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.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#