Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).

### Changed

- **Breaking:** Removed `pat.secret.hashedTokenKey` from PAT configuration. The
SHA256 hash is now computed automatically at seed time by Initium v1.0.4 using
MiniJinja's `sha256` and `base64encode` filters. Users only need to supply the
plaintext PAT token in their Kubernetes Secret.
**Migration:** Remove the `hashedToken` key from your PAT Secret and the
`pat.secret.hashedTokenKey` from your values. Only `pat.secret.tokenKey` (default: `"token"`) is needed.
- **Breaking:** Replaced raw DSN secret (`server.secrets.storeDsn`) with structured
`database.*` configuration. The chart now constructs the DSN internally from
`database.type`, `database.host`, `database.port`, `database.user`, `database.name`,
Expand Down
20 changes: 7 additions & 13 deletions charts/netbird/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,39 +169,34 @@ creation — useful for automation, CI/CD, and GitOps workflows.
### Generating a PAT

NetBird PATs have the format `nbp_<30-char-secret><6-char-checksum>` (40
chars total). The database stores a base64-encoded SHA256 hash.

Generate a token and its hash:
chars total). The SHA256 hash required by the database is computed
automatically by the seed process (Initium v1.0.4+) — you only need to
generate the plaintext token.

```bash
# Using Python
python3 -c "
import hashlib, base64, secrets, zlib
import secrets, zlib
secret = secrets.token_urlsafe(22)[:30]
checksum = zlib.crc32(secret.encode()) & 0xffffffff
chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
cs = ''
v = checksum
while v > 0: cs = chars[v % 62] + cs; v //= 62
token = 'nbp_' + secret + cs.rjust(6, '0')
hashed = base64.b64encode(hashlib.sha256(token.encode()).digest()).decode()
print(f'Token: {token}')
print(f'Hash: {hashed}')
print(f'Token: {token}')
"

# Or using openssl
# Or using openssl (simplified checksum)
TOKEN="nbp_$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c30)000000"
HASH=$(printf '%s' "$TOKEN" | openssl dgst -sha256 -binary | openssl base64 -A)
echo "Token: $TOKEN"
echo "Hash: $HASH"
```

### Creating the Secret

```bash
kubectl create secret generic netbird-pat \
--from-literal=token='nbp_...' \
--from-literal=hashedToken='base64hash...' \
-n netbird
```

Expand Down Expand Up @@ -273,9 +268,8 @@ curl -H "Authorization: Token nbp_..." https://netbird.example.com/api/groups
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `pat.enabled` | bool | `false` | Enable PAT seeding via post-install Job |
| `pat.secret.secretName` | string | `""` | Kubernetes Secret containing token and hash |
| `pat.secret.secretName` | string | `""` | Kubernetes Secret containing the plaintext PAT |
| `pat.secret.tokenKey` | string | `"token"` | Key in Secret for the plaintext PAT |
| `pat.secret.hashedTokenKey` | string | `"hashedToken"` | Key in Secret for the base64-encoded SHA256 hash |
| `pat.name` | string | `"helm-seeded-token"` | Display name for the PAT |
| `pat.userId` | string | `"helm-seed-user"` | User ID for the service user |
| `pat.accountId` | string | `"helm-seed-account"` | Account ID for the service user |
Expand Down
7 changes: 4 additions & 3 deletions charts/netbird/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,9 @@ inserting a Personal Access Token into the database.
The seed waits for the personal_access_tokens table (created by NetBird
on startup via GORM AutoMigrate), then idempotently inserts the
account, user, and PAT records.
MiniJinja placeholders:
{{ env.PAT_HASHED_TOKEN }} — base64-encoded SHA256 hash of the PAT
MiniJinja placeholders (Initium v1.0.4+):
{{ env.PAT_TOKEN | sha256("bytes") | base64_encode }} — computes the
base64-encoded SHA256 hash from the plaintext PAT at seed time.
*/}}
{{- define "netbird.pat.seedSpec" -}}
database:
Expand Down Expand Up @@ -313,7 +314,7 @@ phases:
- id: "helm-seeded-pat"
user_id: {{ .Values.pat.userId | quote }}
name: {{ .Values.pat.name | quote }}
hashed_token: "{{ "{{ env.PAT_HASHED_TOKEN }}" }}"
hashed_token: "{{ "{{ env.PAT_TOKEN | sha256(\"bytes\") | base64_encode }}" }}"
expiration_date: {{ now | dateModify (printf "+%dh" (mul .Values.pat.expirationDays 24)) | date "2006-01-02 15:04:05" | quote }}
created_by: {{ .Values.pat.userId | quote }}
created_at: {{ now | date "2006-01-02 15:04:05" | quote }}
Expand Down
4 changes: 2 additions & 2 deletions charts/netbird/templates/pat-seed-job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ spec:
- --spec
- /spec/pat-seed.yaml
env:
- name: PAT_HASHED_TOKEN
- name: PAT_TOKEN
valueFrom:
secretKeyRef:
name: {{ .Values.pat.secret.secretName }}
key: {{ .Values.pat.secret.hashedTokenKey }}
key: {{ .Values.pat.secret.tokenKey }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
Expand Down
4 changes: 2 additions & 2 deletions charts/netbird/templates/server-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,11 @@ spec:
- --spec
- /spec/pat-seed.yaml
env:
- name: PAT_HASHED_TOKEN
- name: PAT_TOKEN
valueFrom:
secretKeyRef:
name: {{ .Values.pat.secret.secretName }}
key: {{ .Values.pat.secret.hashedTokenKey }}
key: {{ .Values.pat.secret.tokenKey }}
securityContext:
runAsUser: 0
readOnlyRootFilesystem: true
Expand Down
4 changes: 2 additions & 2 deletions charts/netbird/tests/pat-seed-configmap_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,14 @@ tests:
path: data["pat-seed.yaml"]
pattern: "name: accounts"

- it: should use PAT_HASHED_TOKEN MiniJinja placeholder
- it: should compute hashed_token from PAT_TOKEN via sha256 and base64_encode
set:
pat.enabled: true
pat.secret.secretName: my-pat-secret
asserts:
- matchRegex:
path: data["pat-seed.yaml"]
pattern: "env\\.PAT_HASHED_TOKEN"
pattern: 'env\.PAT_TOKEN \| sha256\("bytes"\) \| base64_encode'

- it: should seed account user and pat tables
set:
Expand Down
8 changes: 4 additions & 4 deletions charts/netbird/tests/pat-seed-job_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,11 @@ tests:
path: spec.template.spec.containers[0].image
value: "ghcr.io/kitstream/initium:1.0.4"

- it: should inject PAT_HASHED_TOKEN env var from secret
- it: should inject PAT_TOKEN env var from secret
set:
pat.enabled: true
pat.secret.secretName: my-pat-secret
pat.secret.hashedTokenKey: myHashKey
pat.secret.tokenKey: myTokenKey
database.type: postgresql
database.host: pg.example.com
database.user: netbird
Expand All @@ -175,11 +175,11 @@ tests:
- contains:
path: spec.template.spec.containers[0].env
content:
name: PAT_HASHED_TOKEN
name: PAT_TOKEN
valueFrom:
secretKeyRef:
name: my-pat-secret
key: myHashKey
key: myTokenKey

- it: should inject DB_PASSWORD for postgresql
set:
Expand Down
8 changes: 4 additions & 4 deletions charts/netbird/tests/server-deployment_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -399,21 +399,21 @@ tests:
- --spec
- /spec/pat-seed.yaml

- it: should inject PAT_HASHED_TOKEN into sidecar
- it: should inject PAT_TOKEN into sidecar
set:
pat.enabled: true
pat.secret.secretName: my-pat-secret
pat.secret.hashedTokenKey: myHash
pat.secret.tokenKey: myToken
database.type: sqlite
asserts:
- contains:
path: spec.template.spec.initContainers[1].env
content:
name: PAT_HASHED_TOKEN
name: PAT_TOKEN
valueFrom:
secretKeyRef:
name: my-pat-secret
key: myHash
key: myToken

- it: should run sidecar as root for SQLite PVC access
set:
Expand Down
14 changes: 7 additions & 7 deletions charts/netbird/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,27 +84,27 @@ database:
# deployment. This enables immediate API access without manual token creation.
#
# Prerequisites:
# 1. Generate a PAT and its hash (see README for instructions)
# 2. Create a Kubernetes Secret with both values:
# 1. Generate a PAT token (see README for instructions)
# 2. Create a Kubernetes Secret with the token:
#
# kubectl create secret generic netbird-pat \
# --from-literal=token='nbp_...' \
# --from-literal=hashedToken='base64hash...' \
# -n netbird
#
# The SHA256 hash required by the database is computed automatically at
# seed time by Initium (v1.0.4+) — you only need to supply the plaintext token.
# =============================================================================
pat:
# -- Enable PAT seeding. When true, a post-install Job inserts a
# Personal Access Token into the database.
enabled: false

# -- Kubernetes Secret containing the PAT credentials.
# -- Kubernetes Secret containing the PAT token.
secret:
# -- Name of the existing Secret with token and hashed token.
# -- Name of the existing Secret holding the plaintext PAT.
secretName: ""
# -- Key in the Secret holding the plaintext PAT (e.g. "nbp_...").
tokenKey: "token"
# -- Key in the Secret holding the base64-encoded SHA256 hash.
hashedTokenKey: "hashedToken"

# -- Display name for the seeded PAT.
name: "helm-seeded-token"
Expand Down
11 changes: 4 additions & 7 deletions ci/scripts/e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ EOF
# NetBird PAT format: nbp_ (4) + secret (30) + base62(CRC32(secret)) (6) = 40 chars
generate_pat_secret() {
log "Generating PAT secret for testing..."
read -r PAT_TOKEN PAT_HASH < <(python3 -c "
import hashlib, base64, binascii
PAT_TOKEN=$(python3 -c "
import binascii

BASE62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
def base62_encode(num, length=6):
Expand All @@ -176,14 +176,11 @@ def base62_encode(num, length=6):
secret = 'TestSecretValue1234567890ABCDE' # exactly 30 chars
crc = binascii.crc32(secret.encode()) & 0xFFFFFFFF
token = 'nbp_' + secret + base62_encode(crc)
h = base64.b64encode(hashlib.sha256(token.encode()).digest()).decode()
print(token, h)
print(token)
")
log "Test PAT token: $PAT_TOKEN (length=${#PAT_TOKEN})"
log "Test PAT hash: $PAT_HASH"
kubectl -n "$NAMESPACE" create secret generic netbird-pat \
--from-literal=token="$PAT_TOKEN" \
--from-literal=hashedToken="$PAT_HASH"
--from-literal=token="$PAT_TOKEN"
}
case "$BACKEND" in
sqlite)
Expand Down