# Firewall lists

The list memberships install adds so the router's raw drop rules let the agent's replies through, how to name your own lists or none, and what doctor checks.

Source: https://docs.jmrp.io/mikroscope/install/firewall/

`install` adds the veth to an interface list and the container's /30 to an address list, so that
the raw drop rules of a MikroTik firewall let the agent's replies through. Name the lists your rules
use with `--iface-list` and `--addr-list`, or `none` when no rule needs them. Without `--expose`,
`install` writes no firewall rule.

## Raw drop rules

MikroTik's [Building Advanced Firewall](https://help.mikrotik.com/docs/spaces/ROS/pages/328513/Building+Advanced+Firewall)
guide adds raw rules commented `defconf:`. Two of them silently drop every packet a new container
sends:

| Rule                                                                | Drops a packet that                                   | A new container is dropped because | What lets it through                                                                                   |
| ------------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `drop the rest`, `in-interface-list=!LAN`                           | enters on an interface outside the `LAN` list         | its veth is in no interface list   | the veth in `LAN`: `--iface-list`                                                                      |
| `drop local if not from default IP range`, `src-address-list=!LANs` | comes from an address outside the `LANs` address list | its /30 is in no address list      | the /30 in `LANs`: `--addr-list`                                                                       |
| the same rule written `src-address=!192.168.88.0/24`                | comes from an address outside that range              | its /30 is outside the range       | no list: add an accept rule for `in-interface=<veth>` before it, or pick a `--subnet` inside the range |

The drop is silent. From your host the agent does not answer, which looks the same as a container
that is not running, so the probe after `install` asks the router whether the container runs before
it suggests anything else ([Network access](https://docs.jmrp.io/mikroscope/install/reaching-the-agent/)).

Other drop rules in `raw`, `input` or `forward` can drop the container's traffic elsewhere.
`install` adds nothing for them beyond the two memberships; `doctor`'s [trap check](https://docs.jmrp.io/mikroscope/install/firewall/#trap-check)
reads them and names the rule.

## List memberships

With the default lists, `install` writes these two entries:

| Step                      | Command                                                                                                                     | Left out with       |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| interface-list membership | `/interface/list/member/add list="LAN" interface="veth-mikroscope" comment="mikroscope:mikroscope (managed by mikroscope)"` | `--iface-list none` |
| address-list membership   | `/ip/firewall/address-list/add list="LANs" address=172.30.10.0/30 comment="mikroscope:mikroscope (managed by mikroscope)"`  | `--addr-list none`  |

- Both carry the tag. `uninstall` removes each by the tag, the list and the member.
- The interface list must exist. The address list need not: its first entry creates it, and
  `uninstall` removes the entry.
- With both memberships, a host on the LAN reaches the agent directly through the router
  ([verified](https://docs.jmrp.io/mikroscope/about/status/#verified-direct-lists)).

A membership is not scoped to mikroscope. Any other rule of your router that matches the `LAN`
interface list or the `LANs` address list matches the container's traffic too, for as long as it is
installed. Read your own rules with that in mind.

## Custom list names

Pass the lists your drop rules use:

| Flag           | Variable                | Default | Accepted                                                                                               | Names                                                             |
| -------------- | ----------------------- | ------- | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- |
| `--iface-list` | `MIKROSCOPE_IFACE_LIST` | `LAN`   | `^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$` or `none`; not RouterOS's built-in `all`, `dynamic` or `static` | the list your `in-interface-list=!…` drop rule uses               |
| `--addr-list`  | `MIKROSCOPE_ADDR_LIST`  | `LANs`  | `^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$` or `none`                                                          | the list your `drop local if not from default IP range` rule uses |

```sh
mikroscope install --iface-list MYLAN --addr-list MYNETS
```

A built-in interface list takes no member, so `--iface-list static` stops before the first connect:

```text
mikroscope: iface-list "static" is a RouterOS built-in list, which takes no member: name the list your firewall's in-interface-list=!… drop rule uses, or none to join no list
```

`status`, `upgrade` and `uninstall` read the lists from the install on the router, so they need no
list flag. A list flag that contradicts the install is refused, naming both values ([Install
shape](https://docs.jmrp.io/mikroscope/reference/cli/#pass-the-same-shape-to-status-upgrade-and-uninstall)).

## No membership

A router with no rule that drops by list needs neither membership. Leave both out:

```sh
mikroscope install --iface-list none --addr-list none
```

`doctor` tells you when that is the case: its trap check ends with
`they would pass with no list membership as well`, and a missing interface list is reported with
`--iface-list none` as the first fix:

```text
  MISSING interface list LAN exists (found=0)
          fix: --iface-list none: no firewall rule here needs the veth in an interface list. Or create it: `/interface/list/add name=LAN`
```

## Trap check

`doctor`, run alone or by `install`, checks the lists and then follows the agent's replies through
your firewall:

The checks doctor runs:

| Check, as printed | Passes when | The fix it names |
| --- | --- | --- |
| interface list <list> exists | the `--iface-list` list (default `LAN`) exists. With `--iface-list none` doctor prints `interface list the veth joins` and passes: no membership is written | `--iface-list none` when no firewall rule needs the veth in a list (doctor offers it first then); otherwise `/interface/list/add name=…`, or pass the list your `in-interface-list=!…` drop rule uses |
| address list <list> | always: install adds the /30 to the `--addr-list` list (default `LANs`), which creates it when it is missing, and uninstall removes the entry. With `--addr-list none` doctor prints `address list the /30 joins`. Whether a rule needs the membership is the next row's question | none |
| no firewall rule drops the agent's replies | doctor reads every enabled rule of the chains the agent's replies meet, `/ip/firewall/raw` prerouting and `/ip/firewall/filter` forward and input, and walks each one as RouterOS does, first match wins, with the replies in the lists the plan joins: no rule drops them. A warning when a rule might, because it matches on something doctor does not judge (a destination, a mark, a rate), and when the replies to a LAN host pass but a rule may drop the ones to the router itself (filter input), which the relay transport needs | the `--iface-list` and `--addr-list` that let the replies through; when no list does (a `src-address=!<range>` rule, say), add an accept rule for `in-interface=<veth>` before that rule, or pick a `--subnet` inside the range |

The trap check reads every enabled rule of `/ip/firewall/raw` prerouting and `/ip/firewall/filter`
forward and input, and walks each chain as RouterOS does, first match first. The replies it follows
come in on the veth, from the agent's address and port, in the lists the plan joins.

| Prints                                                                                     | Means                                                                                   | Do                                                                        |
| ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `ok … no rule drops them`                                                                  | the replies pass with the plan's memberships                                            | nothing                                                                   |
| `ok … they would pass with no list membership as well`                                     | no rule needs a membership                                                              | `--iface-list none --addr-list none`, if you want no membership           |
| `MISSING … drops them`, fix `install with --iface-list … --addr-list …`                    | a rule drops the replies, and other lists the rules name let them through               | install with the lists the fix names                                      |
| `MISSING … drops them`, fix `… add an accept rule for in-interface=<veth> before it`       | no list the rules name lets the replies through, as with a `src-address=!<range>` rule | add that accept rule, or pick a `--subnet` inside the range the fix names |
| `WARN … may drop them`                                                                     | the rule matches on something `doctor` does not judge: a destination, a mark, a rate    | if the agent does not answer after `install`, look at that rule first     |
| `WARN … may drop the ones to the router itself`                                            | replies to a LAN host pass, and a `filter input` rule may drop replies to the router    | use the direct transport; the relay may not reach the agent               |

`doctor` names a rule by its table, chain and number, its matchers and its comment, as in
`/ip/firewall/raw rule 0 (chain=prerouting action=drop, …) "…"`. It never proposes a list the
router's uplink is in, and it takes an address list other than the plan's to hold no entry for the
/30.

It walks a rule the way RouterOS applies it, which is not always the way it reads. RouterOS passes
over a rule it marks invalid, and a rule that names an interface list that was removed stays valid
with the list's id in place of its name and matches as if the list were empty
([Router exposure](https://docs.jmrp.io/mikroscope/install/firewall/#router-exposure)).

## Router exposure

Two warnings are about the router rather than the install. They change neither `doctor`'s exit
status nor whether `install` goes ahead, but a router whose firewall does not do what it reads as
doing carries traffic it was never meant to, and every panel then measures that traffic:

The checks doctor runs:

| Check, as printed | Passes when | The fix it names |
| --- | --- | --- |
| no firewall rule doctor reads is invalid or names a deleted list | a warning: no rule of raw prerouting or filter forward and input is marked invalid, which RouterOS passes over as if it were not there (it names an interface that was removed or is not ready, and `about` says which), and none names an interface list that was removed, which RouterOS keeps as the list's id (`in-interface-list=!*2000010`) and matches as an empty list. A rule changed a moment before reads invalid with no reason until RouterOS has applied it | fix or remove what an invalid rule names; set a deleted list again by name, since creating a list of the same name does not repair the rule. With no reason given, run `doctor` again |
| the router does not answer DNS from its uplink | a warning: `/ip/dns` `allow-remote-requests` is off, or a rule of raw prerouting or filter input drops a UDP query to port 53 that comes in on the interface of the active default route, walked as for the trap check, first match wins. A warning that the queries may pass when a rule matches on something doctor does not judge, a source address list say. IPv6 is not read | a drop rule for UDP and TCP port 53 on the uplink before any accept that takes it, or `/ip/dns/set allow-remote-requests=no` if no LAN host uses the router as its resolver |

- **A rule that does nothing, or does too much.** An invalid rule, one that names an interface that
  was removed or is not ready, is passed over: an invalid drop drops nothing. A rule whose interface
  list was removed reads `in-interface-list=!*2000010` and matches as if the list were empty, so a
  `!LAN` drop left that way drops every packet that reaches the input chain, the router's own SSH
  and Winbox from the LAN included. Creating a list of the same name does not repair it: set the
  rule's list again by name.
- **An open resolver.** With `/ip/dns allow-remote-requests=yes`, the router answers DNS on every
  interface its firewall lets queries in on. `doctor` follows a UDP query to port 53 that comes in
  on the interface of the active default route through raw prerouting and filter input; when no
  rule drops it, the router answers DNS for the Internet, and reflection attacks that find it fill
  its connection table and its CPU. The default configuration's `in-interface-list=!LAN` drop on
  the input chain stops them.

Both were checked against RouterOS in the lab ([Tested on](https://docs.jmrp.io/mikroscope/about/status/#doctor-exposure)).

## Existing memberships

If the veth is already in the interface list, or the /30 already in the address list, and that
entry does not carry mikroscope's tag, `install` stops at that step and names it: the effect exists,
but mikroscope did not create it and will not remove it. Pick another `--veth` or `--subnet`, or
remove your entry by hand if it is yours. `uninstall` never touches it.

## Expose rules

Only `--expose` writes firewall rules: a dst-nat on the router's LAN address and a forward accept
placed before the first forward drop, both tagged. `uninstall` removes them with the rest of the
install, because it reads from the router that the install was exposed. [Expose on the
LAN](https://docs.jmrp.io/mikroscope/security/expose/) has both rules and who reaches the agent through them.

## See also

- [Network access](https://docs.jmrp.io/mikroscope/install/reaching-the-agent/): the three ways to reach the agent, and
  what to do when the memberships are not enough.
- [Requirements](https://docs.jmrp.io/mikroscope/install/prerequisites/): every `doctor` check.
- [Expose on the LAN](https://docs.jmrp.io/mikroscope/security/expose/): the two rules `--expose` adds.
- [Tested on](https://docs.jmrp.io/mikroscope/about/status/): the firewalls these memberships ran against.
