Security operations is the day-to-day side of keeping a Binions host safe: what is exposed, what is locked down, and which knobs you turn. This page is the operator’s view — the live network posture, the firewall, where secrets live, how licence enforcement behaves at runtime, and outbound safety. The deeper reasoning — the threat model, the systemd sandbox, and how licences are signed and checked — lives in the Security section; this page links there rather than repeating it.
Secure by default. A fresh Binions install ships closed: a default-drop firewall, every health and metrics endpoint bound to loopback, and secrets in owner-only files. You do not have to harden it after the fact — you mostly just avoid loosening it.
Binions is a single-host platform, and almost everything it runs is meant to be reached only by the host itself or by you on your own network. The one deliberately public surface is the Traefik web edge. The table below is the mental model to keep: three concentric rings, narrowing from the public internet down to the host’s own loopback interface.
| Exposure ring | What lives here | Who can reach it |
|---|---|---|
| Public | The Traefik HTTP/HTTPS edge — the only intentionally internet-facing entry point | Anyone, over HTTP/HTTPS |
| LAN / cluster-only | MinIO S3 object storage, the Jaeger tracing UI, the admin UIs (Traefik dashboard, MinIO console, Showman hub) and SSH | Hosts on your LAN/cluster only |
| Loopback-only | Every daemon’s /health and /metrics, the Traefik admin API, and the internal MinIO console | The host itself — nothing off-box |
The boundary between these rings is enforced by a single firewall that drops anything not explicitly allowed. So the rings are not a convention you have to remember — they are how packets actually flow on the host.
Good to know. Health and metrics endpoints are bound to the loopback interface, not just firewalled. Even if a firewall rule were removed by mistake, those endpoints still would not answer from another machine — the daemons never listen on an external address in the first place.
The firewall is an nftables ruleset with a default-drop policy: traffic that does not match an explicit allow rule is silently dropped, both inbound and for anything the platform did not ask for. The whole policy lives in one file, so there is a single place to read and reason about what is open.
To inspect the live ruleset on the host, list the active nftables configuration:
# Show the entire live ruleset (chains, policies, and counters)
sudo nft list ruleset
# Show just the inbound (input) chain and its default policy
sudo nft list chain inet filter input
Reading the output, you are looking for two things. First, the policy on the input chain should be drop — that is the safety net behind every individual rule. Second, the accept rules should be the short, deliberate list you expect: the Traefik web ports open to the world, the LAN-scoped services (object storage, tracing UI, admin UIs, SSH) restricted to your own network, and nothing accepting traffic to the loopback-only ports from outside.
The design intent is simple: open the minimum, drop the rest. Public exposure is reserved for the one service that needs it; everything operational is kept on the LAN or the host. If you ever need a daemon’s endpoint reachable from another machine, the right move is an explicit, scoped firewall rule and a conscious decision — not turning off the default-drop policy.
Do not flush the ruleset. A command such as
nft flush rulesetremoves the default-drop policy and every allow rule at once, briefly leaving the host wide open until the firewall is reloaded. Edit the policy file and reload it deliberately instead of clearing rules live.
Binions keeps credentials out of its configuration files entirely. Nothing sensitive is ever inlined into a TOML or YAML file — secrets live in dedicated files with tight ownership and permissions, and the config merely points at them.
There are two homes for secrets, with a clear division of labour:
0600, owned by the daemon’s dedicated service user. Only that one service user can read them.On disk that looks like this:
# A daemon's own credential files: owner-read/write only, owned by its service user
$ ls -l /opt/binions/database/secrets/
-rw------- 1 databasesvc databasesvc 45 postgres.password
-rw------- 1 databasesvc databasesvc 45 redis.password
# Host-wide config, licence files and TLS material
$ ls /etc/binions/
master.toml
secrets/ # TLS certificate + key for the web edge
*.license # one signed licence file per binion
Rotating a secret is deliberately boring: replace the file with the new value (keeping the same owner and 0600 permissions) and restart the daemon that uses it so it re-reads the file. No central vault to coordinate, no config edit, no inline change to leak into a backup or a log.
# Rotate a daemon's password: replace the file, then restart that daemon
sudo install -o databasesvc -g databasesvc -m 0600 \
new-redis.password /opt/binions/database/secrets/redis.password
sudo systemctl restart binions-database.service
The full reasoning — how the ownership model maps to the systemd sandbox, and how TLS material is provisioned — is covered in Secrets management and TLS certificates.
Binions licences a complete set — the whole group of binions on one host. A paid set is £1 per binion, a one-time £13 per host; running one set on one host for non-commercial use is free. The operational question on this page is not what a licence costs but how the host behaves around it at runtime.
Four mechanisms do the work, and all of them are visible to an operator:
BINIONS_LICENSE_ENFORCE, controls how strict the platform is at start-up. By default enforcement is permissive — a daemon with a missing or invalid licence logs a warning and boots anyway, so you can evaluate freely. Set BINIONS_LICENSE_ENFORCE to enable strict mode, where daemons refuse to boot without a valid licence.To see the enforcement mode and CRL refresh on a host, inspect the environment the daemons run with and the refresh timer:
# Is strict licence enforcement on for this host?
systemctl show binions-logger.service -p Environment | grep -o 'BINIONS_LICENSE_ENFORCE=[^ ]*'
# When does the revocation list refresh next, and did the last run succeed?
systemctl list-timers 'binions-crl-refresh*'
journalctl -u binions-crl-refresh.service -n 20 --no-pager
Before production, decide on enforcement. The permissive default is for evaluation. When you put a host into real service, set
BINIONS_LICENSE_ENFORCEon so a licence problem fails loudly at boot instead of slipping by as a warning. The mechanics are detailed in Licence enforcement.
Inbound exposure is only half of a host’s attack surface. Because the webhookcaller daemon makes outbound HTTP requests on behalf of your playbooks, it is fitted with an SSRF allow-list — a list of destinations it is permitted to call. Anything not on the list is refused, which stops a crafted event or a mistyped playbook from making the host call arbitrary internal addresses (a cloud metadata endpoint, another box on your LAN, a loopback admin port).
The important operational fact is the default: an empty allow-list means deny-all. Out of the box, webhookcaller will not call anywhere until you add the hosts your workflows legitimately need. That is the safe direction — you opt destinations in, rather than discovering after the fact that the daemon could reach somewhere it should not. See the webhookcaller service for how to populate the allow-list, and Webhooks & APIs for using it in a playbook.
Pulling these threads together: Binions expects to be administered from the host itself or from your own network, never from the public internet. The admin UIs — the Traefik dashboard, the MinIO console, the Showman hub — and SSH are scoped to the LAN. The health, metrics, and internal admin endpoints sit on loopback. The only thing the wider world is meant to reach is the application traffic served through the Traefik edge.
In practice that means: reach the admin surfaces over your LAN (a VPN or SSH tunnel if you are remote), keep the default-drop firewall intact, and resist the temptation to publish an admin port “just to check something” from outside. Every operational control on this page is built on that assumption, and the platform is at its safest when you leave it that way.
The short version: public is the web edge only; the LAN carries storage, tracing, the admin UIs and SSH; loopback carries health, metrics and internal admin. Secrets live in owner-only files and are never inlined. For production, turn enforcement on, and leave outbound allow-lists empty until you deliberately fill them.
Three of the admin surfaces — the Traefik dashboard (:8444), the Jaeger tracing UI (:8443), and the Showman hub (:8446) — sit behind a single HTTP Basic authentication gate at the edge. They share one account: the user admin with one password. Sign in once and the same pair works across all three.
The credential is never inlined in a config file. Traefik reads it from a password file that the edge watches, so a change takes effect immediately, with no restart:
| File | Contents | Permissions |
|---|---|---|
/etc/binions/secrets/traefik/admin-uis.htpasswd | The hashed credential Traefik reads (bcrypt) | 0640 — edge-readable |
/etc/binions/secrets/admin-credentials.txt | A plaintext copy, so you can look up the current password | 0600 — root only |
To set a password of your own, pass it to the credential helper in the environment and use --force to replace any existing one. The helper rewrites both files (the hash Traefik reads and the plaintext copy) together, so they never drift apart, and the login name stays admin:
sudo env BINIONS_ADMIN_PASSWORD='your-strong-password' \
binions-admin-htpasswd generate --force
You can confirm the two files still agree at any time with sudo binions-admin-htpasswd verify.
Change the default before you expose the host. So the dashboards work the moment you install, the admin account ships with a well-known default password. Treat it as a placeholder: set your own with the command above before the host reaches any network beyond the machine itself, and rotate it whenever you suspect it has been shared.
The MinIO console is the exception. The object-store console (
:8445) does not use this shared gate — it has its own sign-in backed by MinIO’s own root credentials, kept separately. Changing theadminpassword above does not affect it. For the full map of edge entry points, see Network requirements.