Rules that enforce themselves

Diesen Beitrag gibt es auch auf Deutsch →

Most server automation is a question of time of day: updates run at three in the morning, backups on Sunday. That works well for everything that is an action – and badly for everything that is a state. A firewall is not supposed to be configured correctly at three in the morning. It is supposed to be correct all the time.

That is exactly what LCM has a second kind of rule for, next to the scheduled ones: the baseline rule.

Two kinds of rules

In LCM, servers are bundled into groups, and a group carries schedules and rules. A scheduled rule hangs on a schedule and runs at cron time, one server after another – updates, scripts, a reboot in the maintenance window.

A baseline rule hangs on no clock at all. It is evaluated on every connection to the server, so at the very least on the regular health check. And it does not simply run: it first looks at how things actually are, and intervenes only on deviation.

For the firewall that means: if the port configuration matches what the rule prescribes, nothing happens – no command, no entry, no noise. If somebody opened a port by hand, or a package upgrade reset the configuration, the target state is restored. And because that is a change to a production system, it shows up as an audit entry and in the log. A configuration change should not live only in another job’s output.

Why not everything can be a baseline rule

The approach requires something that not every rule brings along: a target state that the actual state can be compared against. That is why baseline rules exist only for the four types that carry one – firewall, APT cache, and the two rules around directory permissions.

A shell command carries none. There is no way to ask it whether it is “already done”; as a baseline rule it would run unconditionally on every health check. For that, a schedule is the right place, where every run is its own job with its own result.

The reverse holds just as much: the permission rules cannot be attached to a schedule. They describe a state, not an action at a given time.

When two groups govern the same thing

A server may belong to any number of groups – convenient, but it creates a conflict as soon as two of them carry a baseline rule of the same type. Two firewall rules describe the same target state differently; only one can take effect.

That is decided by the group’s priority, and the lower number wins – read like MX records. Your own groups start at 100, the system group sits at 1000 and is thus deliberately the weakest: its rules apply to every server and form the baseline that a more specific group is allowed to override.

The superseded rule does not quietly disappear, either. It is named explicitly in the health check report, together with the group whose priority took precedence – so the answer to “why did this rule not apply?” is already in the report instead of having to be reconstructed.

If both groups carry the same priority, the older one wins. That is predictable, but it is not a decision anybody made – which is why LCM points out the tie rather than papering over it.

A catch that refuses to let a mistake through

A firewall baseline rule has to state explicitly which ports are meant to be open. If that is missing, the rule is rejected – not out of caution, but because the alternative would be fatal: a firewall rule without a port list would close every service port on every server in the group, and restore that state on every health check. You would have locked yourself out, on repeat.

If SSH really is meant to be the only thing open, that is stated with an empty list []. The difference between “I want nothing open” and “I forgot to fill this in” is one of those things a tool should not try to guess.

Further reading

The full description with all rule types is in the documentation under Groups, Schedules & Rules. An overview of LCM is on the product page – and if you would rather just try it, there is a demo system at lcm-demo.techeve.de.

← All articles