Skip to content

Security and compliance

This page is the security architecture and compliance reference for Bor. It is written for auditors, security officers and procurement teams who need to know how Bor protects data, which cryptography it uses, and how it maps to FIPS 140-3, BSI TR-02102, SOG-IS, eIDAS, NIS2, the EU Cybersecurity Act and GDPR. It describes the behavior of the community edition as shipped in the distribution packages.

Everything on this page was checked against the Bor source code. Operational procedures (protecting secrets, enforcing MFA, firewall rules, syslog forwarding, file permissions) are in the Hardening guide; this page links to it instead of repeating them.

Certification status

Bor itself holds no formal certification: no FIPS 140-3 module certificate of its own, no Common Criteria / EUCC certificate, no eIDAS qualified status. Bor is designed to align with the standards listed here. Where an external component holds a validation (the Go Cryptographic Module), this page says so and says exactly what it covers.

Security model and trust boundaries

Bor has two components: bor-server (web UI, REST API, gRPC services, internal certificate authority, PostgreSQL storage) and bor-agent (a root daemon on each managed Linux endpoint). The server never connects to endpoints; agents always connect to the server.

Boundary Direction Protocol Authentication Notes
Browser -> server Inbound to the web port (server.enrollment_port, default 8443; 443 recommended in production) HTTPS (TLS 1.2+), HTTP/2 or HTTP/1.1 Username + password (local or LDAP), optional TOTP / WebAuthn second factor; session in an HS256 JWT cookie REST API and the static web UI. CSRF protection, security headers, rate-limited login endpoints.
Agent -> server, enrollment Inbound to the web port gRPC over TLS (agent requires TLS 1.3) One-time enrollment token, or a Kerberos (SPNEGO) ticket The agent sends a certificate signing request (CSR); the server returns a signed client certificate and the CA certificate.
Agent -> server, policy stream Inbound to the policy port (server.policy_port, default 8444) gRPC over TLS 1.3, HTTP/2 only Mutual TLS: client certificate issued by the Bor CA, checked against the revocation list Long-lived stream for policy delivery, compliance reports, tamper reports, certificate renewal, disk-encryption escrow.
Server -> PostgreSQL Outbound Local Unix socket (default) or TCP with libpq sslmode Peer authentication (socket) or password Default package configuration uses the local socket with sslmode: disable.
Server -> LDAP / Active Directory Outbound LDAPS or StartTLS (TLS 1.2+), or plain LDAP if configured Service account bind, then user bind See LDAP.
Server -> Kerberos None at run time - Service keytab on disk The server validates tickets offline with its keytab; it does not contact the KDC.
Server -> Tang servers Outbound HTTPS (TLS 1.2+) or HTTP Advertisement signature (ES512) Only to fetch and verify advertisements of registered Tang servers.
Server -> ACME CA Outbound, plus inbound HTTP-01 / TLS-ALPN-01 challenges HTTPS; challenge listener on acme.http_port ACME account key Only with acme.enabled.
Server -> Flatpak remotes Outbound HTTPS (TLS 1.2+) - Catalog refresh; private network destinations blocked unless flatpak_catalog.allow_private_networks is set.
Server -> syslog / SIEM Outbound UDP, TCP, or TCP+TLS (TLS 1.2+) Server certificate of the receiver (TLS) Optional real-time audit forwarding.
Prometheus -> server Inbound to the metrics listener (default 127.0.0.1:9090) HTTP, or HTTPS with metrics.tls_cert_file/tls_key_file Optional bearer token Not exposed by default.

The web UI and enrollment share one port

The web UI, the REST API and the enrollment gRPC service are served by a single listener on server.enrollment_port; the server routes each request by its Content-Type. They cannot be split onto separate ports, and server.address binds both the web port and the policy port to the same address. Endpoints therefore need the web port for enrollment and for installing bor-agent from the server's /agent/ repository, in addition to the policy port.

Key design properties:

  • Agent identity is a certificate. After enrollment an agent never uses passwords or API keys. The certificate's common name is the node name, assigned by the server (the name requested in the CSR is discarded), and node names are unique.
  • The agent key never leaves the endpoint. The agent generates its key pair locally and sends only a CSR.
  • Server compromise does not unlock disks at boot. The server is not in the boot path; escrowed recovery keys are envelope-encrypted under a key encryption key (KEK) kept outside the database.
  • Least privilege on the server host. bor-server runs as the unprivileged bor user under a sandboxed systemd unit; see Built-in service hardening. bor-agent runs as root because it writes system policy files.

Cryptographic inventory

TLS listeners

Setting Web port (UI, REST, enrollment) Policy port (agents only) Agent client (both ports)
Minimum version TLS 1.2 TLS 1.3 TLS 1.3
Maximum version Go default (TLS 1.3) Go default (TLS 1.3) Go default (TLS 1.3)
TLS 1.2 cipher suites ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES128-GCM-SHA256, ECDHE-RSA-AES128-GCM-SHA256 not applicable not applicable
TLS 1.3 cipher suites Go defaults: TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256 (not configurable in Go) Go defaults (same) Go defaults (same)
Key exchange groups X25519, P-256, P-384 (in this order) Go defaults Offers X25519, P-256, P-384 only
ALPN h2, http/1.1 (plus acme-tls/1 when ACME is enabled) h2 h2 (gRPC)
Client certificate VerifyClientCertIfGiven: verified against the Bor CA if presented; required at the gRPC layer for every enrollment-service call except Enroll and KerberosEnroll RequireAndVerifyClientCert against the Bor CA Presents its client certificate after enrollment
Server certificate Internal-CA-signed, your own, or ACME Always the internal-CA-signed server certificate Verified against the stored Bor CA certificate after enrollment
Package app app policyclient

Because the agent offers only X25519, P-256 and P-384, the policy stream negotiates X25519 by default and P-256 in FIPS mode.

Other TLS clients in the server (ldap, Tang, Flatpak catalog, audit syslog) set a minimum of TLS 1.2 and otherwise use Go defaults.

Certificates and PKI

Item Algorithm / curve Signature Lifetime and rotation Package
Internal CA (auto-generated in ca.autogen_dir) ECDSA P-384 Self-signed, ECDSA with SHA-384 10 years. No automatic rotation; rotating the CA means re-enrolling every agent. pki
Internal CA in an HSM (PKCS#11, optional build) ECDSA P-384 generated on the token ECDSA with SHA-384 10 years pki (pkcs11 build tag)
Server certificate (ui.crt in tls.autogen_dir) ECDSA P-256, digitalSignature key usage, serverAuth EKU Signed by the CA (ECDSA with SHA-384) 1 year. Regenerated at server start when it no longer verifies against the CA (including when expired) or when required host names are missing. pki
Agent client certificate ECDSA P-256 key generated on the endpoint, digitalSignature key usage, clientAuth EKU Signed by the CA (ECDSA with SHA-384) 90 days. The agent requests renewal (new key pair, new CSR over mTLS) when it starts and the certificate expires within 30 days. pki, policyclient
Certificate serial numbers 128-bit random from crypto/rand - - pki
CSR policy The CA rejects RSA keys below 3072 bits and ECDSA curves other than P-256, P-384, P-521; Ed25519 is accepted; the CSR signature is verified. - - pki
ACME certificate (web port only) ECDSA P-256 requested by default; RSA 2048 for clients that do not support ECDSA (behavior of the golang.org/x/crypto/acme/autocert library) Issued by the ACME CA Renewed automatically by the library pki

Revocation. Bor does not publish CRLs or run OCSP. Revoked certificate serial numbers are stored in the revoked_certificates table. Every gRPC call and every new stream on both ports passes an interceptor that requires a verified client certificate (except the two enrollment calls) and rejects the call if the serial number is revoked. Revoke Certificate and Decommission on the Nodes page write that record; see TLS and certificates.

PKCS#11. HSM support for the CA key is compiled only with the pkcs11 build tag (make server-pkcs11, requires CGO). The release packages are built with the plain make server target, so packaged bor-server binaries do not include PKCS#11 support. See Store the CA key in an HSM.

Web authentication and sessions

Item Algorithm and parameters Lifetime Package
Local password hashing PBKDF2-HMAC-SHA256, 600,000 iterations, 16-byte random salt, 32-byte output, constant-time comparison. Format $pbkdf2$sha256$<iter>$<salt>$<key>. - services
Password policy Minimum 12 characters for local accounts. No composition rules, no history, no expiry. LDAP passwords are verified by the directory and follow the directory's policy. - services
Access token JWT, HS256 with jwt_secret (minimum 32 bytes; random value generated by the package on install) security.jwt_lifetime, default 1 hour services
Refresh token JWT, HS256, token_type: refresh. Not stored in the database. security.refresh_lifetime, default 24 hours services
Server-side revocation Per-user cut-off timestamp (tokens_valid_after); tokens issued before it are rejected. Set on logout, password change, administrative password reset and account disable. - services
Sign-in session token (between login steps) JWT, HS256, cannot be used as an access token 5 minutes services
MFA-enrollment-at-sign-in token JWT, HS256 10 minutes services
Step-up token JWT, HS256, audience bor-step-up, bound to one purpose, single use 5 minutes services
Session cookie bor_session HttpOnly, Secure, SameSite=Strict, path / Access-token lifetime api
Refresh cookie bor_refresh HttpOnly, Secure, SameSite=Strict, path /api/v1/auth/refresh Refresh-token lifetime api
CSRF protection Double-submit token: 128-bit random bor_csrf cookie (Secure, SameSite=Strict, readable by JavaScript) must match the X-CSRF-Token header on every POST/PUT/PATCH/DELETE under /api/ except the public sign-in endpoints 24 hours api
Brute-force protection Sign-in, MFA and WebAuthn endpoints: 10 requests per minute per client IP address (taken from the TCP connection), HTTP 429 above that - api
Security headers Content-Security-Policy (default-src 'self', frame-ancestors 'none'), Strict-Transport-Security: max-age=63072000; includeSubDomains, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy - api

Multi-factor authentication

Item Parameters Package
TOTP RFC 6238, HMAC-SHA256 (default) or HMAC-SHA512 (Settings -> MFA Policy), 6 digits, 30-second period, +/-1 step tolerance, 160-bit random secret services
TOTP secret at rest AES-256-GCM, random 96-bit nonce per secret; key derived with HKDF-SHA256 from BOR_MFA_SECRET (environment only), or from jwt_secret when that is unset services
Backup codes 8 codes, each 40 bits from crypto/rand (shown as XXXXX-XXXXX), stored as bcrypt hashes (library default cost 10), single use services
WebAuthn / FIDO2 Enabled only when webauthn.rpid is set. User verification preferred; no attestation requested (the authenticator model is not verified); credential algorithms are the go-webauthn library defaults (ECDSA, RSA and EdDSA COSE algorithms); signature counter stored and checked; ceremony state held server-side for 5 minutes services
Step-up re-authentication Required to reveal a LUKS recovery key: password plus a TOTP code when BOR_ESCROW_REQUIRE_MFA is true (default) services

See Multi-factor authentication for setup.

Enrollment

Item Parameters Package
Enrollment token 256 bits from crypto/rand, hex-encoded; only its SHA-256 hash is stored; valid 5 minutes; consumed by the first enrollment attempt whether it succeeds or not; bound to one node group services
Admin token for the token-creation RPC security.admin_token; compared in constant time; RPC disabled when empty grpc
Kerberos enrollment SPNEGO / Kerberos AP-REQ validated with the server's service keytab (gokrb5 library). Bor does not restrict encryption types: they are those of the keytab and the KDC. Create the keytab with AES encryption types only. services

See Kerberos.

Disk encryption escrow

Item Parameters Package
Recovery key generation (agent) 256 bits from crypto/rand, encoded in the systemd-cryptenroll recovery-key format luks (agent)
Recovery keyslot Added with cryptsetup luksAddKey, PBKDF2-HMAC-SHA512 with 1,000 iterations (the same as systemd-cryptenroll for high-entropy recovery keys) luks (agent)
Per-record data encryption key (DEK) 256-bit random; recovery key encrypted with AES-256-GCM; AAD bor-escrow-v1|<volume_id>|<luks_uuid>|<escrow_id>|<kek_id> binds the ciphertext to its volume and record escrow
Key encryption key (KEK) 32 random bytes in a file (disk_encryption.escrow_kek_file / BOR_ESCROW_KEK_FILE), raw or base64. The server refuses to start if the file is group- or world-readable. DEKs are wrapped with AES-256-GCM, AAD bor-escrow-dek-v1|<kek_id>. escrow
KEK rotation New KEK id (escrow_kek_id, default file-1); old KEKs stay readable via escrow_kek_previous_files; an hourly job re-wraps records under the current KEK in batches of 100 escrow, services
HSM-held KEK Not implemented. escrow_pkcs11_key_label is accepted but only logs a warning; escrow stays disabled unless a KEK file is set. app
Volume encryption Bor does not format volumes. Compliance checks default to aes-xts-plain64 with 512-bit volume keys (AES-256-XTS) and LUKS2. luks (agent)
TPM2 binding Through systemd-cryptenroll; PCR 7 by default, PCRs 7+14 with the Secure Boot + shim profile, or a custom list (never an empty list) luks (agent)
Tang / Clevis Bound with the clevis tools on the endpoint; the server verifies Tang advertisements (ES512 signatures, RFC 7638 SHA-256 key thumbprints) tang, luks (agent)

Details: Disk encryption.

Supply chain and integrity

Item Parameters Where
Agent package repository served at /agent/ apt Release signed (clearsigned InRelease and detached Release.gpg); dnf repomd.xml detached signature; public key served as repo-key.asc. Signing uses the OpenPGP release key held as a CI secret. Repository metadata uses SHA-256 (createrepo_c default, apt-ftparchive). scripts/assemble-agent-repo.sh, CI
Individual package files Not signed individually; integrity comes from the signed repository metadata. The .apk and Arch packages in the repository are direct downloads without a signed index. CI

Other cryptographic uses

Purpose Algorithm Package
All keys, tokens, salts, nonces, serial numbers crypto/rand (the operating system CSPRNG; the Go Cryptographic Module DRBG in FIPS mode) all
Agent tamper detection File watcher compares managed files with the desired content and restores them; no cryptographic signature on policy files agent filewatcher
Audit log integrity No cryptographic protection (no hash chain, no signatures). See Audit logging. -

FIPS 140-3

How the binaries are built

All release packages of bor-server and bor-agent are built by the make server and make agent targets with GOFIPS140=v1.0.0. This tells the Go toolchain to compile in the frozen Go Cryptographic Module v1.0.0 instead of the toolchain's current crypto code. The project's Makefile references CAVP algorithm certificate A6650 for this module. The CMVP module validation status is maintained by NIST; check the NIST CMVP database for the current module certificate before you rely on it in an audit.

Validated module versus approved mode

Term What it means for Bor
Validated module The cryptographic code inside the binaries is the Go Cryptographic Module v1.0.0 snapshot. This is true for every packaged binary, with or without runtime settings. It does not make Bor a validated module.
Approved mode (GODEBUG=fips140=on) At startup the module runs its integrity check and self-tests. crypto/tls then negotiates only FIPS-approved protocol versions, cipher suites, groups and signature algorithms: X25519 and ChaCha20-Poly1305 are no longer used, so key exchange falls back to P-256 or P-384 and TLS 1.3 uses AES-GCM. Random numbers come from the module's DRBG.
Default (no GODEBUG) The module is used, but non-approved choices such as X25519 key exchange remain enabled and preferred.

Algorithms Bor uses that are outside the Go Cryptographic Module or not FIPS-approved, and therefore not covered by the module validation even in approved mode:

  • bcrypt (Blowfish-based) for MFA backup codes.
  • X25519 and ChaCha20-Poly1305 in TLS when FIPS mode is off.
  • Kerberos ticket decryption through the gokrb5 library, which implements its own Kerberos encryption types.
  • WebAuthn credential signatures accepted from security keys (library defaults, including RSA without a minimum key size).
  • LUKS, TPM2 and Clevis operations run by cryptsetup, systemd-cryptenroll and clevis on the endpoint: their FIPS status depends on the endpoint distribution.

Enable approved mode

Set GODEBUG=fips140=on with a systemd drop-in on the server and on every endpoint. The step-by-step procedure, including verification, is in FIPS 140-3 in the hardening guide. In short:

# sudo systemctl edit bor-server   (and: sudo systemctl edit bor-agent)
[Service]
Environment=GODEBUG=fips140=on

Then sudo systemctl restart bor-server (or bor-agent). Certificates you supply yourself (tls.cert_file, ca.cert_file), the LDAP server, the syslog receiver and the PostgreSQL server must also use approved algorithms, or connections to them fail in approved mode.

Standards mapping

Column "Operator action" lists what you must configure; "Gaps" lists what Bor does not do or only partly does.

FIPS 140-3

Requirement How Bor meets it Operator action Gaps and limitations
Use a validated cryptographic module All binaries embed the Go Cryptographic Module v1.0.0 (GOFIPS140=v1.0.0) Verify the module's CMVP status for your audit Bor is not itself a validated module
Use only approved algorithms Generated keys (ECDSA P-256/P-384), AES-GCM, SHA-256/384, HMAC-SHA256/512, PBKDF2, HKDF are approved Set GODEBUG=fips140=on on server and agents bcrypt backup codes, Kerberos (gokrb5) and WebAuthn credential algorithms are outside the module
Approved TLS configuration ECDHE + AES-GCM only on TLS 1.2; approved mode removes X25519 and ChaCha20 As above Without approved mode X25519 is preferred
Key management Keys generated with crypto/rand; private keys written with mode 0600 Protect /var/lib/bor/pki; back up the CA (Backup and restore) No HSM in packaged builds; escrow KEK only file-based

BSI TR-02102-1 and TR-02102-2

Requirement How Bor meets it Operator action Gaps and limitations
TLS 1.2 minimum, TLS 1.3 preferred TLS 1.2 minimum on the web port, TLS 1.3 only on the policy port and for all agent connections Keep browsers current TLS 1.2 still offered on the web port for compatibility
Forward secrecy, AEAD only TLS 1.2 suites are ECDHE with AES-GCM; no CBC, RC4, 3DES, static RSA None TLS 1.3 suite order is chosen by Go and includes ChaCha20-Poly1305, which is not in the TR-02102-2 recommendation; enable FIPS mode to exclude it
Approved curves P-256 and P-384 offered To restrict key exchange to NIST curves, enable FIPS mode X25519 is offered first by default; check the current TR-02102-2 edition for its status in your context
Signature key sizes CA ECDSA P-384, end-entity ECDSA P-256; CSRs below RSA-3072 or below P-256 rejected Supply your own certificates with RSA >= 3072 or ECDSA P-256+ ACME may issue RSA 2048 certificates to clients without ECDSA support
Hashing SHA-256 or stronger everywhere Bor chooses the hash None Legacy unsalted SHA-256 backup-code hashes are still accepted for migration
Symmetric encryption AES-256-GCM for TOTP secrets and escrow None PostgreSQL data at rest is not encrypted by Bor

SOG-IS Agreed Cryptographic Mechanisms

Mechanism class Bor Gaps and limitations
Block cipher and mode AES-128/256 in GCM (TLS, at-rest secrets, escrow) ChaCha20-Poly1305 possible in TLS 1.3 without FIPS mode
Digital signatures ECDSA on NIST P-256 / P-384 Ed25519 accepted in CSRs; RSA 2048 possible from ACME
Key agreement ECDHE on P-256 / P-384 X25519 preferred without FIPS mode
Hash and MAC SHA-256, SHA-384, SHA-512, HMAC-SHA256, HMAC-SHA512 -
Password-based key derivation PBKDF2-HMAC-SHA256, 600,000 iterations bcrypt for backup codes is not an agreed mechanism
Random number generation Operating system CSPRNG via crypto/rand -

eIDAS (EU 910/2014)

Requirement How Bor relates Gaps and limitations
Qualified trust services Bor is not a trust service. Its internal CA is a private PKI for agent authentication only. Certificates issued by Bor are not qualified certificates and must not be used for electronic signatures or seals.
Algorithms for PKI operations CA uses ECDSA P-384 with SHA-384; end-entity keys ECDSA P-256; digitalSignature key usage only; random 128-bit serials No CRL/OCSP; no certificate policy or practice statement
Tamper-evident audit trails Audit log in PostgreSQL plus optional real-time syslog forwarding No hash chain or signatures; tamper evidence depends on forwarding to a write-once SIEM
Time stamping Audit timestamps from the server clock No qualified time stamps; keep the server clock synchronized (NTP)

NIS2 Directive, Article 21 measures

Art. 21(2) measure How Bor supports it Operator action Gaps and limitations
(b) Incident handling Audit log of sign-ins, failures, permission denials, changes, tamper detections and recovery-key reveals; CEF / OCSF syslog export Forward to a SIEM (Audit log) Successful requests only for generic API changes; failed changes appear only as permission denials or not at all
(c) Business continuity Documented backup and restore Follow Backup and restore No built-in high availability or automated backups
(d) Supply chain security Signed agent repositories, pinned Go module versions (go.sum), weekly Dependabot updates for Go, npm and GitHub Actions Verify the repository signing key on endpoints No SBOM, no build provenance attestation, no signatures on individual packages
(e) Vulnerability handling and disclosure Private disclosure channel (see Vulnerability reporting) Subscribe to GitHub releases No published CVE / advisory feed
(h) Cryptography and encryption See Cryptographic inventory Enable FIPS mode if required; use TLS for PostgreSQL and LDAP Database at-rest encryption is the operator's responsibility
(i) Access control and asset management RBAC with granular permissions; node inventory; certificate-based agent identity; revocation Assign least-privilege roles (Permissions) Node-group-scoped roles are not available in the community edition
(j) Multi-factor authentication TOTP and WebAuthn, global enforcement, step-up for recovery-key reveals Turn on Require MFA for all users (Hardening guide) MFA is off by default; bor-server --reset-mfa bypasses the audit log

EU Cybersecurity Act / EUCC

Topic Bor Gaps and limitations
Certification None. Bor is not EUCC or Common Criteria certified. -
Secure development Source is public; CI runs unit tests and golangci-lint on server and agent before packages are built; dependency updates via Dependabot No published threat model or formal security target
Defense in depth Separate ports with different TLS policies; mTLS for agents; sandboxed server unit; CSRF, CSP and rate limiting on the web UI -
Least privilege Server runs as bor with only CAP_NET_BIND_SERVICE; RBAC on the UI Agent runs as root by necessity
Input validation CSR key strength and signature checks; server-assigned certificate identity; typed policy schemas -

GDPR

Principle / right How Bor supports it Operator action Gaps and limitations
Data minimization (Art. 5(1)(c)) Optional truncation of client IP addresses in audit records to /24 (IPv4) or /48 (IPv6) Set BOR_AUDIT_ANONYMIZE_IPS=true in the environment (the audit.anonymize_ips YAML key is not read) Sign-in and logout audit events always store the full IP address; node inventory stores endpoint IP addresses; tamper events store local usernames of processes
Storage limitation (Art. 5(1)(e)) Audit retention, default 365 days, purged at startup and daily; retired recovery keys kept 30 days, orphaned 90 days Set audit.retention_days / BOR_AUDIT_RETENTION_DAYS to your policy Data forwarded to a SIEM follows the SIEM's retention
Right of access / portability (Art. 15, 20) GET /api/v1/users/me/export returns the signed-in user's account, MFA status and security keys as JSON None Audit records about the user are not included
Right to erasure (Art. 17) Deleting a user removes the account, MFA data, security keys and role assignments Delete the user; purge or pseudonymize audit entries in the database if required Audit entries keep the username (only the user ID link is cleared); no built-in pseudonymization of audit history
Transparency Privacy policy link in the UI (ui.privacy_policy_url) Publish and link your privacy notice -
Security of processing (Art. 32) TLS in transit, hashed passwords, encrypted TOTP secrets and escrowed keys Encrypt the database volume and backups; restrict database access PostgreSQL contents are not encrypted by Bor

Data protection

What is stored where

Data Location Protection at rest
User accounts: username, email, full name, source (local / LDAP), enabled flag PostgreSQL users Plaintext (personal data)
Local password hashes users.password_hash PBKDF2-HMAC-SHA256
TOTP secrets user_mfa AES-256-GCM
MFA backup codes user_mfa bcrypt hashes
WebAuthn credentials (public keys, AAGUID, counters, names) user_webauthn_credentials, webauthn_sessions Public data, plaintext
Roles, groups, bindings roles, user_groups, user_role_bindings and related tables Plaintext
Nodes: name, FQDN, machine ID, IP address, OS, desktop, agent version, certificate serial and expiry nodes Plaintext
Enrollment tokens enrollment_tokens SHA-256 hash only
Revoked certificate serials revoked_certificates Plaintext
Policies and bindings policies, policy_bindings Plaintext; delivered to every agent in the bound groups. Do not put secrets in policies.
Compliance results compliance_results Plaintext
Audit log: time, user ID, username, action, resource, details (redacted request body), IP address audit_logs Plaintext
LUKS volume inventory luks_volumes, node_disk_encryption Plaintext
Escrowed LUKS recovery keys luks_recovery_keys AES-256-GCM under a per-record DEK, DEK wrapped by the KEK
Internal CA and server key /var/lib/bor/pki PEM files, mode 0600, owned by bor (or the CA key in an HSM with a custom build)
ACME account key and certificates /var/lib/bor/acme Files owned by bor
Configuration secrets: jwt_secret, BOR_MFA_SECRET, database password, LDAP bind password, admin_token, metrics token, PKCS#11 PIN /etc/bor/server.yaml or /etc/bor/server.env Plaintext, protected by file permissions (root:bor 0640)
Escrow KEK File named by escrow_kek_file Raw key, mode 0600 enforced
Initial admin password security.admin_password in server.yaml, or /var/lib/bor/initial_admin_password Plaintext until you remove it (Hardening guide)
Agent key, certificate, CA certificate /var/lib/bor/agent on the endpoint agent.key mode 0600, root-owned

Data in transit

All browser, agent, LDAP (when TLS is configured), Tang (HTTPS), Flatpak and SIEM (tcp+tls) traffic can be protected with TLS as described above. Plaintext options exist and are the operator's choice: LDAP without TLS, syslog over udp or tcp, the metrics endpoint without TLS, and PostgreSQL over TCP with sslmode: disable. For PostgreSQL over TCP use sslmode: verify-full and provide the server's CA with the standard libpq PGSSLROOTCERT environment variable in /etc/bor/server.env.

What is never logged

  • Audit records: request bodies are stored only after every field whose name contains password, passwd, secret, token, credential, passphrase, private_key, privatekey, api_key, apikey, jwt, auth_token, access_token, refresh_token or recovery_key is replaced by [REDACTED], recursively. Recovery-key reveals bypass request-body capture.
  • Enrollment tokens are stored only as SHA-256 hashes.
  • Recovery keys are never written to the agent's state file or logs; only rotation progress is persisted.

Initial admin password

On a fresh install the package prints the generated admin password to the console and writes it to server.yaml. Remove it after the first login as described in the Hardening guide.

Audit logging for compliance

Aspect Detail
Events Successful (2xx) POST/PUT/PATCH/DELETE API requests; successful and failed sign-ins, logouts, MFA enrollment and removal, step-up; permission denials (access_denied); role removals; agent tamper detections (tamper_detected); policy export and import; recovery-key escrow, reveal, refused reveal, rotation and destruction (recoverykey.*, luks.*)
Fields Event time, actor (user ID and username, or node for agent events), action, resource type and ID, outcome, source IP address, details (redacted request body, tamper file path and processes with PID, command and user)
Storage PostgreSQL audit_logs
Viewing and export Audit Logs page (audit_log:view); Export CSV / Export JSON (audit_log:export)
Real-time forwarding RFC 5424 syslog over UDP, TCP or TCP+TLS (TLS 1.2+), payload in ArcSight CEF (CEF:0) or OCSF JSON
Retention audit.retention_days (default 365, 0 disables purging)
Integrity None built in. Anyone with write access to the database can change or delete records, and retention purges them. For tamper evidence, forward events in real time to a SIEM or write-once store.

Not recorded in the audit log: read-only (GET) requests other than those listed, failed (non-2xx) change requests other than permission denials, and bor-server --reset-mfa (it runs outside the server process).

Usage, filters, retention and syslog configuration: Audit log.

Supply chain

Topic Status
Source Public on GitHub (https://github.com/VuteTech/Bor). Licensed LGPL-3.0-or-later.
Build GitHub Actions builds deb, rpm, apk and Arch packages for amd64, arm64 and ppc64le with nfpm (pinned version) after unit tests and lint pass. Go toolchain version comes from go.mod; Go dependencies are pinned by go.sum. Builds are not bit-for-bit reproducible and no build provenance is published.
Package signing The agent repository each server hosts is signed with the project's OpenPGP release key when the CI secret is available; unsigned builds are flagged in the Deploy agent dialog, which then falls back to unsigned repository settings. Individual package files are not signed.
Dependency updates Dependabot checks Go modules (server and agent), npm (frontend) and GitHub Actions weekly. GitHub Actions are referenced by version tag, not by commit hash.
SBOM Not currently published.
Vulnerability scanning No dedicated scanner (such as govulncheck) runs in CI.

Vulnerability reporting

Report security issues privately by email to bor@vute.tech, not as public GitHub issues. Include a description, steps to reproduce and any suggested mitigation. The project commits to acknowledge reports within 48 hours and to aim for a fix within 90 days, coordinating disclosure with the reporter.

Known limitations

These are properties of the current code that you should account for in a risk assessment.

Area Limitation Mitigation
Agent certificate renewal The agent checks its certificate only when the service starts. An agent that runs for more than 60 days without a restart lets its certificate expire, after which the node must be re-enrolled. Restart agents or reboot endpoints regularly; alert on bor_node_certificate_expiry_seconds (Monitoring).
Server certificate renewal The internally issued server certificate (1 year) is regenerated only at server start. Restart the server at least yearly, or use your own certificate or ACME for the web port.
Revocation of open streams Revocation is checked when a call or stream starts. A policy stream that is already open is not closed when its certificate is revoked. Restart the server or wait for the agent to reconnect.
Renewed certificates On renewal the previous certificate is not revoked and stays valid until it expires (up to 30 days). Revoking a node from the UI revokes only the current serial, and a successful renewal clears the node's revocation records. After revoking a node that renewed recently, Decommission it instead, which deletes the node record so its other certificates can no longer renew or stream.
Kerberos enrollment The node name sent by the agent is not checked against the Kerberos principal, and the principal's realm is not compared with kerberos.realm. Any principal that can obtain a ticket for the service can enroll a new node into the default node group. Restrict which principals can get service tickets; review new nodes; use token enrollment where this matters.
Audit source IP The audit log takes the client IP from X-Forwarded-For or X-Real-IP when present, without checking for a trusted proxy, so a client can set it. Rate limiting uses the TCP peer address and is not affected. Treat audit IP addresses as informational, or strip these headers at a trusted reverse proxy.
IP anonymization coverage BOR_AUDIT_ANONYMIZE_IPS does not apply to sign-in and logout events. Account for this in your records of processing.
Audit log integrity No hash chain or signatures. Forward events to a SIEM in real time.
Backup codes Hashed with bcrypt at cost 10 (not a FIPS-approved algorithm); 40 bits of entropy per code. Rate limiting applies to the sign-in endpoints.
BOR_MFA_SECRET No minimum length is enforced; when unset, jwt_secret is used, so changing jwt_secret breaks existing TOTP enrollments. Set a dedicated 32-byte random value before users enroll.
WebAuthn No attestation, user verification only preferred. Combine with a password (always required) and enforce MFA.
CSRF and metrics token comparison Compared with ordinary string comparison rather than constant-time comparison. Low risk; keep the metrics listener on loopback.
Rate limiting behind a proxy All clients behind a reverse proxy share the proxy's address for rate limiting. Prefer direct TLS to the web port.
PKCS#11 Not included in packaged binaries; escrow KEK in an HSM not implemented. Build with make server-pkcs11 if you need an HSM for the CA.
Database at rest Bor does not encrypt PostgreSQL data or backups. Use encrypted storage and encrypted backups.
MFA reset bor-server --reset-mfa is not written to the audit log. Record its use in your change log.