Skip to content

Commit

Permalink
Merge pull request #100 from tomeon/polkit-rules-definition
Browse files Browse the repository at this point in the history
polkit rules generation
  • Loading branch information
tomeon authored Jul 22, 2023
2 parents 6d0ed32 + d81dcd0 commit 41b1a36
Show file tree
Hide file tree
Showing 12 changed files with 1,278 additions and 50 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,14 @@ jobs:
apt-get -y update
apt-get -y install python3 sipcalc
run: ./run-tests
nixos:
name: Run NixOS system tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
with:
extra_nix_config: |
system-features = benchmark big-parallel kvm nixos-test uid-range
- name: run system tests
run: make nixos-test
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.*.sw[a-z0-9]
.sw[a-z0-9]
*~

# Nix build results
result
result-*
repl-result-*
129 changes: 129 additions & 0 deletions HACKING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Hacking on update-systemd-resolved

## Nix flake usage

This project supplies a [`flake.nix`](./flake.nix) file defining a Nix
flake.[^nix-flakes]

[^nix-flakes]: See the [NixOS wiki](https://nixos.wiki/wiki/Flakes) and the
[`nix flake` page in the Nix package manager reference manual](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html)
for background on Nix flakes.

This Nix flake defines three important important outputs:

1. A Nix package for `update-systemd-resolved` (see [here](./nix/packages.nix)),
2. A NixOS test of `update-systemd-resolved` features (see
[here](./nix/checks.nix)), and
3. A Nix development shell[^devshell] (see [here](./nix/devshells.nix)).

[^devshell]: Based on the [`numtide/devshell`](https://github.com/numtide/devshell) project.

In order to work on the `update-systemd-resolved` project's Nix features,
you'll need to [install the Nix package
manager](https://nixos.org/download.html) and [ensure that the `flakes` and
`nix-command` experimental features are
enabled](https://nixos.wiki/wiki/Flakes#Enable_flakes).

### Building the `update-systemd-resolved` package

To build the `update-systemd-resolved` package exposed by this flake, run the
following command:[^verbose-output]

[^verbose-output]: Note that the `-L` flag can be omitted for terser output.

```shell-session
$ nix build -L '.#'
```

Or:

```shell-session
$ nix build -L '.#update-systemd-resolved'
```

These two forms are functionally equivalent because the
`update-systemd-resolved` package is the default package.

In addition to building the package, `nix build` will place a symbolic link to
its output path at `./result` (`ls -lAR ./result/`, `tree ./result/`, or
similar to see what the package contains).

### Nix flake checks

This project includes a NixOS test of `update-systemd-resolved`'s functionality
and features, exposed as a Nix flake check.

Please see the NixOS manual's ["Testing with NixOS" section](https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests)
for information on [writing](https://nixos.org/manual/nixos/stable/index.html#sec-writing-nixos-tests)
and [running](https://nixos.org/manual/nixos/stable/index.html#sec-writing-nixos-tests)
NixOS tests, as well as information on [running them interactively](https://nixos.org/manual/nixos/stable/index.html#sec-running-nixos-tests-interactively)
and [linking them to Nix packages](https://nixos.org/manual/nixos/stable/index.html#sec-linking-nixos-tests-to-packages).

#### How the test works

This project's NixOS test sets up three machines:

1. An OpenVPN server,
2. An OpenVPN client, and
3. A DNS resolver running [Dnsmasq](https://thekelleys.org.uk/dnsmasq/doc.html).

The OpenVPN server and client run a [point-to-point configuration with a static
key](https://openvpn.net/community-resources/static-key-mini-howto/). The
client's OpenVPN configuration file includes several instances of [the
`dhcp-option`s recognized by `update-systemd-resolved`](./README.md#usage).
The [test script](https://nixos.org/manual/nixos/stable/index.html#test-opt-testScript)
starts all three machines, waits for certain systemd services to become active,
and then asserts that certain hostnames are resolvable _from the client_ that
would not be resolvable unless the client were configured to use the DNS
settings specified in its OpenVPN configuration file.

#### Extending the NixOS test

If you are implementing a new feature or correcting a bug in
`update-systemd-resolved`, you are encouraged to extend the NixOS test with new
test cases that exercise your feature or verify that the bug is fixed --
_unless_ you can adequately express these within the [unit test
suite](./README.md#how-to-help), as that suite runs significanly faster than
the NixOS test and has no prerequisites other than Bash.

#### Running Nix flake checks

To run the [above-mentioned](#nix-flake-usage) NixOS system test and other
checks,[^other-checks] execute the following command:[^verbose-output]

```shell-session
$ nix flake -L
```

[^other-checks]: Other checks include linting the Nix source code in this
project, and running a syntax check on the polkit rules
generated by `update-systemd-resolved print-polkit-rules`.

##### Running a check for a specific system

Running `nix flake check [-L]` will execute Nix flake checks for all supported
systems.[^supported-systems] To run a check for a particular system, instead
use the `nix build` command. For instance, to execute the NixOS system test
for the `x86_64-linux` system, run:

```shell-session
$ nix build -L '.#checks.x86_64-linux.update-systemd-resolved'
```

[^supported-systems]: Run `nix flake show` to view flake outputs namespaced by
all supported systems.

### Entering the Nix development shell

To enter the Nix development shell, run the following command:

```shell-session
$ nix develop
```

You will be presented with a menu of commands available within the development
shell.

#### Summary of available commands

- `fmt`: format all Nix code in this project using [`alejandra`](https://github.com/kamadorueda/alejandra).
28 changes: 24 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@ PREFIX ?= /usr/local/bin

SRC = update-systemd-resolved
DEST = $(DESTDIR)$(PREFIX)/$(SRC)
RULES = $(DESTDIR)/etc/polkit-1/rules.d/10-$(SRC).rules
RULES_OPTIONS ?= --polkit-allowed-user=openvpn --polkit-allowed-group=network

.PHONY: all install info
.PHONY: all install info rules

all: install info

install:
@install -Dm750 $(SRC) $(DEST)
@install -Dm644 $(SRC).conf $(DEST).conf
$(DEST): $(SRC)
@install -Dm750 $< $@

$(DEST).conf: $(SRC).conf
@install -Dm644 $< $@

$(RULES): $(SRC)
@mkdir -p $$(dirname $@)
@./$(SRC) print-polkit-rules $(RULES_OPTIONS) > $@

install: $(DEST) $(DEST).conf $(TEMPLATE_RULES_DEST)

rules: $(RULES)

info:
@printf 'Successfully installed %s to %s.\n' $(SRC) $(DEST)
Expand All @@ -32,5 +44,13 @@ info:
@echo
@printf 'or pass --config %s.conf\n' $(DEST)
@echo 'in addition to any other --config arguments to your openvpn command.'

test:
@./run-tests

nixos-test:
@nix build -L ".#checks.$$(nix eval --impure --raw --expr builtins.currentSystem).update-systemd-resolved"

# Enter a console with NixOS test machines available
nixos-test-driver:
@$$(nix-build --no-out-link -A update-systemd-resolved.nixosTest.driver ./nix)/bin/nixos-test-driver --keep-vm-state
145 changes: 125 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,50 @@ This script requires:

- Bash 4.3 or above.
- [coreutils](https://www.gnu.org/software/coreutils/) or
[busybox](https://www.busybox.net/) (for the `whoami` command).
[busybox](https://www.busybox.net/) (for the `id` command).
- [iproute2](https://wiki.linuxfoundation.org/networking/iproute2) (for the
`ip` command).
- [systemd](https://systemd.io/) (for the `busctl` and `resolvectl` commands).

Optional dependencies:

- [`python`](https://python.org) or
[`sipcalc`](https://github.com/sii/sipcalc). If available, these will be
used for IP address parsing and validation;[^iphandling] otherwise
`update-systemd-resolved` will use native Bash routines for this.
- [util-linux](https://en.wikipedia.org/wiki/Util-linux) (for the `logger`
command).
### IP Parsing and Validation

- [`python`](https://python.org).
**or**
- [`sipcalc`](https://github.com/sii/sipcalc).

If available, these will be used for IP address parsing and
validation;[^iphandling] otherwise `update-systemd-resolved` will use native
Bash routines for this.

[^iphandling]: Required for translating numerical labels like `1.2.3.4` to the
byte arrays recognized by [the `SetLinkDNS()` function on
`systemd-resolved`'s `org.freedesktop.resolve1.Manager` D-Bus
interface](https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html)).

### Logging

- [util-linux](https://en.wikipedia.org/wiki/Util-linux)

If available, the `logger` command included in the `util-linux` distribution
will be used for logging. Otherwise, all logs will go to standard error using
Bash's `printf` builtin.

### Polkit Rules Generation

- [`jq`](https://jqlang.github.io/jq/).
**or**
- [`perl`](https://www.perl.org/), or
**or**
- [`python`](https://python.org).

If available, these will be used for serializing the [names of the users and
groups allowed to call `systemd-resolved`'s DBus methods](#polkit-rules) to
JSON lists for use within the [generated polkit
rules](#generating-polkit-rules). Otherwise, `update-systemd-resolved` will
fall back to native Bash routines for generating these lists.

## Installation

[aur]:https://aur.archlinux.org/packages/openvpn-update-systemd-resolved/
Expand Down Expand Up @@ -99,6 +124,77 @@ hosts: files resolve myhostname

The changes will be applied as soon as the file is saved.

### Polkit Rules

If you run the OpenVPN client as an unprivileged user, you may need to add
polkit rules authorizing that user to perform the various DBus calls that
`update-systemd-resolved` makes. Some installation methods bundle these rules;
for instance, on Arch Linux, where `openvpn-client@<name>.service` instances
run as the unprivileged `openvpn` user, the
[openvpn-update-systemd-resolved][aur] AUR package ships suitable rules in the
file `/etc/polkit-1/rules.d/10-update-systemd-resolved.rules`.

#### Generating Polkit Rules

> **Note**
> `update-systemd-resolved` strives to generate polkit rules with the smallest
> scope consistent with its proper functioning. Nonetheless, in order to avoid
> security risks, you are encouraged to review the generated polkit rules
> before installing them.
You can also generate suitable rules with (some variation on) the following
commands:

```shell-session
$ update-systemd-resolved print-polkit-rules --polkit-allowed-user some-user --polkit-allowed-user another-user > ./10-custom-update-systemd-resolved.rules
$ sudo install -Dm0640 ./10-custom-update-systemd-resolved.rules /etc/polkit-1/rules.d/10-custom-update-systemd-resolved.rules
```

This will allow `update-systemd-resolved` to successfully make its DBus calls
when invoked from OpenVPN client services that run as the users `some-user` or
`another-user`.

You can also authorize members of specified groups with:

```shell-session
$ update-systemd-resolved print-polkit-rules --polkit-allowed-group some-group --polkit-allowed-group another-group > ./10-custom-update-systemd-resolved.rules
$ sudo install -Dm0640 ./10-custom-update-systemd-resolved.rules /etc/polkit-1/rules.d/10-custom-update-systemd-resolved.rules
```

This will allow `update-systemd-resolved` to successfully make its DBus calls
when invoked from OpenVPN client services that run under the groups
`some-group` or `another-group`.

Finally, you can generate rules that pull appropriate user and group values
from OpenVPN systemd units with:

```shell-session
$ update-systemd-resolved print-polkit-rules --polkit-systemd-openvpn-unit my-openvpn-client.service
$ sudo install -Dm0640 ./10-custom-update-systemd-resolved.rules /etc/polkit-1/rules.d/10-custom-update-systemd-resolved.rules
```

Given:

```shell-session
$ systemctl show -P User my-openvpn-client.service
myuser
$ systemctl show -P Group my-openvpn-client.service
mygroup
```

The generated `10-custom-update-systemd-resolved.rules` file will contain rules
allowing the `myuser` user and members of the `mygroup` group to perform the
requisite DBus calls.

You can run `update-systemd-resolved print-polkit-rules` with any combination
of `--polkit-allowed-user`, `--polkit-allowed-group`, and
`--polkit-systemd-openvpn-unit`. If called without options,
`update-systemd-resolved print-polkit-rules` will attempt to derive appropriate
user and group authorizations from a systemd OpenVPN unit matching
`openvpn-client@.service`, the [systemd service
template](https://www.freedesktop.org/software/systemd/man/systemd.service.html#Service%20Templates)
used for OpenVPN client services on distributions including Arch Linux.

### Stub Resolver

The `systemd-resolved` service (since systemd-231) also listens on `127.0.0.53`
Expand Down Expand Up @@ -157,20 +253,24 @@ most likely will not need this.

#### down/pre-down with user/group

The `down` and `down-pre` options here will not work as expected where the
The `down` and `down-pre` options here may not work as expected where the
`openvpn` daemon drops privileges after establishing the connection (i.e. when
using the `user` and `group` options). This is because only the `root` user
will have the privileges required to talk to `systemd-resolved.service` over
DBus. The `openvpn-plugin-down-root.so` plug-in does provide support for
enabling the `down` script to be run as the `root` user, but this has been
known to be unreliable.

Ultimately this shouldn't affect normal operation as `systemd-resolved.service`
will remove all settings associated with the link (and therefore naturally
update `/etc/resolv.conf`, if you have it symlinked) when the TUN or TAP device
is closed. The option for `down` and `down-pre` just make this step explicit
before the device is torn down rather than implicit on the change in
environment.
using the `user` and `group` options). This is because, by default, only the
`root` user will have the privileges required to talk to
`systemd-resolved.service` over DBus. The `openvpn-plugin-down-root.so` plug-in
does provide support for enabling the `down` script to be run as the `root`
user, but this has been known to be unreliable.

You can authorize unprivileged users or groups to revert the OpenVPN link's DNS
settings during the "down" phase using the methods described in the ["Polkit
Rules" section](#polkit-rules).

Ultimately, dropping privileges shouldn't affect normal "down" operation, since
`systemd-resolved.service` will remove all settings associated with the link
(and therefore naturally update `/etc/resolv.conf`, if you have it symlinked)
when the TUN or TAP device is closed. The option for `down` and `down-pre` just
make this step explicit before the device is torn down rather than implicit on
the change in environment.

### Command Line Settings

Expand Down Expand Up @@ -382,6 +482,11 @@ language.
GitHub Actions are enabled on this repository: Click the link at the top of this
README to see the current state of the code and its tests.

### Development notes

Please see [`HACKING.md`](./HACKING.md) for notes on developing
`update-systemd-resolved`.

## Licence

GPL
Expand Down
Loading

0 comments on commit 41b1a36

Please sign in to comment.