Changesets, as in Jarl: configuration changes are operations that can be staged in a changeset, checked as they are added (validation and nft -c), reviewed with the diff of the ruleset, and applied together as one history entry, then on probation like any change made over the network. The web UI collects edits for review by default (pending rows, a bar with Review and Apply now, a Changes page, a switch for live edits); hc stages with -C ID or HOUSECARL_CHANGESET (checked like JARL_CHANGESET) and has hc changeset create/list/show/diff/apply/discard/remove. Undo, import and takeover refuse a changeset. --dry-run shows the ruleset's diff. Bandwidth and quotas: rate limits in bytes, per source or destination; quotas per rule or per address that reset daily, weekly, monthly on a day, or by hand, with their usage carried across reloads, saved for reboots, shown by hc quotas and on the rules page, and reset with hc rule reset-quota. Rules that count traffic are checked before established connections are accepted (so they see every packet) and can't accept. Per-address limits group IPv6 by /64 by default (--group-v4/-v6). Per-source rate meters use update. The importer turns meters, byte rates and quotas into these fields. The examples in the web UI, hc's help and the docs use documentation addresses and generic names instead of the maintainer's network. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
260 lines
11 KiB
Markdown
260 lines
11 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, with the ruleset's diff |
|
|
| `-C, --changeset ID` | `HOUSECARL_CHANGESET` (checked, see [Changesets](#changesets)) | stage configuration changes in this changeset instead of making them |
|
|
| `-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, with the diff of the
|
|
ruleset; nftables checks it too.
|
|
|
|
## Changesets
|
|
|
|
To review several changes and apply them together, as one entry in the
|
|
history, stage them in a changeset:
|
|
|
|
```sh
|
|
export HOUSECARL_CHANGESET=$(hc changeset create "guest network" -q)
|
|
hc zone add guest -i eth1.30
|
|
hc rule add forward --in guest --out wan
|
|
hc changeset diff # the operations, problems, nftables' check, the ruleset's diff
|
|
hc changeset remove 2 # take operation 2 out
|
|
hc changeset apply # one change; over the network on probation
|
|
unset HOUSECARL_CHANGESET
|
|
```
|
|
|
|
```
|
|
$ hc rule add forward --in guest --out wan
|
|
changeset 7 (guest network, 1 operation) from HOUSECARL_CHANGESET
|
|
staged: create rule forward #52
|
|
staged in changeset 7 as operation 2 (2 operations now); review with 'hc changeset diff 7', apply with 'hc changeset apply 7'
|
|
```
|
|
|
|
Each change is checked as it is staged: the changeset with it must stay
|
|
valid, and nftables must accept the ruleset, so make a set before the
|
|
rule that names it. Applying checks the whole changeset again, on the
|
|
configuration as it is then; an operation whose object changed since is
|
|
named (`operation 3 (update rule input #12) can't be applied: …`), and
|
|
`hc changeset remove` takes it out.
|
|
|
|
| Command | |
|
|
|---|---|
|
|
| `changeset create [DESCRIPTION]` | start one; `-q` prints only its ID |
|
|
| `changeset list [--status open\|applied\|discarded\|""] [--mine]` | open ones by default |
|
|
| `changeset show [ID]` | its operations |
|
|
| `changeset diff [ID]` | what applying it would do |
|
|
| `changeset apply [ID] [--description D]` | apply it |
|
|
| `changeset discard [ID]` | throw it away |
|
|
| `changeset remove [ID] OP` | take one operation out |
|
|
|
|
`show`, `diff`, `apply`, `discard` and `remove` work on the changeset you
|
|
name, else the one in `-C`, else the one in `HOUSECARL_CHANGESET`.
|
|
|
|
A changeset from `HOUSECARL_CHANGESET` is checked before anything uses
|
|
it, because an exported ID easily outlives its changeset:
|
|
|
|
- Staging into it, and `apply`, `discard` and `remove` on it, need it to
|
|
be open and yours. Otherwise the command stops and says why (`there is
|
|
no changeset 99`, `changeset 42 is applied`, `changeset 57 is sam's`).
|
|
Commands that only read, and `hc changeset create`, don't care.
|
|
- Each command says on stderr which changeset it uses: `changeset 42
|
|
(guest network, 2 operations) from HOUSECARL_CHANGESET` (not with `-q`).
|
|
- After `apply` or `discard`, a reminder to `unset HOUSECARL_CHANGESET`.
|
|
|
|
`-C ID` and an ID argument are used as given (housecarld still refuses a
|
|
changeset that isn't open, or another user's unless you are an
|
|
administrator). Only configuration changes are staged: dynamic set
|
|
entries, `confirm` and `rollback` are made at once, and `history undo`,
|
|
`import` and `takeover` can't be staged, so they are refused while a
|
|
changeset is named. Changesets staged in the web UI show up here too,
|
|
and the other way round.
|
|
|
|
## 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`) |
|
|
| `quotas` | what each rule's quota used (each address's, for per-address quotas), and when it resets |
|
|
| `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 reset-quota ID` | start a rule's quota again now |
|
|
| `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|1mbytes/second[,burst=N][,over][,per-source|,per-destination]`,
|
|
`--quota 50gbytes[,over][,per-source|,per-destination][,daily|,weekly|,monthly=DAY]`,
|
|
`--group-v4 N`, `--group-v6 N`, `--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.
|