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 <unistd.h>
++// #include <stdlib.h>
++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)