fix typos and write localhost whitelisting section

nixos-config-re
Alexander Tomokhov 2023-06-23 03:39:44 +04:00
parent a47afa4183
commit 062f512df2
1 changed files with 68 additions and 41 deletions

View File

@ -8,13 +8,26 @@ Generally, it's better to implement as many layers of security as possible. Alth
Systemd is the standard software suite for organizing and running services/daemons in a modern GNU/Linux distribution, including NixOS. Systemd provides means to secure services. And in many ways, the isolation level of a systemd service can be similar to that of containers (by the means of sandboxing, namespaces, cgroups, etc; interestingly, [systemd even allows running](https://www.stevenrombauts.be/2019/01/run-multiple-instances-of-the-same-systemd-unit/) [multiple instances of the same service](https://opensource.com/article/20/12/multiple-service-instances-systemctl)). However, systemd hardening defaults are quite loose (perhaps, not to disturb the operation of newly written services and their administrators in any way).
What NixOS does - it generates systemd configuration files in accordance to NixOS configuration given, written in Nix language. To some extent, Nix acts as a macro language and NixOS configuration module system acts as a unified control center, so that you don't bother about location of systemd files, their syntax and common stuff, which NixOS generates for you. Also, NixOS manages runtime switching between systemd configurations, conducting services restarts when required and whole system rollbacks from GRUB/systemd-boot/extlinux.
What NixOS does - it generates systemd configuration files in accordance to NixOS configuration given, written in Nix language. To some extent, Nix acts as a macro language and NixOS configuration module system acts as a unified control center, so that you don't bother about location of systemd files, their syntax and common stuff, which NixOS generates for you. Also, NixOS manages runtime switching between systemd configurations, conducting services restarts when required, and whole system rollbacks from GRUB/systemd-boot/extlinux.
## overview of systemd services integration within NixOS configuration
NixOS features lots of systemd services, which are ready to use (without even knowing what systemd is) just by setting appropriate options in `configuration.nix`. For example, write `services.netdata.enable = true;` to enable [Netdata](https://www.netdata.cloud/) monitoring service. Documentation for all related options can be found on the [website](https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=services.) or in `man configuration.nix` (also in `man home-configuration.nix` for [managing desktop](https://github.com/nix-community/home-manager) user services). Often many useful high-level tunables are available as `services.<name>.*` options.
When services, provided by NixOS, are insufficient or additional tuning is demanded, `systemd.services.<name>.*` set of options comes into play. They allow to define custom systemd services or modify existing ones. Regardless of the origin of a systemd service (provided by NixOS or written by yourself), `systemd` native options for sections such as `[Unit]` and `[Service]` can be specified accordingly in the following [nix attribute sets](https://nixos.org/manual/nix/stable/language/values.html#attribute-set): `systemd.services.<name>.unitConfig` and `systemd.services.<name>.serviceConfig`. `[Install]` section options such as `Alias`, `WantedBy` and `RequiredBy` can be specified as [nix lists](https://learnxinyminutes.com/docs/nix/) in `systemd.services.<name>.aliases`, `systemd.services.<name>.wantedBy`, `systemd.services.<name>.requiredBy`. You can find more information about such options [online](https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=systemd.services.) or in `man configuration.nix` as usual.
When services, provided by NixOS, are insufficient or additional tuning is demanded, `systemd.services.<name>.*` set of options comes into play. They allow to define custom systemd services or modify existing ones. Regardless of the origin of a systemd service (provided by NixOS or written by yourself), `systemd` native directives for sections such as `[Unit]` and `[Service]` can be specified accordingly in the following [nix attribute sets](https://nixos.org/manual/nix/stable/language/values.html#attribute-set):
* `[Unix]`: `systemd.services.<name>.unitConfig = { SYSTEMD_DIRECTIVE = VALUE; ... }`
* `[Service]`: `systemd.services.<name>.serviceConfig = { SYSTEMD_DIRECTIVE = VALUE; ... }`
String values must be enclosed in double quotes. Boolean values are written as `true` and `false`. This is just [Nix syntax](https://nixos.org/manual/nix/stable/language/index.html#overview).
`[Install]` section directives such as `Alias`, `WantedBy` and `RequiredBy` can be specified as [nix lists](https://learnxinyminutes.com/docs/nix/) in:
* `systemd.services.<name>.aliases`
* `systemd.services.<name>.wantedBy`
* `systemd.services.<name>.requiredBy`.
You can find more information about such options [online](https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=systemd.services.) or in `man configuration.nix` as usual.
In a nutshell, configuring systemd options for services on NixOS typically boils down to these steps:
@ -27,8 +40,8 @@ Alternatively, new configurations can be tested inside a QEMU VM clone of your s
Be aware that systemd directives (options) are case sensitive! But NixOS doesn't know whether systemd recognizes any directives or not, whereas systemd does not complain neither! So, once new configuration is applied, analyze output of these commands and compare with intended objectives:
* `systemd cat <name>` - contents of a systemd unit file, generated by NixOS
* `systemd show <name>` - actual properties of a systemd unit in use
* `systemctl cat <name>` - contents of a systemd unit file, generated by NixOS
* `systemctl show <name>` - actual properties of a systemd unit in effect
Also, keep in mind that mutable operations like `systemd SERVICE enable` are useless, because they would deviate the system from declarative reproducible configuration and NixOS won't let or will stubbornly resist you doing so at the design level. And there is no need, since each permanent setting is in the hands of NixOS.
@ -38,42 +51,6 @@ NixOS provides many services, available as `services.<name>.*`, which already ha
There are, however, services like `services.dovecot2`, `services.postfix` and `services.nextcloud`, which use their own means to spawn sub-processes under a specific user by a master process. Such master process is run under `root`. For example, nextcloud uses `php:fpm` ([PHP FastCGI Process Manager](https://php-fpm.org/)). Obviously, shell can be spawned by such processes and a lot more, but they do not have network connections outside world and intended specifically for process/workers management and logging. Ideally, we would want them to be run under non-root user regardless, but usually [it's not easy to do](https://github.com/docker-library/php/issues/70#issuecomment-1386729923) and upstream might not expect such usage.
### blocking outgoing internet connections
The idea is to keep responding to incoming requests to some service, but forbid any outgoing connections, initiated by itself.
When it comes to a more sophisticated firewall, unfortunatelly systemd is not capable of such granular control. So, `iptables` configuration will be:
```nix
networking.firewall = {
extraCommands = ''
iptables -t filter -I OUTPUT 1 -m owner --uid-owner ${user} -m state --state NEW -j REJECT
'';
extraStopCommands = ''
iptables -t filter -D OUTPUT 1 -m owner --uid-owner ${user} -m state --state NEW
'';
};
```
### resources limits strategy
Systemd resource control settings allow you to limit the resources provided to a service. For example, if `MemoryMax` limit is exceeded, OOM killer gets invoked.
```nix
systemd.service = {
nginx = {
serviceConfig = {
CpuAccounting = true;
CpuQuota = "70%";
MemoryAccounting = true;
MemoryMax = "768M";
BlockIOWeigth = 10;
};
};
}
```
`MemoryMax` is the absolute limit. It is recommended to use `MemoryHigh` as the main control mechanism, because it allows to go above the limit, but the processses are heavily slowed down and memory is taken away aggressively.
_Btw, if your systemd service code gets large and you want to wrap it into something more esthetic, you [can](https://nixos.wiki/wiki/Extend_NixOS) [write your own NixOS service module](https://scvalex.net/posts/58/)._
### cgroups
@ -89,6 +66,56 @@ DefaultBlockIOAccounting=yes
DefaultIPAccounting=yes
```
### resources control (limits) for a systemd service
Systemd resource control directives allow you to limit resources provided to a service. For example, if `MemoryMax` limit is exceeded, OOM killer gets invoked.
```nix
systemd.service = {
nginx = {
serviceConfig = {
CpuAccounting = true;
CpuQuota = "70%";
MemoryAccounting = true;
MemoryMax = "768M";
BlockIOWeight = 10;
};
};
}
```
`MemoryMax` is the absolute limit. It is recommended to use `MemoryHigh` as the main control mechanism, because it allows to go above the limit, but the processses are heavily slowed down and memory is taken away aggressively according to systemd documentation.
Refer to [official documentation](https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Options) for many other options.
### blocking any network connections except localhost
This is appropriate, for example, if a service communicates with outside world via proxy (like `nginx`). And can be configured also with the help of systemd resource control directives, partially mentioned above.
```nix
systemd.services.netdata.serviceConfig = {
IPAddressDeny = "any";
IPAddressAllow = "localhost";
};
```
### blocking outgoing internet connections
When it comes to a more sophisticated firewall, unfortunatelly systemd is not capable of such granular control. So, `iptables` configuration will be:
The idea here is to keep responding to incoming requests to some service, but forbid any outgoing connections, initiated by itself.
```nix
networking.firewall = {
extraCommands = ''
iptables -t filter -I OUTPUT 1 -m owner --uid-owner ${user} -m state --state NEW -j REJECT
'';
extraStopCommands = ''
iptables -t filter -D OUTPUT 1 -m owner --uid-owner ${user} -m state --state NEW
'';
};
```
### list of systemd options and their implications
@ -96,7 +123,7 @@ DefaultIPAccounting=yes
### basic systemd commands for diagnostics
- `systemd status`, `systemd restart`, `systemd cat`, `htop` tree
- `systemctl status`, `systemctl restart`, `systemctl cat`, `htop` tree
`systemd-analyze`'s words "`SAFE`", "`EXPOSED`" and "`UNSAFE`" do not mean the factual situation, rather whether various systemd hardedning features are in use or not.