mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-10 15:39:07 +02:00
Test the PKCS#11 build against SoftHSM in CI and warn once where the build has no driver
This commit is contained in:
@@ -207,6 +207,42 @@ jobs:
|
||||
# regression test, and need no frontend bundle.
|
||||
run: CGO_ENABLED=1 go test -timeout 5m ./client/ui/authsession/... ./client/ui/i18n/... ./client/ui/preferences/... ./client/ui/services/...
|
||||
|
||||
test_client_pkcs11:
|
||||
name: "Client PKCS#11 / Unit"
|
||||
# The deb and rpm packages ship the client built with the pkcs11 tag, which loads
|
||||
# PKCS#11 modules through purego. The other jobs build without the tag, so this one
|
||||
# compiles that driver and signs with a real SoftHSM token, the build release ships.
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: false
|
||||
|
||||
- name: Install SoftHSM
|
||||
run: sudo apt update && sudo apt install -y -q softhsm2
|
||||
|
||||
- name: Initialise token
|
||||
run: |
|
||||
mkdir -p "$RUNNER_TEMP/softhsm/tokens"
|
||||
printf 'directories.tokendir = %s\nobjectstore.backend = file\n' "$RUNNER_TEMP/softhsm/tokens" > "$RUNNER_TEMP/softhsm/softhsm2.conf"
|
||||
echo "SOFTHSM2_CONF=$RUNNER_TEMP/softhsm/softhsm2.conf" >> "$GITHUB_ENV"
|
||||
SOFTHSM2_CONF="$RUNNER_TEMP/softhsm/softhsm2.conf" softhsm2-util --init-token --free --label netbird --pin 1234 --so-pin 1234
|
||||
|
||||
- name: Vet
|
||||
run: CGO_ENABLED=0 go vet -tags pkcs11 ./client/internal/pkcs11/... ./client/internal/certproof/...
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
NB_TEST_PKCS11_URI: "pkcs11:token=netbird?module-path=/usr/lib/softhsm/libsofthsm2.so&pin-value=1234"
|
||||
run: CGO_ENABLED=0 go test -tags pkcs11 -timeout 5m ./client/internal/pkcs11/... ./client/internal/certproof/...
|
||||
|
||||
test_client_on_docker:
|
||||
name: "Client (Docker) / Unit"
|
||||
needs: [build-cache]
|
||||
|
||||
@@ -197,8 +197,10 @@ 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 `NB_TPM_PIN` logs that
|
||||
the build lacks the support.
|
||||
images keep the fully static build. The arm and 386 packages carry the tag too but have
|
||||
no driver, so they behave like the static build. A build without support logs one warning
|
||||
when a token is configured and keeps reading the PEM directory. The "Client PKCS#11 /
|
||||
Unit" CI job builds with the tag and signs with a SoftHSM token.
|
||||
|
||||
To exercise the path without hardware, initialise a SoftHSM token and run the end-to-end
|
||||
test, which imports a key and certificate itself:
|
||||
|
||||
@@ -339,3 +339,26 @@ func TestNewPKCS11Store_PIN(t *testing.T) {
|
||||
assert.ErrorIs(t, err, errPINNeedsToken, "%s: a PIN must not go to whichever token is listed first", name)
|
||||
}
|
||||
}
|
||||
|
||||
// TestPKCS11Store_WrongPINIsTriedOnce logs in to the real token with a wrong PIN: the
|
||||
// token refuses it, and the next collection refuses to send the same PIN again rather
|
||||
// than spending another attempt of the token's lockout counter.
|
||||
func TestPKCS11Store_WrongPINIsTriedOnce(t *testing.T) {
|
||||
_, uri := pkcs11TestStore(t, "")
|
||||
|
||||
wrongPIN := "wrong-pin-" + t.Name()
|
||||
store, err := NewPKCS11Store(PKCS11Config{URI: uri, PIN: wrongPIN}, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = store.Candidates(context.Background())
|
||||
require.Error(t, err)
|
||||
assert.True(t, pkcs11.PINRejected(err), "the token itself rejects the PIN: %v", err)
|
||||
|
||||
_, err = store.Candidates(context.Background())
|
||||
assert.ErrorIs(t, err, errPINRejectedBefore, "the rejected PIN is not sent to the token again")
|
||||
|
||||
good, err := NewPKCS11Store(PKCS11Config{URI: uri}, "")
|
||||
require.NoError(t, err)
|
||||
_, err = good.Candidates(context.Background())
|
||||
assert.NoError(t, err, "the correct PIN for the same token is unaffected")
|
||||
}
|
||||
|
||||
@@ -2,7 +2,15 @@
|
||||
|
||||
package certproof
|
||||
|
||||
import log "github.com/sirupsen/logrus"
|
||||
import (
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/pkcs11"
|
||||
)
|
||||
|
||||
var unsupportedTokenOnce sync.Once
|
||||
|
||||
// DefaultStore is the PEM directory named by NB_CERT_STORE_DIR, or /etc/netbird/certs.
|
||||
func DefaultStore() Store {
|
||||
@@ -16,6 +24,12 @@ func storeWithToken(cfg Config) Store {
|
||||
if cfg.PKCS11.URI == "" && cfg.PKCS11.PIN == "" {
|
||||
return files
|
||||
}
|
||||
if !pkcs11.Supported() {
|
||||
unsupportedTokenOnce.Do(func() {
|
||||
log.Warnf("ignoring the configured PKCS#11 token: %v", pkcs11.ErrUnsupported)
|
||||
})
|
||||
return files
|
||||
}
|
||||
token, err := NewPKCS11Store(cfg.PKCS11, cfg.dir())
|
||||
if err != nil {
|
||||
log.Warnf("ignoring PKCS#11 URI: %v", err)
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/pkcs11"
|
||||
)
|
||||
|
||||
func TestStoreWithToken(t *testing.T) {
|
||||
@@ -23,10 +25,18 @@ func TestStoreWithToken(t *testing.T) {
|
||||
assert.IsType(t, &FileStore{}, storeWithToken(Config{PKCS11: PKCS11Config{URI: "not-a-pkcs11-uri"}}), "an invalid URI must not hide the PEM directory")
|
||||
assert.IsType(t, &FileStore{}, storeWithToken(Config{PKCS11: PKCS11Config{PIN: "1234"}}), "a PIN naming no token is refused and leaves the PEM directory")
|
||||
|
||||
for name, cfg := range map[string]PKCS11Config{
|
||||
configured := map[string]PKCS11Config{
|
||||
"env pin with token uri": {URI: "pkcs11:token=netbird", PIN: "1234"},
|
||||
"uri alone": {URI: "pkcs11:token=netbird?pin-value=1234"},
|
||||
} {
|
||||
}
|
||||
if !pkcs11.Supported() {
|
||||
for name, cfg := range configured {
|
||||
assert.IsType(t, &FileStore{}, storeWithToken(Config{Dir: dir, PKCS11: cfg}),
|
||||
"%s: a build without PKCS#11 support reads the PEM directory alone", name)
|
||||
}
|
||||
return
|
||||
}
|
||||
for name, cfg := range configured {
|
||||
store, ok := storeWithToken(Config{Dir: dir, PKCS11: cfg}).(Stores)
|
||||
require.True(t, ok, "%s joins the token to the PEM directory", name)
|
||||
require.Len(t, store, 2, name)
|
||||
|
||||
@@ -121,6 +121,11 @@ type module struct {
|
||||
cSign func(session ulong, data *byte, dataLen ulong, signature *byte, signatureLen *ulong) ulong
|
||||
}
|
||||
|
||||
// Supported reports whether this build can load PKCS#11 modules.
|
||||
func Supported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func load(path string) (driver, error) {
|
||||
lib, err := purego.Dlopen(path, purego.RTLD_NOW|purego.RTLD_LOCAL)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
package pkcs11
|
||||
|
||||
// Supported reports whether this build can load PKCS#11 modules.
|
||||
func Supported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func load(string) (driver, error) {
|
||||
return nil, ErrUnsupported
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user