From 3b317a4f0cb5b0082b06e89249da60bf15305eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Wed, 21 Oct 2026 10:50:35 +0200 Subject: [PATCH] chages in systemd --- README.md | 34 +++++++++- SHA256SUMS | 15 +++-- packaging/rustproxy-filesystem-access.md | 85 ++++++++++++++++++++++++ scripts/lib.sh | 2 +- scripts/normalize-systemd-unit.py | 42 ++++++++++++ scripts/prepare-package.sh | 38 +++-------- tests/linux-path-access.sh | 67 +++++++++++++++++++ tests/log-rotation.sh | 2 + tests/static.sh | 5 +- 9 files changed, 249 insertions(+), 41 deletions(-) create mode 100644 packaging/rustproxy-filesystem-access.md create mode 100644 scripts/normalize-systemd-unit.py create mode 100755 tests/linux-path-access.sh diff --git a/README.md b/README.md index 2c9c0a9..840def6 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ data/packages/rpm/x86_64/rustproxy--.x86_64.rpm data/packages/rpm/x86_64/rustproxy-docker-agent--.x86_64.rpm ``` -Obie paczki są podpisane istniejącym kluczem GPG repozytorium, a repo APT/DNF automatycznie zawiera oba produkty. Zmiana rewizji pakowania Linux (`PACKAGING_REVISION=4`) wymusza przebudowę także dla niezmienionego commita. +Obie paczki są podpisane istniejącym kluczem GPG repozytorium, a repo APT/DNF automatycznie zawiera oba produkty. Zmiana rewizji pakowania Linux (`PACKAGING_REVISION=5`) wymusza przebudowę także dla niezmienionego commita. Instalacja na hoście Docker (uruchom Docker Engine wcześniej): @@ -249,7 +249,33 @@ configctl rustproxy start ``` Obsługa miękkiego przeładowania zwiększa `OPNSENSE_PACKAGING_REVISION` do `4`. -Pakowanie Linux ma `PACKAGING_REVISION=4`, a natywne FreeBSD `FREEBSD_NATIVE_PACKAGING_REVISION=1`. Każdy kanał otrzymuje osobną aktualizację przy zmianie swojej rewizji. +Pakowanie Linux ma `PACKAGING_REVISION=5`, a natywne FreeBSD `FREEBSD_NATIVE_PACKAGING_REVISION=1`. Każdy kanał otrzymuje osobną aktualizację przy zmianie swojej rewizji. + +## DEB/RPM: dostęp do plików strony i zewnętrznych certyfikatów + +Od rewizji pakowania **5** jednostka `rustproxy.service` używa +`ProtectHome=read-only` zamiast `ProtectHome=true`. Dzięki temu katalogi +`/home/...`, `/var/www/...` i `/srv/...` mogą być czytane na podstawie +normalnych uprawnień grupowych lub ACL, bez lokalnych wyjątków systemd. +`ProtectSystem=strict` nadal chroni te lokalizacje przed zapisem. Builder +normalizuje również `ProtectHome` w starszym `debian/rustproxy.service`, jeśli +taki plik istnieje w pobranym źródle; sama rewizja pakowania wystarcza do +ponownego zbudowania DEB/RPM przy niezmienionym kodzie aplikacji. + +Instrukcja trafia **do obu paczek** jako +`/usr/share/doc/rustproxy/FILESYSTEM_ACCESS.md` (źródło: +[`packaging/rustproxy-filesystem-access.md`](packaging/rustproxy-filesystem-access.md)). +Opisuje bezpieczne przyznanie grupy lub ACL, odświeżenie grup przez restart, +obsługę certyfikatów Certbot i ich symlinków oraz ograniczenie aplikacyjne: +zewnętrzne PEM wymagają zaufanych katalogów właściciela `root`/`rustproxy`. +Pakiet nie przypisuje automatycznie szerokich grup do użytkownika usługi, +nie zmienia uprawnień witryn ani nie nadpisuje istniejących drop-inów. + +Test pakowania, bez podpisywania i kompilacji Rust: + +```sh +./tests/linux-path-access.sh +``` ## Rotacja logów RustProxy @@ -258,7 +284,7 @@ Pakowanie Linux ma `PACKAGING_REVISION=4`, a natywne FreeBSD `FREEBSD_NATIVE_PAC **Uwaga FreeBSD:** `newsyslog` działa przez zmianę nazwy pliku, nie ma `copytruncate`. Pliki JSONL i inne logi otwarte bezpośrednio przez aplikację muszą być przez nią ponownie otwierane po rotacji. W przeciwnym razie proces może pisać do poprzedniego (przeniesionego) pliku; zachowanie nie jest identyczne z Linux. Reguły są instalowane, ale na działającym OPNsense należy zweryfikować sposób otwierania plików przez RustProxy. -Bieżąca rewizja pakowania Linux z obsługą Docker Agenta wynosi `PACKAGING_REVISION=4` i `OPNSENSE_PACKAGING_REVISION=4`; nowy kanał używa `FREEBSD_NATIVE_PACKAGING_REVISION=1`. `check-and-build.sh` sprawdza osobno rewizje pakowania Linux, OPNsense i FreeBSD native; nowa wersja samej wtyczki przebudowuje **tylko OPNsense**, nawet przy niezmienionym SHA aplikacji. Rewizja pakietu PKG zwiększy się automatycznie. W przypadku zmiany rewizji Linux analogicznie odświeżane są DEB/RPM. Konfiguracja w `/etc/logrotate.d/rustproxy` jest oznaczona jako `noreplace`, więc lokalna wersja zmodyfikowana przez administratora może zostać zachowana (zależnie od menedżera pakietów). +Bieżąca rewizja pakowania Linux wynosi `PACKAGING_REVISION=5` i `OPNSENSE_PACKAGING_REVISION=4`; nowy kanał używa `FREEBSD_NATIVE_PACKAGING_REVISION=1`. `check-and-build.sh` sprawdza osobno rewizje pakowania Linux, OPNsense i FreeBSD native; nowa wersja samej wtyczki przebudowuje **tylko OPNsense**, nawet przy niezmienionym SHA aplikacji. Rewizja pakietu PKG zwiększy się automatycznie. W przypadku zmiany rewizji Linux analogicznie odświeżane są DEB/RPM. Konfiguracja w `/etc/logrotate.d/rustproxy` jest oznaczona jako `noreplace`, więc lokalna wersja zmodyfikowana przez administratora może zostać zachowana (zależnie od menedżera pakietów). Kontrola po zainstalowaniu: @@ -314,6 +340,7 @@ curl -f http://127.0.0.1:18766/healthz ```bash ./tests/static.sh +./tests/linux-path-access.sh ./tests/check-rustproxy-source.sh /sciezka/do/RustProxy ``` @@ -399,6 +426,7 @@ Pakietowe testy offline: ```sh ./tests/static.sh +./tests/linux-path-access.sh ./tests/opnsense-plugin.sh ./tests/log-rotation.sh ./tests/freebsd-native.sh diff --git a/SHA256SUMS b/SHA256SUMS index 8187e12..ce881a6 100644 --- a/SHA256SUMS +++ b/SHA256SUMS @@ -1,6 +1,6 @@ -182435de9baadbda78d22b4b84a2c61063b3b1c01f60fbe04523d00d9de814a2 .env.example +3d666acb470e1755b030e6316e43c28116a0a283ddd3d447c89c5d49349e58a8 .env.example 17298e7669ed82b4c98abfce803e1a3f0a265b0abdc2567bf1316aa15e98aad3 Dockerfile -6f52356e3aca7ce12d81fe04b1cc27c6153d66af8414870399e9fa962b71d84b README.md +1124acac6c0fa0bdd462639a0256e04c432adcf6b5c5399fe7a26d58030115bf README.md e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/.gitkeep 654795adee13eb3b86f01b32f4e87ee628e3154a7f04923f7226756136be5fd7 docker-compose.yml e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 gpg/.gitkeep @@ -32,6 +32,7 @@ d4fc4b261c8252afbc6f403f828656cb7a49174de8cd239a233f5475d4cfeacd opnsense/plugi 43004e4dd882bb89b35cfd5ee7c0a844de2ed4f5833bd5c4e57b61f4fd124119 packaging/freebsd/+PRE_DEINSTALL 173c204b5a3071cef2563881ac9f52437141a3303bc5c3f3a50fd637a045b4cf packaging/freebsd/rustproxy.newsyslog c8dde46217c008bfc31420c19ecbd31e3db6d71ee48f99be856ad40ca3c7f490 packaging/freebsd/rustproxy.rc +79122afbd5964a87c0d933abe8b34745d4189e2a586d5918a82efbb9b2f50864 packaging/rustproxy-filesystem-access.md 0f7970888117b4bc810ad3f6abc6796f189d34e4863875b2501785e689471be6 packaging/rustproxy.logrotate eee3cb8959cb90cd1b5b320ec4674892e5d1a987049d1028dae39c3bdc9f3dc7 scripts/apply-freebsd-patches.sh 33246bb1645d443b0554559e0140695a3e8aafb316c84a90ae670eb2941dce23 scripts/build-freebsd-release.sh @@ -47,16 +48,18 @@ cba164bde8967045bfb76de7eff46d4407dfb7ed2bc861fbc600782551d1629e scripts/ensure 80f49932346268f2cd63cbfe4814c666cd665661e82c25f2e99883929ecae0c0 scripts/generate-opnsense-index.sh a21afa240ca0869c2f92af7a73899f270361b2b8677b897646479452185196fe scripts/generate-opnsense-repo.py 40186599540f6886188abda5dac72bc85d15b9b71ce20788061131bfcd519cb7 scripts/init-storage.sh -97929a54424faf1827b2e75919cb1ed26fe5614ef96df77e540738e6340ea137 scripts/lib.sh +d078c62c54464095e7ddd2071c58a2ac3abd972f3aaa5b4bad0596735eca170c scripts/lib.sh +de4fbbb973d9cddb170ef1e9f4f6f7896018ed5e6184594c9636c0215adf5233 scripts/normalize-systemd-unit.py 0eca8ef60f15dce0c5651fc8a55de10e0d9a11f3816f04153608abcbe2e2e1d8 scripts/package-freebsd.sh b098fa629f9fcc1a54baab3d46b28721eb582c314e084b3210259bf157c124a6 scripts/package-opnsense.sh 4ad289bc2c0e400b39a8d1bcf86395d7576c88e71b678ba8b893ccb72ac35465 scripts/prepare-agent-package.sh -6210bb3d5732c66b181db02f4d54cd9d2653b969ad6afb48356248a6930b4b34 scripts/prepare-package.sh +46908bfac309688340be72edded60efaecd765aa151f520a2491952619f53a43 scripts/prepare-package.sh 56b90f4844b55cdfeba341e9d0133114bd28046542611a38c996cc0bb8c5e2a2 start.sh f0bde843d75330bc49e3ab5547e739d8fbda8d74d0b12dfdbdb7648847e91e88 tests/check-rustproxy-source.sh a55cc19cba2729ea3e8d82e8e5e90d734ba77c0e0d3f56a1b432e2e7e5316dc5 tests/docker-agent-linux.sh e82c52ef15183a539e96909a39e44db68c8301543950e145008b563524b0b9b9 tests/freebsd-native.sh -5f84d297b9e673b347af6ae2d0a311e6cd699e0103822e2e0561ae4613b3503b tests/log-rotation.sh +84e9822fe9bbf87dd574b32fc2df5eebf317d3f26677a46035a49eb16044a4c1 tests/linux-path-access.sh +c5781dd2335cc85e72daffe807c7ba24481b6c2e8bc95f4012be4ea1baeac9a8 tests/log-rotation.sh aab57cd66dc157dc2359d8d57612c174570a7ea09fe753428dce8faad231ba3c tests/opnsense-plugin.sh -0ceaa84d06e7b5fcfb8cab6c76db7d0a3e6f7baa673e3a1b634be2fdca171fbf tests/static.sh +1227d503f784f7765c5950b603e4258ea155c2fdc3779886a7b0c403320be2a8 tests/static.sh c8de9ebbf51e017643d0735f27b960a6cf3c9b7122f6de32858f6b7dcc7ba9e9 tests/toolchains.sh diff --git a/packaging/rustproxy-filesystem-access.md b/packaging/rustproxy-filesystem-access.md new file mode 100644 index 0000000..06bb177 --- /dev/null +++ b/packaging/rustproxy-filesystem-access.md @@ -0,0 +1,85 @@ +# RustProxy (DEB/RPM): dostęp do katalogów WWW i certyfikatów + +Jednostka z paczki uruchamia `rust-proxy` jako `rustproxy:rustproxy`, z +`ProtectSystem=strict` i `ProtectHome=read-only`. Katalogi `/home`, `/var/www`, +`/srv` i zewnętrzne PEM są **dostępne do odczytu**, jeżeli pozwalają na to +standardowe uprawnienia użytkownika, grupy lub POSIX ACL. Nie jest potrzebny +`systemctl edit` ani dopisywanie `ReadOnlyPaths=`. Dostęp do zapisu poza +`/var/lib/rustproxy`, `/var/log/rustproxy` i dopuszczonym `/etc/rustproxy` +pozostaje zablokowany przez systemd. Nie zmieniaj `ProtectSystem` na `false`. + +## 1. Katalog strony WWW w /home + +Przykład (zastąp `klient` i ścieżkę nazwą swojej strony): + +```bash +sudo setfacl -m u:rustproxy:--x /home/klient +sudo setfacl -R -m u:rustproxy:r-X /home/klient/public_html +sudo -u rustproxy test -r /home/klient/public_html/index.html && echo OK +sudo systemctl restart rustproxy +``` + +`--x` pozwala jedynie przejść przez prywatny katalog domowy; `r-X` nadaje +odczyt plików i przechodzenie przez podkatalogi. Dla nowo tworzonych plików +ustaw także odpowiednie domyślne ACL na katalogach witryny lub pilnuj grupy +oraz umask narzędzia publikującego. + +## 2. Grupa zamiast ACL (np. /var/www) + +Nadaj witrynie **osobną, możliwie wąską grupę**, np. `web-readers`, zapewnij +jej `r` do plików i `x` do katalogów, a potem: + +```bash +sudo usermod -aG web-readers rustproxy +id rustproxy +sudo systemctl restart rustproxy +``` + +Grupy dodatkowe użytkownika `rustproxy` są automatycznie uwzględniane przez +systemd przy starcie usługi. Nie wpisuj ich w `SupplementaryGroups=` i nie +udostępniaj plików całemu światu (`chmod 777` / `chmod -R 755` nie są wymagane). +Restart jest konieczny po zmianie grup, ponieważ uruchomiony proces nie +pobiera nowej listy grup samoczynnie. + +## 3. Zewnętrzny certyfikat z Certbot / Let's Encrypt + +Dla `example.com` nadaj jedynie możliwość przechodzenia przez katalogi i +odczytu bieżących rzeczywistych plików (symlinki `live` prowadzą do `archive`): + +```bash +sudo setfacl -m u:rustproxy:--x /etc/letsencrypt /etc/letsencrypt/live \ + /etc/letsencrypt/live/example.com /etc/letsencrypt/archive \ + /etc/letsencrypt/archive/example.com +sudo setfacl -m u:rustproxy:r-- "$(readlink -f /etc/letsencrypt/live/example.com/fullchain.pem)" +sudo setfacl -m u:rustproxy:r-- "$(readlink -f /etc/letsencrypt/live/example.com/privkey.pem)" +sudo systemctl reload rustproxy +``` + +**Ważne:** RustProxy celowo wymaga, aby zewnętrzne pliki certyfikatu/klucza oraz +wszystkie rzeczywiste katalogi nadrzędne były własnością `root` albo +`rustproxy`, nie były zapisywalne przez grupę/innych, a klucz prywatny nie był +czytelny przez innych. Katalog WWW może być własnością innego użytkownika; +ścieżka do PEM ma ostrzejszą walidację. Nie zakładaj, że certyfikat trzymany +pod `/home/klient` z katalogiem należącym do `klient` zadziała po samym ACL. +Umieść go w zaufanym katalogu np. `/etc/letsencrypt` i nadaj odczyt ACL/grupą. +Po odnowieniu certyfikatu (nowy plik w `archive/`) odtwórz uprawnienia przez +hook odnowienia, a potem przeładuj RustProxy. + +## 4. Diagnostyka + +```bash +systemctl show rustproxy -p User -p Group -p ProtectHome -p ProtectSystem -p ReadWritePaths +systemctl cat rustproxy +namei -l /home/klient/public_html/index.html +sudo -u rustproxy test -r /home/klient/public_html/index.html && echo OK +journalctl -u rustproxy -n 60 --no-pager +``` + +Test `sudo -u` potwierdza prawa POSIX **poza** sandboxem systemd. Jeśli działa, +a RustProxy dalej nie czyta pliku, sprawdź aktywną jednostkę/drop-in, politykę +SELinux lub AppArmor (`ausearch -m avc -ts recent` na SELinux), symlinki i +system plików. Ręczna jednostka w `/etc/systemd/system/rustproxy.service` ma +pierwszeństwo przed pakietową w `/usr/lib/systemd/system` i nie zostanie +nadpisana automatycznie. Web Server ma własną politykę symlinków. ACL nie +pozwoli zapisać strony lub uploadów pod `/home`/`/var/www`, dopóki nie zostanie +jawnie dopuszczony zapis w sandboxie; zapis aplikacji trzymaj osobno. diff --git a/scripts/lib.sh b/scripts/lib.sh index 5054c09..e4d8b46 100755 --- a/scripts/lib.sh +++ b/scripts/lib.sh @@ -2,7 +2,7 @@ set -Eeuo pipefail # Increment when package payload/installation semantics change without a source commit. -PACKAGING_REVISION=4 +PACKAGING_REVISION=5 OPNSENSE_PACKAGING_REVISION=4 FREEBSD_NATIVE_PACKAGING_REVISION=1 diff --git a/scripts/normalize-systemd-unit.py b/scripts/normalize-systemd-unit.py new file mode 100644 index 0000000..7de9cb0 --- /dev/null +++ b/scripts/normalize-systemd-unit.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +"""Normalize the packaged RustProxy systemd unit for Linux DEB/RPM.""" + +from pathlib import Path +import re +import sys +path = Path(sys.argv[1]) +name = sys.argv[2] +text = path.read_text() +if not re.search(r'^\[Service\][ \t]*$', text, re.M): + raise SystemExit('unit has no [Service] section') +# This normalization also covers older/source-provided debian/*.service units: +# ProtectHome=true masks /home completely, so neither chgrp nor setfacl can +# make web roots/certificate files there readable. Keep the directories +# read-only, in addition to the existing strict filesystem sandbox. +if re.search(r'^[ \t]*ProtectHome[ \t]*=', text, re.M): + text = re.sub(r'^[ \t]*ProtectHome[ \t]*=.*$', 'ProtectHome=read-only', + text, flags=re.M) +else: + text = re.sub(r'^(\[Service\][ \t]*\n)', + lambda m: m.group(1) + 'ProtectHome=read-only\n', + text, count=1, flags=re.M) +if not re.search(r'^ExecReload=', text, re.M): + if not re.search(r'^ExecStart=', text, re.M): + raise SystemExit('unit has no ExecStart; cannot attach reload') + text = re.sub( + r'^(ExecStart=[^\n]*\n)', + lambda m: m.group(1) + + f'ExecReload=/usr/bin/{name} --data-dir /var/lib/rustproxy --admin-listen 127.0.0.1:9090 reload\n', + text, + count=1, + flags=re.M, + ) +if re.search(r'^ProtectSystem=strict\s*$', text, re.M) and not re.search( + r'^ReadWritePaths=.*(?:^|[ \t])-?/etc/rustproxy(?:[ \t]|$)', text, re.M +): + text = re.sub( + r'^(ProtectSystem=strict\s*\n)', + lambda m: m.group(1) + 'ReadWritePaths=-/etc/rustproxy\n', + text, count=1, flags=re.M, + ) +path.write_text(text) diff --git a/scripts/prepare-package.sh b/scripts/prepare-package.sh index 2e3f64e..460a874 100755 --- a/scripts/prepare-package.sh +++ b/scripts/prepare-package.sh @@ -14,6 +14,7 @@ install -D -m 0755 "$BINARY_PATH" "$PKG/payload/usr/bin/$BINARY_NAME" SERVICE_SRC="$SRC/debian/${PACKAGE_NAME}.service" ENV_SRC="$SRC/debian/${PACKAGE_NAME}.environment" LOGROTATE_SRC="$BUILDER_ROOT/packaging/rustproxy.logrotate" +ACCESS_GUIDE_SRC="$BUILDER_ROOT/packaging/rustproxy-filesystem-access.md" TMPFILES_SRC="$SRC/debian/${PACKAGE_NAME}.tmpfiles" POSTINST_SRC="$SRC/debian/${PACKAGE_NAME}.postinst" @@ -52,7 +53,9 @@ ProtectSystem=strict LogsDirectory=rustproxy LogsDirectoryMode=0700 ReadWritePaths=/var/lib/rustproxy /var/log/rustproxy -ProtectHome=true +# Allow ACL/group-controlled read access to web roots and external certificates +# under /home; ProtectSystem=strict still makes all other paths read-only. +ProtectHome=read-only ProtectKernelTunables=true ProtectKernelModules=true ProtectKernelLogs=true @@ -71,40 +74,16 @@ fi # Match the 0.34.x CLI. Avoid changing upstream units that already provide # their own ExecReload. Linux package revision forces rebuilding old binaries. -python3 - "$PKG/payload/usr/lib/systemd/system/${PACKAGE_NAME}.service" "$BINARY_NAME" <<'PY_UNIT' -from pathlib import Path -import re -import sys -path = Path(sys.argv[1]) -name = sys.argv[2] -text = path.read_text() -if not re.search(r'^ExecReload=', text, re.M): - if not re.search(r'^ExecStart=', text, re.M): - raise SystemExit('unit has no ExecStart; cannot attach reload') - text = re.sub( - r'^(ExecStart=[^\n]*\n)', - lambda m: m.group(1) + - f'ExecReload=/usr/bin/{name} --data-dir /var/lib/rustproxy --admin-listen 127.0.0.1:9090 reload\n', - text, - count=1, - flags=re.M, - ) -if re.search(r'^ProtectSystem=strict\s*$', text, re.M) and not re.search( - r'^ReadWritePaths=.*(?:^|[ \t])-?/etc/rustproxy(?:[ \t]|$)', text, re.M -): - text = re.sub( - r'^(ProtectSystem=strict\s*\n)', - lambda m: m.group(1) + 'ReadWritePaths=-/etc/rustproxy\n', - text, count=1, flags=re.M, - ) -path.write_text(text) -PY_UNIT +python3 "$BUILDER_ROOT/scripts/normalize-systemd-unit.py" \ + "$PKG/payload/usr/lib/systemd/system/${PACKAGE_NAME}.service" "$BINARY_NAME" if [[ -r "$ENV_SRC" ]]; then install -D -m 0640 "$ENV_SRC" "$PKG/payload/etc/${PACKAGE_NAME}/environment" fi [[ -r "$LOGROTATE_SRC" ]] || fatal "missing bundled logrotate configuration: $LOGROTATE_SRC" install -D -m 0644 "$LOGROTATE_SRC" "$PKG/payload/etc/logrotate.d/${PACKAGE_NAME}" +[[ -r "$ACCESS_GUIDE_SRC" ]] || fatal "missing Linux filesystem access guide: $ACCESS_GUIDE_SRC" +install -D -m 0644 "$ACCESS_GUIDE_SRC" "$PKG/payload/usr/share/doc/${PACKAGE_NAME}/FILESYSTEM_ACCESS.md" if [[ -r "$TMPFILES_SRC" ]]; then install -D -m 0644 "$TMPFILES_SRC" "$PKG/payload/usr/lib/tmpfiles.d/${PACKAGE_NAME}.conf" fi @@ -214,6 +193,7 @@ add_content "$PKG/payload/usr/bin/$BINARY_NAME" "/usr/bin/$BINARY_NAME" 0755 add_content "$PKG/payload/usr/lib/systemd/system/${PACKAGE_NAME}.service" "/usr/lib/systemd/system/${PACKAGE_NAME}.service" 0644 [[ -f "$PKG/payload/etc/${PACKAGE_NAME}/environment" ]] && add_content "$PKG/payload/etc/${PACKAGE_NAME}/environment" "/etc/${PACKAGE_NAME}/environment" 0640 'config|noreplace' add_content "$PKG/payload/etc/logrotate.d/${PACKAGE_NAME}" "/etc/logrotate.d/${PACKAGE_NAME}" 0644 'config|noreplace' +add_content "$PKG/payload/usr/share/doc/${PACKAGE_NAME}/FILESYSTEM_ACCESS.md" "/usr/share/doc/${PACKAGE_NAME}/FILESYSTEM_ACCESS.md" 0644 [[ -f "$PKG/payload/usr/lib/tmpfiles.d/${PACKAGE_NAME}.conf" ]] && add_content "$PKG/payload/usr/lib/tmpfiles.d/${PACKAGE_NAME}.conf" "/usr/lib/tmpfiles.d/${PACKAGE_NAME}.conf" 0644 [[ -f "$PKG/payload/usr/share/${PACKAGE_NAME}/grafana/rustproxy-overview.json" ]] && add_content "$PKG/payload/usr/share/${PACKAGE_NAME}/grafana/rustproxy-overview.json" "/usr/share/${PACKAGE_NAME}/grafana/rustproxy-overview.json" 0644 [[ -f "$PKG/payload/usr/share/doc/${PACKAGE_NAME}/grafana/README.md" ]] && add_content "$PKG/payload/usr/share/doc/${PACKAGE_NAME}/grafana/README.md" "/usr/share/doc/${PACKAGE_NAME}/grafana/README.md" 0644 diff --git a/tests/linux-path-access.sh b/tests/linux-path-access.sh new file mode 100755 index 0000000..315fb1a --- /dev/null +++ b/tests/linux-path-access.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +set -Eeuo pipefail +ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +WORK=$(mktemp -d) +trap 'rm -rf "$WORK"' EXIT +mkdir -p "$WORK/source" +printf '#!/bin/sh\nexit 0\n' > "$WORK/rust-proxy" +chmod 0755 "$WORK/rust-proxy" + +export BUILDER_ROOT="$ROOT" PACKAGE_NAME=rustproxy BINARY_NAME=rust-proxy +export VERSION=1.0.0 PACKAGE_RELEASE=1 DESCRIPTION=RustProxy LICENSE=UNKNOWN +export MAINTAINER=test@example.invalid HOMEPAGE=https://example.invalid +export GPG_PRIVATE_KEY_FILE=/nonexistent/test.gpg + +check_unit() { + local unit=$1 + # A read-only home does not conceal /home, unlike ProtectHome=true/tmpfs. + [[ $(grep -c '^ProtectHome=read-only$' "$unit") -eq 1 ]] + ! grep -Eq '^ProtectHome=(true|yes|tmpfs|false|no)$' "$unit" + grep -Fxq 'ProtectSystem=strict' "$unit" + grep -Fxq 'User=rustproxy' "$unit" + grep -Fxq 'Group=rustproxy' "$unit" + grep -Fxq 'NoNewPrivileges=true' "$unit" + grep -Fxq 'CapabilityBoundingSet=CAP_NET_BIND_SERVICE' "$unit" + grep -Fq 'ReadWritePaths=-/etc/rustproxy' "$unit" + grep -Fxq 'ExecReload=/usr/bin/rust-proxy --data-dir /var/lib/rustproxy --admin-listen 127.0.0.1:9090 reload' "$unit" +} + +# Current RustProxy archive has no debian unit; both nFPM targets use fallback. +"$ROOT/scripts/prepare-package.sh" "$WORK/source" "$WORK/fallback" "$WORK/rust-proxy" +check_unit "$WORK/fallback/payload/usr/lib/systemd/system/rustproxy.service" +for platform in deb rpm; do + grep -Fq '/usr/lib/systemd/system/rustproxy.service' "$WORK/fallback/nfpm-$platform.yaml" + grep -Fq '/usr/share/doc/rustproxy/FILESYSTEM_ACCESS.md' "$WORK/fallback/nfpm-$platform.yaml" +done +cmp "$ROOT/packaging/rustproxy-filesystem-access.md" \ + "$WORK/fallback/payload/usr/share/doc/rustproxy/FILESYSTEM_ACCESS.md" + +# Some source releases ship their own unit. Normalize its outdated home policy +# rather than letting it override the new package default. +mkdir -p "$WORK/source/debian" +cat > "$WORK/source/debian/rustproxy.service" <<'UNIT' +[Unit] +Description=RustProxy unit supplied with a source release + +[Service] +User=rustproxy +Group=rustproxy +ExecStart=/usr/bin/rust-proxy --data-dir /var/lib/rustproxy serve +ProtectSystem=strict +ReadWritePaths=/var/lib/rustproxy /var/log/rustproxy +ProtectHome=true +NoNewPrivileges=true +CapabilityBoundingSet=CAP_NET_BIND_SERVICE + +[Install] +WantedBy=multi-user.target +UNIT +python3 "$ROOT/scripts/normalize-systemd-unit.py" "$WORK/source/debian/rustproxy.service" rust-proxy +check_unit "$WORK/source/debian/rustproxy.service" + +# A custom unit without ProtectHome must also get the new safe policy. +sed -i '/^ProtectHome=/d' "$WORK/source/debian/rustproxy.service" +python3 "$ROOT/scripts/normalize-systemd-unit.py" "$WORK/source/debian/rustproxy.service" rust-proxy +check_unit "$WORK/source/debian/rustproxy.service" + +echo 'Linux DEB/RPM external path access policy: OK' diff --git a/tests/log-rotation.sh b/tests/log-rotation.sh index cc13841..30cc223 100755 --- a/tests/log-rotation.sh +++ b/tests/log-rotation.sh @@ -65,6 +65,8 @@ cmp "$ROOT/packaging/rustproxy.logrotate" "$WORK/linux/payload/etc/logrotate.d/r UNIT="$WORK/linux/payload/usr/lib/systemd/system/rustproxy.service" grep -Fq 'ExecReload=/usr/bin/rust-proxy --data-dir /var/lib/rustproxy --admin-listen 127.0.0.1:9090 reload' "$UNIT" grep -Fq 'ReadWritePaths=-/etc/rustproxy' "$UNIT" +grep -Fxq 'ProtectHome=read-only' "$UNIT" +grep -Fxq 'ProtectSystem=strict' "$UNIT" for metadata in nfpm-deb.yaml nfpm-rpm.yaml; do grep -Fq '/etc/logrotate.d/rustproxy' "$WORK/linux/$metadata" grep -Fq 'config|noreplace' "$WORK/linux/$metadata" diff --git a/tests/static.sh b/tests/static.sh index d8eb2fa..a45b6d4 100755 --- a/tests/static.sh +++ b/tests/static.sh @@ -22,7 +22,7 @@ grep -q 'FREEBSD_NATIVE_PACKAGING_REVISION=1' "$ROOT/scripts/lib.sh" grep -Fq 'FREEBSD_NATIVE_ENABLED: "${FREEBSD_NATIVE_ENABLED:-0}"' "$ROOT/docker-compose.yml" grep -Fq 'location /freebsd/' "$ROOT/http/nginx.conf" grep -Fq 'FREEBSD_NATIVE_ENABLED=0' "$ROOT/.env.example" -grep -Fq 'ExecReload=/usr/bin/' "$ROOT/scripts/prepare-package.sh" +grep -Fq 'ExecReload=/usr/bin/' "$ROOT/scripts/normalize-systemd-unit.py" grep -q 'OPNsense branch will still run' "$ROOT/scripts/build-release.sh" # Linux tests gate Linux packaging only. @@ -33,12 +33,13 @@ grep -Fq 'AGENT_DEB_FILE=' "$ROOT/scripts/build-linux-release.sh" grep -Fq 'AGENT_RPM_FILE=' "$ROOT/scripts/build-linux-release.sh" grep -Fq 'rustproxy-docker-agent' "$ROOT/scripts/generate-index.sh" grep -Fq 'COPY --from=go-toolchain' "$ROOT/Dockerfile" -grep -q 'PACKAGING_REVISION=4' "$ROOT/scripts/lib.sh" +grep -q 'PACKAGING_REVISION=5' "$ROOT/scripts/lib.sh" grep -Fq 'ensure-rust-toolchain.sh' "$ROOT/scripts/build-linux-release.sh" grep -Fq 'ensure-rust-toolchain.sh' "$ROOT/scripts/build-opnsense.sh" grep -Fq 'nfpm package' "$ROOT/scripts/build-linux-release.sh" grep -q 'aptly .*publish' "$ROOT/scripts/build-linux-release.sh" grep -q 'createrepo_c' "$ROOT/scripts/build-linux-release.sh" +[[ -x "$ROOT/tests/linux-path-access.sh" ]] ! grep -q 'cargo test' "$ROOT/scripts/build-opnsense-release.sh" ! grep -q 'cargo test' "$ROOT/scripts/build-opnsense.sh"