From: Christoph Liebender Subject: www/anubis: add unveil(2) restrictions To: ports@openbsd.org Date: Mon, 19 May 2025 18:26:06 +0200 Hi ports@, this patch adds unveil(2) sandboxing to www/anubis. I think it is probably a good idea in this case, as anubis is intended to "take the beating" in a reverse-proxy setup. Because of this and the fact that the daemon runs as the www user (which is not a daemon user), the things that it can do should be restricted. Initially, I suggested both pledge(2) and unveil(2), but stu@ argued that pledge would be too much to support reliably. This is why this patch only adds calls to unveil. The call site is before the daemon starts listening. So any kind of configuration parsing is already done. If I understand correctly, the only things then required are, depending on configuration: - /etc/{resolv.conf, hosts, ssl/{cert.pem, openssl.cnf}} with read permissions, when TARGET is set to a hostname - read-write on TARGET if it points is a unix socket - create on BIND if BIND_NETWORK is unix (same for METRICS_BIND) comments, testers, ok? - Christoph diff --git a/www/anubis/Makefile b/www/anubis/Makefile index 8e352641089..0f2d0beac90 100644 --- a/www/anubis/Makefile +++ b/www/anubis/Makefile @@ -3,6 +3,7 @@ COMMENT= proof-of-work proxy to protect web resources from scrapers V= 1.18.0 DISTNAME= anubis-src-vendor-npm-$V PKGNAME= anubis-$V +REVISION= 0 CATEGORIES= www diff --git a/www/anubis/patches/patch-cmd_anubis_main_go b/www/anubis/patches/patch-cmd_anubis_main_go new file mode 100644 index 00000000000..5cd7aa9c40d --- /dev/null +++ b/www/anubis/patches/patch-cmd_anubis_main_go @@ -0,0 +1,64 @@ +Index: cmd/anubis/main.go +--- cmd/anubis/main.go.orig ++++ cmd/anubis/main.go +@@ -37,6 +37,27 @@ import ( + "github.com/prometheus/client_golang/prometheus/promhttp" + ) + ++// #include ++// #include ++import "C" ++import "unsafe" ++ ++func unveil(path string, permissions string) { ++ if path == "" && permissions == "" { ++ C.unveil(nil, nil) ++ slog.Debug("unveiled") ++ return ++ } ++ ++ cpath := C.CString(path) ++ cpermissions := C.CString(permissions) ++ defer C.free(unsafe.Pointer(cpath)) ++ defer C.free(unsafe.Pointer(cpermissions)) ++ ++ C.unveil(cpath, cpermissions) ++ slog.Debug("unveil", "path", path, "perms", permissions) ++} ++ + var ( + basePrefix = flag.String("base-prefix", "", "base prefix (root URL) the application is served under e.g. /myapp") + bind = flag.String("bind", ":8923", "network address to bind HTTP to") +@@ -335,6 +356,32 @@ func main() { + log.Printf("cannot shut down: %v", err) + } + }() ++ ++ bindUnix := *bindNetwork == "unix" ++ metricsBindUnix := *metricsBindNetwork == "unix" ++ targetUnix := strings.HasPrefix(*target, "unix://") ++ targetIP := net.ParseIP(*target) != nil ++ if bindUnix { ++ unveil(*bind, "c") ++ } ++ if metricsBindUnix { ++ unveil(*metricsBind, "c") ++ } ++ if targetUnix { ++ unveil(strings.TrimPrefix(*target, "unix://"), "rw") ++ } ++ if !targetUnix && !targetIP { ++ rpaths := []string{ ++ "/etc/resolv.conf", ++ "/etc/hosts", ++ "/etc/ssl/openssl.cnf", ++ "/etc/ssl/cert.pem", ++ } ++ for _, rpath := range rpaths { ++ unveil(rpath, "r") ++ } ++ } ++ unveil("", "") + + if err := srv.Serve(listener); !errors.Is(err, http.ErrServerClosed) { + log.Fatal(err)