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)

Reply via email to