Skip to content

feat: add PKI HSM option#270

Open
carlosmonastyrski wants to merge 1 commit into
mainfrom
feat/hsm-pkcs11-handler
Open

feat: add PKI HSM option#270
carlosmonastyrski wants to merge 1 commit into
mainfrom
feat/hsm-pkcs11-handler

Conversation

@carlosmonastyrski

Copy link
Copy Markdown
Contributor

Description 📣

This pull request adds the HSM connector to the CLI. This new option will allow gateways to perform PTCS11 actions so the application can interact with HSMs as part of PKI code-signing

Type ✨

  • Bug fix
  • New feature
  • Improvement
  • Breaking change
  • Documentation

Tests 🛠️

# Here's some code block to paste some code snippets

@infisical-review-police

Copy link
Copy Markdown

💬 Discussion in Slack: #pr-review-cli-270-feat-add-pki-hsm-option

Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel.

@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds PKCS#11 / HSM support to the Infisical gateway, shipping as a separate infisical-pkcs11 binary (CGO + dynamic linking required). The gateway can now advertise pkcs11 capability on heartbeat, load a vendor PKCS#11 driver at startup, and serve HSM operations (test, generate-key-pair, get-public-key, sign) over a dedicated infisical-pkcs11 ALPN channel on the existing TLS connection.

  • Two new goreleaser build targets (Linux amd64 + arm64 with pkcs11 build tag) and a matching CI step that installs the aarch64 cross-compiler.
  • Three new files implement the PKCS#11 layer: a build-tag-gated stub (pkcs11_disabled.go), a full CGO implementation via github.com/miekg/pkcs11 (pkcs11_enabled.go), and a single-request-per-connection HTTP-over-TLS handler (pkcs11_handler.go).
  • The heartbeat now carries a capabilities JSON body; the GatewayHeartbeatRequest model and CallGatewayHeartBeatV2 signature are updated accordingly.

Confidence Score: 3/5

Two issues need attention before this is production-safe: the heartbeat body change affects all existing gateways (not just pkcs11-enabled ones), and the per-connection deadline can expire during mutex contention for concurrent HSM requests.

The heartbeat change silently alters the request body sent by every deployed gateway (non-pkcs11 included) because an initialized empty map is not omitted by omitempty. The per-connection 30-second deadline starts ticking before the global PKCS#11 mutex is acquired, so a long HSM operation on one connection can cause spurious timeout failures on concurrent connections. Both issues affect correctness on the happy path under realistic load conditions.

packages/gateway-v2/gateway.go (heartbeat capabilities map) and packages/gateway-v2/pkcs11_handler.go (connection deadline vs mutex ordering)

Security Review

  • PIN memory residue (pkcs11_handler.go UnmarshalJSON): zeroBytes(env.PIN) zeros the parsed []byte copy of the PIN but cannot zero the intermediate Go string (raw.PIN) or the raw JSON bytes that also contain the plaintext PIN. These remain in heap memory until GC collects them. This is a best-effort measure with a known gap.
  • No broader SSRF or injection vectors identified in the new code: the PKCS#11 module path is validated as an absolute local filesystem path before dlopen, no user-controlled URLs are fetched, and all HSM operations go through the existing mTLS-authenticated gateway channel.

Important Files Changed

Filename Overview
packages/gateway-v2/gateway.go Wires PKCS#11 module into the gateway lifecycle. Heartbeat now sends {"capabilities":{}} for all non-pkcs11 gateways due to empty-map vs nil-map omitempty behavior, changing the heartbeat body for every deployed gateway.
packages/gateway-v2/pkcs11_handler.go HTTP handler for PKCS#11 over TLS. The 30-second deadline is set before the global mutex is acquired, which can cause spurious timeout failures for concurrent requests while a long HSM operation holds the lock.
packages/gateway-v2/pkcs11_enabled.go Adds the CGO-backed PKCS#11 implementation using miekg/pkcs11. Operations are serialized by a global mutex, PIN zeroing is best-effort only, and RSA key-size detection relies on an exact modulus-byte-length check that won't recognize non-standard sizes.
packages/gateway-v2/pkcs11.go Defines the Pkcs11Module interface, error types/codes, and key algorithm constants. Clean, well-structured.
packages/gateway-v2/pkcs11_disabled.go Build-tag stub that returns a clear error when PKCS#11 support is not compiled in. Correct and unambiguous.
packages/api/api.go Adds body parameter to CallGatewayHeartBeatV2. The API change itself is correct, but the caller in gateway.go always passes an initialized (potentially empty) map rather than nil.
packages/api/model.go Adds GatewayHeartbeatRequest with omitempty capabilities map. Model is correct; the issue is in how the caller constructs the map.
packages/cmd/gateway.go Adds --pkcs11-module CLI flag with solid up-front validation: must be absolute, must exist, must not be a directory.
.goreleaser.yaml Adds two new pkcs11-tagged build targets (amd64 + arm64) with appropriate CGO toolchain settings and a separate pkcs11 archive. Build configuration looks correct.
.github/workflows/release_build_infisical_cli.yml Adds cross-compiler installation step for the aarch64 toolchain needed by the pkcs11 arm64 build. Straightforward CI change.

Comments Outside Diff (2)

  1. packages/gateway-v2/pkcs11_handler.go, line 953-990 (link)

    P1 Connection deadline starts before mutex is acquired

    The 30-second TLS deadline is set at the top of servePkcs11OverTLS, before any work is done. The global m.mu mutex in withSession serializes all PKCS#11 operations. If a long-running operation (e.g., RSA-4096 key generation on a slow HSM) is in progress when a second request arrives, the second connection's 30-second clock is already ticking while it waits to acquire the mutex. By the time the first operation finishes and the mutex is released, the second connection's deadline may have already expired, causing the write to fail with a deadline-exceeded error even though the HSM operation itself succeeds. Consider resetting the deadline after the HTTP request is parsed (just before calling ServeHTTP) so it more accurately covers only the HSM round-trip.

  2. packages/gateway-v2/pkcs11_handler.go, line 923-936 (link)

    P2 security PIN not fully erased from memory

    zeroBytes(env.PIN) zeros only the []byte field populated in UnmarshalJSON. The raw JSON bytes passed to json.Unmarshal and the intermediate raw.PIN Go string (an immutable heap allocation) also contain the plaintext PIN and are not touched by zeroBytes. Go strings cannot be zeroed by the program, and the GC decides when to reclaim them. This is a known limitation of using Go for PIN handling, but it is worth being explicit about: defer zeroBytes(env.PIN) is a best-effort measure that does not fully protect the PIN from memory inspection after the call returns.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (1): Last reviewed commit: "Add PKI HSM option" | Re-trigger Greptile

Comment thread packages/gateway-v2/gateway.go
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.

1 participant