# 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 | | `--no-confirm` | | leave a change on probation instead of confirming it once `hc` reaches Housecarl again | | `-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, and `hc` confirms it by reaching Housecarl again over a new connection ([operations.md](operations.md#changes-on-probation)): ``` $ hc rule add input --from trusted --service ssh --comment "SSH from home" created rule input #31 (from trusted · ssh) applied and confirmed (history 12): this client reached Housecarl again over a new connection ``` 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; `history undo` is staged like any change; `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)) | | `connections [--search TEXT] [--limit N]`, `connections close --search TEXT [-y]` | tracked connections; closing makes the rules check them again ([operations.md](operations.md#connections)) | | `matrix [FROM TO]` | what the rules do to new connections between zones; with FROM and TO, that pair's rules in order ([rules.md](rules.md#the-zone-matrix)) | | `trace [--address A] [--port N] [--protocol P] [--seconds S]` | follow packets through every table's rules ([operations.md](operations.md#tracing)) | ### 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`, `--every-packet`, `-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] [--auto-confirm on\|off]` | 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.