Zones, address sets (IPv4 and IPv6 mixed, dynamic sets with timeouts) and services, ordered rules in the early, input, forward and output chains, port forwards with hairpin NAT and IPv6 pinholes, source NAT, redirects, and switchable built-ins (loopback, conntrack, ICMPv6, antispoofing, MSS clamping, DHCPv6 client, flowtable). Every change is validated, checked with nft -c and applied in one transaction to Housecarl's own table, inet housecarl; changes made over the network roll back unless confirmed, and the confirmed ruleset is loaded at boot by housecarl-ruleset.service. An empty configuration applies nothing. Also: hit counters and the drop log (nflog), 'hc import' of an existing nftables ruleset and 'hc takeover', the hc CLI, the REST API and the web UI, users, scoped tokens (sets:write:NAME), the audit log, the web UI's certificate through Jarl, Debian packages, docs, unit tests with real nft 1.1.3, and an e2e test with real traffic (71 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
194 lines
8.2 KiB
Markdown
194 lines
8.2 KiB
Markdown
# The hc CLI
|
|
|
|
`hc` talks to housecarld's REST API. `hc help COMMAND` describes each
|
|
command and its flags.
|
|
|
|
## Connecting
|
|
|
|
* **On the firewall**, with nothing configured, the CLI uses
|
|
housecarld's local socket, `/run/housecarl/housecarl.sock`. Root and
|
|
the `housecarl` user are administrators there without a token, and
|
|
their changes need no confirmation: `sudo hc status`.
|
|
* **From elsewhere**, give the server's URL and an API token:
|
|
|
|
```sh
|
|
hc config set server https://fw.example.com:8070
|
|
hc config set token # asks for it
|
|
```
|
|
|
|
The settings are in `~/.config/housecarl/config.yaml` (on macOS
|
|
`~/Library/Application Support/housecarl/config.yaml`, on Windows
|
|
`%AppData%\housecarl\config.yaml`).
|
|
|
|
| Flag | Environment | |
|
|
|---|---|---|
|
|
| `-s, --server URL` | `HOUSECARL_SERVER` | the server; `local` for the local socket |
|
|
| `--token TOKEN` or `@FILE` | `HOUSECARL_TOKEN` | the API token |
|
|
| `--config FILE` | `HOUSECARL_CLI_CONFIG` | the CLI's configuration file |
|
|
| | `HOUSECARL_SOCKET` | the local socket's path |
|
|
| `--insecure`, `--ca-cert FILE` | | TLS: skip the check, or trust another CA |
|
|
| `-n, --dry-run` | | check and show a change without making it |
|
|
| `-o, --output FORMAT` | | `table` (default), `json`, `yaml` or `id` |
|
|
| `-q, --quiet` | | print only errors (and the IDs of new objects) |
|
|
|
|
Exit codes: 0 ok, 1 error, 3 not authenticated or not allowed, 4 not
|
|
found, 5 conflict, 6 invalid or refused by validation or nftables, 7
|
|
server error.
|
|
|
|
## Changes
|
|
|
|
Every change is checked as a whole, then by nftables, before it is put
|
|
in force. A refused change says why, with the problem's code
|
|
([validation.md](validation.md)). An applied one says its history entry
|
|
and any new warnings. Over the network, it is on probation:
|
|
|
|
```
|
|
$ hc rule add input --from trusted --service ssh --comment "SSH from home"
|
|
created rule input #31 (from trusted · ssh)
|
|
applied on probation (history 12): confirm with 'hc confirm' within 2m0s, or it is rolled back
|
|
$ hc confirm
|
|
confirmed 1 change; it is kept and loaded at boot
|
|
```
|
|
|
|
Through the socket the change is kept at once:
|
|
|
|
```
|
|
applied and saved (history 12; undo with 'hc history undo 12')
|
|
```
|
|
|
|
`-n` shows what would change without changing it; nftables checks it
|
|
too.
|
|
|
|
## Commands
|
|
|
|
Objects are named by ID or, for zones, sets, services and port forwards,
|
|
by name.
|
|
|
|
### status and the ruleset
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `status` | the ruleset (in force or not), changes on probation, problems, interfaces, other tables |
|
|
| `interfaces` | the machine's interfaces, their addresses and zones |
|
|
| `ruleset` | the nftables ruleset Housecarl builds |
|
|
| `counters` | the hits of every rule, NAT rule, built-in and policy, by mark (`r12`, `f3`, `b-established`, `p-input`) |
|
|
| `log [-f] [--search TEXT] [--limit N]` | the packets logged by rules and policies ([operations.md](operations.md#the-log)) |
|
|
|
|
### zone, set, service
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `zone list`, `zone show ZONE` | |
|
|
| `zone add NAME -i IFACE... [--external] [--comment C]` | |
|
|
| `zone update ZONE [-i IFACE]... [--add IFACE] [--remove IFACE] [--external[=false]] [--name N] [--comment C]` | `-i` replaces the interfaces |
|
|
| `zone delete ZONE` | refused while rules use it |
|
|
| `set list`, `set show SET` | |
|
|
| `set add NAME [ENTRY...] [--dynamic] [--timeout 24h] [--comment C]` | |
|
|
| `set update SET [--entry E]... [--add E] [--remove E] [--timeout] [--name] [--comment]` | `--entry` replaces the entries |
|
|
| `set entries SET`, `set entries add SET ADDRESS [--timeout 1h]`, `set entries remove SET ADDRESS` | a dynamic set's entries |
|
|
| `set delete SET` | |
|
|
| `service list`, `service show SERVICE` | with the built-in ones |
|
|
| `service add NAME ENTRY... [--comment C]` | entries `tcp/22`, `udp/1000-2000`, `icmp/echo-request`, `icmpv6/packet-too-big` |
|
|
| `service update SERVICE`, `service delete SERVICE` | |
|
|
|
|
See [rules.md](rules.md).
|
|
|
|
### rule
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `rule list [CHAIN]` | in order, with their hits |
|
|
| `rule show ID` | |
|
|
| `rule add CHAIN [MATCH] [-a ACTION] [--first\|--before ID\|--after ID]` | at the end of its chain unless placed |
|
|
| `rule update ID [flags]` | only the flags given |
|
|
| `rule move ID --before ID\|--after ID\|--first\|--last\|--chain CHAIN` | |
|
|
| `rule enable\|disable ID` | |
|
|
| `rule delete ID` | |
|
|
|
|
The match flags (`rule add`, `snat add`, `redirect add`):
|
|
|
|
| | |
|
|
|---|---|
|
|
| `--in ZONE`, `--out ZONE` (`--not-in`, `--not-out`) | zones |
|
|
| `--from`, `--to` (`--not-from`, `--not-to`) | sets, addresses, prefixes, ranges |
|
|
| `--service` | services or entries |
|
|
| `--family ipv4\|ipv6` | |
|
|
|
|
And for rules: `--state`, `--status`, `--days`, `--hours`, `--limit
|
|
10/minute[,burst=N][,over][,per-source]`, `--conn-limit N`, `-a
|
|
accept|drop|reject|log`, `--reject-with tcp-reset`, `--log`,
|
|
`--log-prefix`, `--raw RULE`, `--comment`, `--disabled`
|
|
([rules.md](rules.md#rules)).
|
|
|
|
### forward, snat, redirect
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `forward list`, `forward show FORWARD` | port forwards, with their hits |
|
|
| `forward add PORTS --to HOST[:PORT] [--proto tcp,udp] [--public ADDR]... [--from SRC]... [--hairpin] [--hairpin-from SRC]... [--to6 HOST] [--name N] [--comment C]` | |
|
|
| `forward update FORWARD [flags]`, `forward delete FORWARD` | |
|
|
| `snat list`, `snat add [MATCH] [--snat-to ADDR]`, `snat update ID`, `snat delete ID` | source NAT, in order; masquerade without `--snat-to` |
|
|
| `redirect list`, `redirect add [MATCH] [--to-port PORT]`, `redirect update ID`, `redirect delete ID` | |
|
|
|
|
See [nat.md](nat.md).
|
|
|
|
### settings
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `settings show` | |
|
|
| `settings set [--input-policy\|--forward-policy\|--output-policy accept\|drop] [--log-drops on\|off] [--builtin NAME=on\|off]... [--own-prefix P]... [--antispoof-v4 P]... [--antispoof-v6 P]... [--flowtable DEV]... [--probation 2m]` | lists given replace the old ones |
|
|
|
|
### probation, history, import
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `confirm` | keep the changes on probation; the ruleset is saved for the boot |
|
|
| `rollback` | undo the changes on probation now |
|
|
| `history list [--limit N]` | changes, newest first |
|
|
| `history show ID` | each object before and after |
|
|
| `history undo ID` | apply the change backwards (refused if something it touched has changed since) |
|
|
| `import [FILE\|-] [--text FILE] [--defines FILE] [--table T] [--replace]` | [import.md](import.md) |
|
|
| `takeover TABLE... [-y]` | delete tables Housecarl's ruleset replaces (administrators) |
|
|
|
|
### cert
|
|
|
|
Administrators only ([certificates.md](certificates.md)).
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `cert` | the certificate: name, CA, validity, renewal, the last check |
|
|
| `cert check` | check that the challenge can be published through Jarl and seen, without the CA (exit code 1 if it can't) |
|
|
| `cert renew [-w]` | get a new certificate now; `-w` waits and shows the result |
|
|
|
|
### users, tokens, audit
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `whoami` | who you are to the server, and what you may do |
|
|
| `user list\|add\|update\|delete` | users (administrators); `user add NAME --role operator` |
|
|
| `token list [--all]`, `token create NAME [--role R] [--grant G]... [--expires 90d] [--user U] [--save]`, `token revoke ID` | API tokens |
|
|
| `token rotate ID\|--current [--grace 1h] [--expires 90d\|never] [--save]` | a new secret for a token, the old one working for the grace period |
|
|
| `password` | change your password |
|
|
| `audit [--user U] [--action PREFIX] [--search TEXT]` | the audit log |
|
|
| `config show`, `config set server\|token\|insecure\|ca_cert [VALUE]` | this CLI's settings |
|
|
|
|
[permissions.md](permissions.md) explains roles and grants.
|
|
|
|
## housecarld
|
|
|
|
The daemon's own commands work on the database directly, as root or the
|
|
`housecarl` user (`sudo -u housecarl housecarld ...`):
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `housecarld serve` | run (the systemd unit does) |
|
|
| `housecarld init [-u NAME] [--token]` | create the database and the first administrator (asks for the name on a terminal) |
|
|
| `housecarld user add NAME [--admin]`, `user password NAME`, `user token NAME TOKEN-NAME` | when the API can't be used |
|
|
| `housecarld check` | check the configuration against this machine's interfaces |
|
|
| `housecarld backup [FILE]`, `housecarld restore FILE` | [operations.md](operations.md) |
|
|
| `housecarld config [--example]` | the settings in use, or a commented example |
|
|
| `housecarld version` | |
|
|
|
|
All take `-c FILE` for another configuration file.
|