mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-10 23:49:09 +02:00
Read the PKCS#11 token PIN from NB_TPM_PIN instead of the profile config
This commit is contained in:
@@ -18,7 +18,7 @@ to one WireGuard peer key and cannot be replayed by another peer.
|
||||
| Windows | signed-in user's `CurrentUser\MY` | a helper launched with that session's token |
|
||||
| Linux and others | PEM directory: `CertStoreDir` in the profile config, else `NB_CERT_STORE_DIR`, else `/etc/netbird/certs` | the daemon, directly |
|
||||
| Linux | a `TSS2 PRIVATE KEY` file in that directory, signed by the TPM | the daemon, through `/dev/tpmrm0` |
|
||||
| Linux | a PKCS#11 token, tpm2-pkcs11 for one, enabled by `CertPKCS11PIN` in the profile config | the daemon, through the token's module, in builds with the `pkcs11` tag |
|
||||
| Linux | a PKCS#11 token, tpm2-pkcs11 for one, enabled by `NB_TPM_PIN` in the daemon's environment | the daemon, through the token's module, in builds with the `pkcs11` tag |
|
||||
|
||||
macOS and Windows both keep per-user certificates out of reach of a privileged daemon,
|
||||
and both are handled the same way: the daemon reads the machine store itself and
|
||||
@@ -147,11 +147,12 @@ NB_TPM_DEVICE=/tmp/swtpm.sock go test ./client/internal/certproof/ -run TestColl
|
||||
|
||||
Distributions that follow Red Hat's guidance reach the TPM through tpm2-pkcs11, a PKCS#11
|
||||
module whose token holds both the key and, after `tpm2_ptool addcert`, the certificate.
|
||||
The store reads that token when the profile config, `/etc/netbird/config.json` by default,
|
||||
carries the token's user PIN:
|
||||
The store reads that token when the daemon's environment carries the token's user PIN in
|
||||
`NB_TPM_PIN`. The PIN is never read from the profile config or a command-line flag; set it
|
||||
on the service instead:
|
||||
|
||||
```json
|
||||
"CertPKCS11PIN": "1234"
|
||||
```sh
|
||||
netbird service install --service-env NB_TPM_PIN=1234
|
||||
```
|
||||
|
||||
That alone opens the first token the p11-kit proxy exposes, which is tpm2-pkcs11 on a
|
||||
@@ -166,7 +167,7 @@ on a host with several tokens or without p11-kit:
|
||||
names the library to load; `module-name=tpm2_pkcs11` resolves to `libtpm2_pkcs11.so` on
|
||||
the loader's search path, and with neither the p11-kit proxy is loaded, which exposes every
|
||||
module the system has registered. The URI may carry the PIN itself, as `pin-value` inline
|
||||
or `pin-source` naming a file, and `CertPKCS11PIN` takes precedence over both. Without any
|
||||
or `pin-source` naming a file, and `NB_TPM_PIN` takes precedence over both. Without any
|
||||
PIN no login happens, and tpm2-pkcs11 then shows no private keys at all. Every other
|
||||
attribute is ignored.
|
||||
|
||||
@@ -184,9 +185,10 @@ Each operation opens a session, logs in, works, logs out and closes, so no token
|
||||
outlives a call, and the PEM directory keeps working when the token does not: the two are
|
||||
queried together and a failing token is logged rather than hiding file certificates.
|
||||
|
||||
Two consequences of the PIN are worth knowing. It is a secret on disk, which the profile
|
||||
config already is: it holds the WireGuard private key and is written readable by root
|
||||
alone, and the debug bundle's config dump leaves `CertPKCS11PIN` out. And a wrong PIN
|
||||
Two consequences of the PIN are worth knowing. It lives in the service definition
|
||||
(the systemd unit environment, for one), so it stays out of the profile config and the
|
||||
debug bundle, which only records whether `CertPKCS11URI` is set because a URI may carry
|
||||
`pin-value`. And a wrong PIN
|
||||
counts against the TPM's dictionary-attack lockout, which is shared with everything else
|
||||
on the machine that uses the TPM.
|
||||
|
||||
@@ -194,7 +196,7 @@ The module is loaded at runtime without cgo, through `purego`, which means the b
|
||||
dynamically linked against libc. The store is therefore compiled in only with `-tags pkcs11`
|
||||
on linux/amd64 and linux/arm64: the deb and rpm packages are built that way, since they
|
||||
target glibc distributions, while the release tarballs and the Alpine-based container
|
||||
images keep the fully static build. Without the tag, setting `CertPKCS11PIN` logs that
|
||||
images keep the fully static build. Without the tag, setting `NB_TPM_PIN` logs that
|
||||
the build lacks the support.
|
||||
|
||||
To exercise the path without hardware, initialise a SoftHSM token and run the end-to-end
|
||||
|
||||
@@ -8,12 +8,17 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/pkcs11"
|
||||
)
|
||||
|
||||
// PINEnv carries the user PIN of the PKCS#11 token. It is read from the daemon's
|
||||
// environment only, so the PIN never lands in the profile config or on a command line.
|
||||
const PINEnv = "NB_TPM_PIN"
|
||||
|
||||
// PKCS11Config names the token whose certificates the store yields. URI is an RFC 7512
|
||||
// PKCS#11 URI, or empty for the first token the p11-kit proxy exposes. PIN is the user
|
||||
// PIN, and takes precedence over a pin-value or pin-source the URI carries.
|
||||
@@ -22,6 +27,11 @@ type PKCS11Config struct {
|
||||
PIN string
|
||||
}
|
||||
|
||||
// PINFromEnv returns the token PIN set in NB_TPM_PIN, or empty when it is unset.
|
||||
func PINFromEnv() string {
|
||||
return os.Getenv(PINEnv)
|
||||
}
|
||||
|
||||
// PKCS11Store yields the identities of a PKCS#11 token, which is how tpm2-pkcs11 exposes
|
||||
// TPM-held keys on Linux. Certificates on the token are paired with keys by CKA_ID, the
|
||||
// convention tpm2_ptool addcert and pkcs11-tool follow; certificate files in the PEM
|
||||
|
||||
@@ -674,7 +674,7 @@ func createEngineConfig(key wgtypes.Key, config *profilemanager.Config, peerConf
|
||||
|
||||
CertStore: certproof.Config{
|
||||
Dir: config.CertStoreDir,
|
||||
PKCS11: certproof.PKCS11Config{URI: config.CertPKCS11URI, PIN: config.CertPKCS11PIN},
|
||||
PKCS11: certproof.PKCS11Config{URI: config.CertPKCS11URI, PIN: certproof.PINFromEnv()},
|
||||
},
|
||||
|
||||
MTU: selectMTU(config.MTU, peerConfig.Mtu),
|
||||
|
||||
@@ -743,6 +743,8 @@ func (g *BundleGenerator) addCommonConfigFields(configContent *strings.Builder)
|
||||
configContent.WriteString(fmt.Sprintf("LocalMetricsEnabled: %v\n", g.internalConfig.LocalMetricsEnabled))
|
||||
configContent.WriteString(fmt.Sprintf("LocalMetricsAddress: %s\n", g.internalConfig.LocalMetricsAddress))
|
||||
configContent.WriteString(fmt.Sprintf("SyncMessageVersion: %v\n", g.internalConfig.SyncMessageVersion))
|
||||
configContent.WriteString(fmt.Sprintf("CertStoreDir: %s\n", g.internalConfig.CertStoreDir))
|
||||
configContent.WriteString(fmt.Sprintf("CertPKCS11URISet: %v\n", g.internalConfig.CertPKCS11URI != ""))
|
||||
|
||||
if g.internalConfig.DisableNotifications != nil {
|
||||
configContent.WriteString(fmt.Sprintf("DisableNotifications: %v\n", *g.internalConfig.DisableNotifications))
|
||||
|
||||
@@ -846,6 +846,7 @@ func TestAddConfig_AllFieldsCovered(t *testing.T) {
|
||||
"Name": "non-config: profile name is not needed for debug purposes",
|
||||
"policy": "non-config: in-memory MDM policy snapshot, surfaced via Config.Policy() / GetConfigResponse.MDMManagedFields",
|
||||
"DebugBundleUploadURL": "sensitive: MDM-provided upload URL may carry credentials or query tokens; kept out of the shared bundle",
|
||||
"CertPKCS11URI": "sensitive: the URI may carry the token PIN as pin-value; only whether it is set is rendered",
|
||||
}
|
||||
|
||||
mURL, _ := url.Parse("https://api.example.com:443")
|
||||
|
||||
@@ -191,13 +191,10 @@ type Config struct {
|
||||
// NB_CERT_STORE_DIR or /etc/netbird/certs; see client/internal/certproof/README.md.
|
||||
CertStoreDir string
|
||||
|
||||
// CertPKCS11PIN is the user PIN of the PKCS#11 token, tpm2-pkcs11 for one, whose
|
||||
// certificates answer certificate posture checks on Linux. Setting it enables the
|
||||
// token store; see client/internal/certproof/README.md.
|
||||
CertPKCS11PIN string
|
||||
|
||||
// CertPKCS11URI is the RFC 7512 URI selecting that token and its module. Empty means
|
||||
// the first token the p11-kit proxy exposes.
|
||||
// CertPKCS11URI is the RFC 7512 URI selecting the PKCS#11 token, tpm2-pkcs11 for one,
|
||||
// and its module, whose certificates answer certificate posture checks on Linux.
|
||||
// Empty means the first token the p11-kit proxy exposes. The token's user PIN comes
|
||||
// from NB_TPM_PIN, never from this file; see client/internal/certproof/README.md.
|
||||
CertPKCS11URI string
|
||||
|
||||
// LazyConnection is the MDM-managed lazy-connection override ("on"/"off"/"").
|
||||
|
||||
Reference in New Issue
Block a user