🌐 getbor.dev · 📦 GitHub · 📖 Contributing · 🔒 Security
Bor is an open-source policy management system for Linux desktops. A central server distributes configuration policies to enrolled Linux endpoints in real-time. A lightweight Go agent daemon on each machine receives policies over an encrypted gRPC stream and enforces them locally.
Currently enforced policies:
- Firefox ESR — system-wide
policies.json(RPM/DEB and Flatpak) - Google Chrome / Chromium — managed JSON in
/etc/opt/chrome/and/etc/chromium/(including Flatpak) - KDE Plasma — KDE Kiosk (
kconfigfiles under/etc/xdg/, KCM module restrictions)
┌─────────────────────┐ gRPC stream / mTLS ┌──────────────────────┐
│ Bor Agent │ ◄────── port 8444 ────────► │ Bor Server │
│ (Go daemon, root) │ │ Go + PatternFly UI │
└─────────────────────┘ gRPC enrollment / TLS └──────────┬───────────┘
• One-time token enrollment ◄── port 8443 ──► │ SQL
• mTLS client certificate auth ▼
• Applies Firefox / Chrome / ┌──────────────────────────────┐
KDE Kiosk policies │ PostgreSQL │
• Reports compliance │ policies · nodes · users │
• Desktop notifications │ bindings · RBAC · audit log │
└──────────────────────────────┘
The server runs two HTTPS listeners with different security postures:
| Port | Purpose | TLS | Client cert |
|---|---|---|---|
| 8443 | Admin UI (REST) + enrollment gRPC | TLS 1.2+ | Optional |
| 8444 | Agent policy stream + cert renewal | TLS 1.3, mTLS | Mandatory |
REST and enrollment gRPC share port 8443, routed by Content-Type:
application/grpc* → gRPC handler, everything else → REST API + embedded
PatternFly web UI.
- Centralised web UI — PatternFly 5 dashboard for policies, nodes, and bindings
- Real-time distribution — gRPC server-side streaming; agents receive changes instantly
- Secure by design — mTLS certificate authentication for every agent; internal CA auto-generated on first run; FIPS 140-3 validated crypto module
- Easy enrollment — one-time token generated in the UI; agent bootstraps its own certificate
- Automatic renewal — agent certificates (90-day) are renewed automatically before expiry; no operator action required
- Delta sync — monotonic revision counter with ring buffer; reconnecting agents receive only what changed
- Node Groups — many-to-many assignment of nodes to policy groups
- RBAC — built-in roles (Super Admin, Org Admin, Policy Editor, …) with granular per-resource permissions
- Audit log — every API action recorded with user, IP, and timestamp
- Tamper protection — file watcher detects and restores managed files that are modified outside Bor
- LDAP/AD integration — optional, enabled via environment variables
- HSM support — optional PKCS#11 integration stores the CA private key in a hardware security module
- Linux-native packaging — deb, rpm, apk, Arch Linux packages via nfpm
| Component | Requirement |
|---|---|
| Server host | Podman / Docker + podman-compose / docker-compose |
| Agent host | Linux x86_64 or arm64 |
| Building from source | Go 1.24+, Node.js 18+, Make |
git clone https://github.com/VuteTech/Bor.git
cd Bor
cp .env.example .env
$EDITOR .env # set DB_PASSWORD, JWT_SECRET, BOR_ADMIN_PASSWORD at minimum
podman-compose up -dThe server starts on https://localhost:8443 (UI + enrollment) and
https://localhost:8444 (agent mTLS). On first boot it auto-generates an
internal CA (ECDSA P-384) and a server TLS certificate (ECDSA P-256) under
/var/lib/bor/pki/.
Log in at https://localhost:8443 with the admin credentials set in .env.
Download the appropriate package for your distribution from the releases page and install it:
# Debian / Ubuntu
sudo dpkg -i bor-agent_<version>_amd64.deb
# Fedora / RHEL
sudo rpm -i bor-agent-<version>.x86_64.rpm
# Arch Linux
sudo pacman -U bor-agent-<version>-x86_64.pkg.tar.zst-
Edit
/etc/bor/config.yaml(installed by the package):server: address: "your-server" enrollment_port: 8443 policy_port: 8444 insecure_skip_verify: true # set false after deploying a trusted cert
-
Generate an enrollment token in the web UI (Node Groups page).
-
Enroll the agent:
sudo bor-agent --token <ENROLLMENT_TOKEN>
The agent generates an ECDSA P-256 key pair, sends a CSR to the server, and stores the signed certificate, private key, and CA cert in
/var/lib/bor/agent/. After enrollment, start it as a service:sudo systemctl enable --now bor-agent -
To re-enroll (e.g. after a CA rotation or to move a node to a different group), pass a new token — the old certificates are removed automatically before re-enrollment begins:
sudo bor-agent --token <NEW_TOKEN>
All build targets are in the Makefile. Run make help to list them.
make install-depsThis installs protoc-gen-go, protoc-gen-go-grpc, golangci-lint, nfpm,
and the TypeScript proto plugin via npm. You must have protoc and Node.js
available separately.
All Go builds use GOFIPS140=v1.0.0, which embeds the FIPS 140-3 validated
crypto module snapshot (CAVP certificate A6650) regardless of the host
toolchain.
make server # → server/server (FIPS 140-3)
make agent # → agent/bor-agent (FIPS 140-3)
make frontend # → embedded into the server binary (run before make server)
make proto # regenerate Go + TypeScript from proto/ definitionsBuild everything in the correct order:
make frontend && make server && make agentTo store the CA private key in a hardware security module, build with the
pkcs11 tag. This requires CGO and the crypto11 dependency:
cd server && go get github.com/ThalesIgnite/crypto11
cd ..
make server-pkcs11 # → server/server (FIPS 140-3 + PKCS#11)See docs/SECURITY.md for the full configuration guide and a SoftHSMv2 example.
The build flag embeds the validated module. To additionally restrict the running process to FIPS-approved algorithms only (removes X25519, enforces P-256/P-384):
GODEBUG=fips140=on ./server/serverAdd Environment=GODEBUG=fips140=on to the systemd unit override for
production deployments.
# Start PostgreSQL
make dev
# Run the server (reads .env automatically via go run)
make run-server
# In a separate terminal — run the agent against the local server
sudo ./agent/bor-agent --config agent/config.yaml.exampleBuild distribution packages for all formats:
make packages # deb + rpm + apk + archlinux, version 0.1.0
make packages VERSION=1.2.3 # specify version
make packages VERSION=1.2.3 ARCH=arm64 # cross-arch
make packages-agent VERSION=1.2.3 # agent packages only
make packages-server VERSION=1.2.3 # server packages onlyPackages are written to builds/. Requires nfpm (make install-deps).
make docker # builds bor-server:latest with podmanmake test # all tests (server + agent)
make test-server # server tests only
make test-agent # agent tests only
make coverage # HTML coverage reports → server/coverage.html, agent/coverage.htmlRun a single test by name:
cd server && go test -v -run TestPolicyService ./internal/services/...
cd agent && go test -v -run TestMergeKConfig ./internal/policy/...The server reads configuration from a YAML file (default /etc/bor/server.yaml,
override with BOR_CONFIG) and environment variables. Environment variables
always take precedence. Copy .env.example to .env for local development,
or use /etc/bor/server.env for production (loaded by the systemd unit).
A full annotated YAML reference is in server/server.yaml.example.
| Variable | Description |
|---|---|
DB_PASSWORD |
PostgreSQL password |
JWT_SECRET |
JWT signing secret — use at least 32 random bytes |
BOR_ADMIN_PASSWORD |
Initial admin password (applied only when no users exist) |
| Variable | Default | Description |
|---|---|---|
BOR_ADDRESS |
(all interfaces) | Server hostname or IP (no port) |
BOR_ENROLLMENT_PORT |
8443 |
UI + enrollment gRPC listen port |
BOR_POLICY_PORT |
8444 |
Agent mTLS policy stream listen port |
BOR_HOSTNAMES |
— | Comma-separated extra SANs for the auto-generated TLS cert |
| Variable | Default | Description |
|---|---|---|
DB_HOST |
localhost |
PostgreSQL host |
DB_PORT |
5432 |
PostgreSQL port |
DB_USER |
bor |
PostgreSQL user |
DB_NAME |
bor |
PostgreSQL database name |
DB_SSLMODE |
disable |
PostgreSQL SSL mode (disable, require, verify-full) |
If the CA and TLS variables are not set, the server auto-generates an internal
CA (ECDSA P-384, 10 years) and a server TLS certificate (ECDSA P-256, 365 days)
under /var/lib/bor/pki/ and reuses them on subsequent starts.
| Variable | Default | Description |
|---|---|---|
BOR_CA_CERT_FILE |
auto | Path to CA certificate PEM |
BOR_CA_KEY_FILE |
auto | Path to CA private key PEM (unused when PKCS#11 is set) |
BOR_CA_AUTOGEN_DIR |
/var/lib/bor/pki/ca |
Directory for auto-generated CA files |
BOR_TLS_CERT_FILE |
auto | Path to server TLS certificate PEM |
BOR_TLS_KEY_FILE |
auto | Path to server TLS private key PEM |
BOR_TLS_AUTOGEN_DIR |
/var/lib/bor/pki/ui |
Directory for auto-generated TLS cert |
Requires the server-pkcs11 binary. The PIN must come from an environment
variable — never store it in a YAML file.
| Variable | Description |
|---|---|
BOR_CA_PKCS11_LIB |
Path to PKCS#11 shared library (.so) |
BOR_CA_PKCS11_TOKEN_LABEL |
HSM token label |
BOR_CA_PKCS11_KEY_LABEL |
CA private key label on the token |
BOR_CA_PKCS11_PIN |
Token PIN |
| Variable | Default | Description |
|---|---|---|
BOR_ADMIN_TOKEN |
— | Static token for gRPC enrollment calls (optional) |
LDAP_ENABLED |
false |
Enable LDAP authentication |
LDAP_HOST |
localhost |
LDAP server hostname |
LDAP_PORT |
389 |
LDAP server port |
LDAP_USE_TLS |
false |
Use TLS for the LDAP connection |
LDAP_BIND_DN |
— | LDAP bind distinguished name |
LDAP_BIND_PASSWORD |
— | LDAP bind password |
LDAP_BASE_DN |
— | LDAP search base DN |
The agent reads /etc/bor/config.yaml by default. Pass --config <path> to
override. A full annotated reference is in agent/config.yaml.example.
server:
address: "bor-server.example.com"
enrollment_port: 8443 # UI + enrollment gRPC
policy_port: 8444 # mTLS policy stream
insecure_skip_verify: false # true only for self-signed certs during setup
agent:
client_id: "" # defaults to system hostname
enrollment:
data_dir: "/var/lib/bor/agent" # cert, key, and CA cert stored here
firefox:
policies_path: "/etc/firefox/policies/policies.json"
flatpak_policies_path: "/var/lib/flatpak/extension/org.mozilla.firefox.systemconfig/x86_64/stable/policies/policies.json"
chrome:
chrome_policies_path: "/etc/opt/chrome/policies/managed"
chromium_policies_path: "/etc/chromium/policies/managed"
chromium_browser_policies_path: "/etc/chromium-browser/policies/managed"
flatpak_chromium_policies_path: "" # set to enable Flatpak Chromium
kconfig:
config_path: "/etc/xdg" # KDE Kiosk overlay directory- Security — cryptographic algorithms, FIPS 140-3 compliance, EU standards, deployment checklist, HSM integration
- Architecture — detailed design and data flows
- Contributing — setup, coding standards, PR process
Contributions are welcome via GitHub pull requests or by emailing patches to [email protected].
See docs/CONTRIBUTING.md for the full guide.
- Core policy management API and web UI
- Dual-port architecture (8443 UI/enrollment, 8444 agent mTLS)
- gRPC streaming with mTLS (TLS 1.3, mandatory client cert)
- Token-based agent enrollment (one-time, 5-minute TTL)
- Automatic certificate renewal (ECDSA P-256, 90-day lifetime)
- FIPS 140-3 validated builds (
GOFIPS140=v1.0.0, CAVP A6650) - PKCS#11 HSM support for CA private key (
-tags pkcs11) - Firefox ESR policy enforcement (RPM/DEB + Flatpak)
- Chrome / Chromium policy enforcement (including Flatpak)
- KDE Plasma KConfig (Kiosk) enforcement + KCM module restrictions
- Tamper protection (file watcher restores managed files)
- RBAC with roles and permissions
- Audit log
- Desktop notifications on policy change
- Many-to-many node group membership
- LDAP / Active Directory authentication
- deb / rpm / apk / Arch Linux packaging
- Persistent compliance reporting (database storage)
- Prometheus metrics endpoint
- AD / FreeIPA LDAP enrollment (Kerberos)
- Additional policy types: dconf, systemd units, firewalld, polkit, packages
- Agent auto-update mechanism
- Multi-tenancy
Copyright (C) 2026 Vute Tech LTD Copyright (C) 2026 Bor contributors
Licensed under the GNU Lesser General Public License v3.0.