Install the management portal offline
The portal is on main. There is no packaged installer and no systemd unit in the repository
yet; the unit below is an example.
The portal is one static Go binary, portal, with no third-party modules in the production
build. It runs on one Linux VM inside the enclave, makes no outbound connections, and needs no
container runtime, database or package registry. Workstations pull from it; it never connects to
them.
What you need
- A Linux VM with systemd 250 or later (for encrypted credentials), ideally with a TPM.
- A server certificate and key for the portal's name, issued by the enclave CA.
- The enclave CA certificate that issues your administrators' smart-card certificates.
- A Go toolchain on the build machine. The VM itself needs none.
Build
cd testifysec/portal
CGO_ENABLED=0 go build -trimpath -o portal .
go test ./...
Copy portal to the VM, for example to /usr/local/bin/portal.
Create the service user and data directory
The portal refuses to create its data directory; the installer does, so its existence is durable before the first profile version is written.
useradd --system --no-create-home --shell /usr/sbin/nologin badger-portal
install -d -m 0700 -o badger-portal -g badger-portal /var/lib/badger-portal
Seal the signing key
The signing key is never plaintext on disk. Generate it and seal it as a systemd encrypted credential bound to the TPM:
portal -generate-key | systemd-creds encrypt --with-key=tpm2 --name=portal-signing-key - /etc/credstore.encrypted/portal-signing-key
Use --with-key=host+tpm2, or --with-key=host on a VM with no TPM. A host-key-only seal is as
safe as the host key file. systemd decrypts the credential at start into memory only the service
can read; the portal loads it from $CREDENTIALS_DIRECTORY and refuses to start without it.
There is no API to export the key. Back it up, if at all, under your key-custody procedure.
Seal the TLS server key the same way, so it is not plaintext on disk either, then remove the plaintext copy:
systemd-creds encrypt --with-key=tpm2 --name=server-key server.key /etc/credstore.encrypted/server-key
shred -u server.key
systemd exposes it to the service at /run/credentials/badger-portal.service/server-key, which
the configuration below names. The unit must be called badger-portal.service for that path.
Write the configuration
{
"listen": ":8443",
"tlsCert": "/etc/badger-portal/server.crt",
"tlsKey": "/run/credentials/badger-portal.service/server-key",
"clientCa": "/etc/badger-portal/enclave-ca.pem",
"dataDir": "/var/lib/badger-portal",
"signingKeyCredential": "portal-signing-key",
"tenantId": "tenant-example",
"minimumSequence": 1,
"bootstrap": {
"CN=ADMIN.ALICE,OU=Example,O=Example": "admin",
"CN=APPROVER.BOB,OU=Example,O=Example": "approver"
}
}
Unknown keys are refused. minimumSequence should be at least the minimum sequence your
admission build trusts.
bootstrap names at most one certificate subject for admin and one for approver, written in
RFC 2253 order (most specific first, as above). It lets those two people act only until a user in
the portal's directory first holds that role; after that the bootstrap entry stops working for
good, even if every holder is later offboarded. It is how the first administrator and approver
grant real roles, not a place to list users.
Run it as a service
An example unit, saved as /etc/systemd/system/badger-portal.service:
[Unit]
Description=Badger management portal
After=network-online.target
[Service]
User=badger-portal
ExecStart=/usr/local/bin/portal -config /etc/badger-portal/config.json
LoadCredentialEncrypted=portal-signing-key
LoadCredentialEncrypted=server-key
NoNewPrivileges=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/badger-portal
[Install]
WantedBy=multi-user.target
The portal serves HTTPS only, with TLS 1.2 or later, and verifies every client certificate against
clientCa. There is no plaintext port. GET /healthz is the only route that answers without a
client certificate:
curl --cacert /etc/badger-portal/enclave-ca.pem https://portal.example.invalid:8443/healthz
The portal takes an exclusive lock on its audit log, so a second process on the same data directory refuses to start.
Add the first users
Roles come from the portal's user directory. The portal stores no passwords: a user is an identity the enclave PKI already issued, identified by the verified certificate subject.
- The bootstrap administrator provisions each person (
POST /api/v1/users). A new user is pending, with no role. - The administrator requests a role for the user (
admin,approverorauditor). - An approver who is neither the requester nor the user approves or rejects it in the approver queue. Only active users with a role can act.
- The administrator adds users to the deployments they work on.
Dropping a role takes effect at once. Offboarding is final: it clears the user's role, deployment memberships and pending requests in one step. Every change is one record in the audit log. A person's clearance is not the portal's to decide; see shared responsibility. The calls are listed in the portal API reference.
Back up and restore
Stop the service or snapshot the VM, then copy the data directory. Every file in it is written
once or appended to, so a copy taken between writes is consistent. Restoring an older backup and
signing again would reuse sequence numbers; check that each profile's highest stored sequence is
at least what workstations last saw before you sign anything. A portal verify-audit command and
a refusal to start on an older audit head are planned and not built.
Next
Reference generated from the product documentation. Match commands and support details to your installed release.