Skip to content

Add optional mTLS support to the tang pin#566

Open
liyue32 wants to merge 1 commit into
latchset:masterfrom
liyue32:tang-mtls-support
Open

Add optional mTLS support to the tang pin#566
liyue32 wants to merge 1 commit into
latchset:masterfrom
liyue32:tang-mtls-support

Conversation

@liyue32

@liyue32 liyue32 commented Jul 7, 2026

Copy link
Copy Markdown

Summary

Adds optional mutual-TLS (mTLS) support to the Tang pin, allowing Clevis to authenticate to a Tang server (typically fronted by a TLS-terminating reverse proxy) with a client certificate, and to verify the server certificate against a custom CA.
The feature is fully opt-in: when none of the new properties are set, the pin behaves exactly as before (plain HTTP/HTTPS with the system's default CA trust). Existing JWEs and workflows are unaffected.

Motivation

Adding mTLS lets the Tang server (typically via a TLS-terminating reverse proxy) authenticate the client's identity through its client certificate. This unlocks per-machine access control on top of the existing network-binding model. In particular, single-machine revocation: revoking or refusing a specific client certificate immediately prevents that one machine from recovering its keys, without affecting any other machine or requiring key rotation on the server. This matches how we want to adopt Clevis out of the box at Meta: bind machines to Tang for automated unlock, and retain the ability to revoke an individual host's recovery access by its identity. It's also useful more generally for any deployment where the Tang server is fronted by HTTPS with client-certificate authentication, or uses a server certificate signed by a private/internal CA.

What changed

Three new optional config properties are accepted by clevis encrypt tang:

  1. cacert: curl --cacert
  2. cert: curl --cert
  3. key: curl --key

When any of above are set:

  1. passed to curl when fetching the advertisement during encryption, and
  2. persisted into the JWE protected header under clevis.tang, so that clevis decrypt reuses them for the POST /rec request without any additional configuration (Clevis decryption takes no config by design, so this metadata must travel in the JWE).

Example:

  clevis encrypt tang '{
    "url":"https://tang.example.com",
    "cacert":"/etc/clevis/ca.crt",
    "cert":"/etc/clevis/client.crt",
    "key":"/etc/clevis/client.key"
  }' < PT > JWE

Backward compatibility

  • When no mTLS properties are set, the injected curl option array is empty, so curl is invoked identically to before.
  • Existing JWEs have no mTLS header fields; decryption reads them as absent and takes the unchanged code path.

Tests

  • Adds a new pin-tang-mtls integration test plus tang_generate_certs, tang_run_mtls, and tang_mtls_sanity_check helpers. The harness fronts tangd with a TLS listener (socat OPENSSL-LISTEN) that mandates client-certificate verification. The test covers:
    • Positive: encrypt (GET /adv) + decrypt (POST /rec) round-trip succeed over mTLS. Since the server enforces client-cert verification, success proves the pin actually presents the client certificate.
    • Header round-trip: the cacert/cert/key paths are correctly persisted into and read back from the JWE protected header.
    • Negative: without a client certificate, encryption fails — proving mTLS is enforced, not silently downgraded.

Allow the tang pin to authenticate to the Tang server with mutual TLS.
Three new optional config properties are accepted by clevis-encrypt-tang:

  cacert - CA bundle to verify the server certificate (curl --cacert)
  cert   - client certificate for mTLS                (curl --cert)
  key    - private key for the client certificate     (curl --key)

When none are set, behaviour is unchanged. The paths are persisted into
the JWE protected header so clevis-decrypt-tang reuses them for the
recovery (POST /rec) request without extra configuration.

Add a pin-tang-mtls integration test (TLS-fronted tangd via socat with
mandatory client-cert verification) plus tang_generate_certs and
tang_run_mtls helpers. The test skips cleanly when openssl or a
socat built with OpenSSL support is unavailable.
@sarroutbi

Copy link
Copy Markdown
Collaborator

/packit test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants