mirror of
https://github.com/whyvl/wireproxy.git
synced 2025-04-29 19:01:42 +02:00
Limit wireproxy's permissions with landlock
This commit is contained in:
parent
eccf83a0cf
commit
728438cc33
3 changed files with 111 additions and 20 deletions
|
@ -3,11 +3,14 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/landlock-lsm/go-landlock/landlock"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/akamensky/argparse"
|
"github.com/akamensky/argparse"
|
||||||
|
@ -21,22 +24,22 @@ const daemonProcess = "daemon-process"
|
||||||
|
|
||||||
var version = "1.0.8-dev"
|
var version = "1.0.8-dev"
|
||||||
|
|
||||||
// attempts to pledge and panic if it fails
|
func panicIfError(err error) {
|
||||||
// this does nothing on non-OpenBSD systems
|
|
||||||
func pledgeOrPanic(promises string) {
|
|
||||||
err := protect.Pledge(promises)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// attempts to pledge and panic if it fails
|
||||||
|
// this does nothing on non-OpenBSD systems
|
||||||
|
func pledgeOrPanic(promises string) {
|
||||||
|
panicIfError(protect.Pledge(promises))
|
||||||
|
}
|
||||||
|
|
||||||
// attempts to unveil and panic if it fails
|
// attempts to unveil and panic if it fails
|
||||||
// this does nothing on non-OpenBSD systems
|
// this does nothing on non-OpenBSD systems
|
||||||
func unveilOrPanic(path string, flags string) {
|
func unveilOrPanic(path string, flags string) {
|
||||||
err := protect.Unveil(path, flags)
|
panicIfError(protect.Unveil(path, flags))
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the executable path via syscalls or infer it from argv
|
// get the executable path via syscalls or infer it from argv
|
||||||
|
@ -48,6 +51,94 @@ func executablePath() string {
|
||||||
return programPath
|
return programPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lock(stage string) {
|
||||||
|
switch stage {
|
||||||
|
case "boot":
|
||||||
|
exePath := executablePath()
|
||||||
|
// OpenBSD
|
||||||
|
unveilOrPanic("/", "r")
|
||||||
|
unveilOrPanic(exePath, "x")
|
||||||
|
// only allow standard stdio operation, file reading, networking, and exec
|
||||||
|
// also remove unveil permission to lock unveil
|
||||||
|
pledgeOrPanic("stdio rpath inet dns proc exec")
|
||||||
|
// Linux
|
||||||
|
panicIfError(landlock.V4.BestEffort().RestrictPaths(
|
||||||
|
landlock.RODirs("/"),
|
||||||
|
))
|
||||||
|
case "boot-daemon":
|
||||||
|
case "read-config":
|
||||||
|
// OpenBSD
|
||||||
|
pledgeOrPanic("stdio rpath inet dns")
|
||||||
|
case "ready":
|
||||||
|
// no file access is allowed from now on, only networking
|
||||||
|
// OpenBSD
|
||||||
|
pledgeOrPanic("stdio inet dns")
|
||||||
|
// Linux
|
||||||
|
net.DefaultResolver.PreferGo = true // needed to lock down dependencies
|
||||||
|
panicIfError(landlock.V4.BestEffort().RestrictPaths(
|
||||||
|
landlock.ROFiles("/etc/resolv.conf"),
|
||||||
|
landlock.ROFiles("/dev/fd"),
|
||||||
|
landlock.ROFiles("/dev/zero"),
|
||||||
|
landlock.ROFiles("/dev/urandom"),
|
||||||
|
landlock.ROFiles("/etc/localtime"),
|
||||||
|
landlock.ROFiles("/proc/self/stat"),
|
||||||
|
landlock.ROFiles("/proc/self/status"),
|
||||||
|
landlock.ROFiles("/usr/share/locale"),
|
||||||
|
landlock.ROFiles("/proc/self/cmdline"),
|
||||||
|
landlock.ROFiles("/usr/share/zoneinfo"),
|
||||||
|
landlock.ROFiles("/proc/sys/kernel/version"),
|
||||||
|
landlock.ROFiles("/proc/sys/kernel/ngroups_max"),
|
||||||
|
landlock.ROFiles("/proc/sys/kernel/cap_last_cap"),
|
||||||
|
landlock.ROFiles("/proc/sys/vm/overcommit_memory"),
|
||||||
|
landlock.RWFiles("/dev/log"),
|
||||||
|
landlock.RWFiles("/dev/null"),
|
||||||
|
landlock.RWFiles("/dev/full"),
|
||||||
|
landlock.RWFiles("/dev/stdin"),
|
||||||
|
landlock.RWFiles("/dev/stdout"),
|
||||||
|
landlock.RWFiles("/dev/stderr"),
|
||||||
|
landlock.RWFiles("/proc/self/fd"),
|
||||||
|
))
|
||||||
|
default:
|
||||||
|
panic("invalid stage")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractPort(addr string) uint16 {
|
||||||
|
_, portStr, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := strconv.Atoi(portStr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint16(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lockNetwork(sections []wireproxy.RoutineSpawner, infoAddr *string) {
|
||||||
|
var rules []landlock.Rule
|
||||||
|
if infoAddr != nil {
|
||||||
|
rules = append(rules, landlock.BindTCP(extractPort(*infoAddr)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, section := range sections {
|
||||||
|
switch section := section.(type) {
|
||||||
|
case *wireproxy.TCPServerTunnelConfig:
|
||||||
|
rules = append(rules, landlock.ConnectTCP(extractPort(section.Target)))
|
||||||
|
case *wireproxy.HTTPConfig:
|
||||||
|
rules = append(rules, landlock.BindTCP(extractPort(section.BindAddress)))
|
||||||
|
case *wireproxy.TCPClientTunnelConfig:
|
||||||
|
rules = append(rules, landlock.ConnectTCP(uint16(section.BindAddress.Port)))
|
||||||
|
case *wireproxy.Socks5Config:
|
||||||
|
rules = append(rules, landlock.BindTCP(extractPort(section.BindAddress)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panicIfError(landlock.V4.RestrictNet(rules...))
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
s := make(chan os.Signal, 1)
|
s := make(chan os.Signal, 1)
|
||||||
signal.Notify(s, syscall.SIGINT, syscall.SIGQUIT)
|
signal.Notify(s, syscall.SIGINT, syscall.SIGQUIT)
|
||||||
|
@ -59,18 +150,12 @@ func main() {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
exePath := executablePath()
|
exePath := executablePath()
|
||||||
unveilOrPanic("/", "r")
|
lock("boot")
|
||||||
unveilOrPanic(exePath, "x")
|
|
||||||
|
|
||||||
// only allow standard stdio operation, file reading, networking, and exec
|
|
||||||
// also remove unveil permission to lock unveil
|
|
||||||
pledgeOrPanic("stdio rpath inet dns proc exec")
|
|
||||||
|
|
||||||
isDaemonProcess := len(os.Args) > 1 && os.Args[1] == daemonProcess
|
isDaemonProcess := len(os.Args) > 1 && os.Args[1] == daemonProcess
|
||||||
args := os.Args
|
args := os.Args
|
||||||
if isDaemonProcess {
|
if isDaemonProcess {
|
||||||
// remove proc and exec if they are not needed
|
lock("boot-daemon")
|
||||||
pledgeOrPanic("stdio rpath inet dns")
|
|
||||||
args = []string{args[0]}
|
args = []string{args[0]}
|
||||||
args = append(args, os.Args[2:]...)
|
args = append(args, os.Args[2:]...)
|
||||||
}
|
}
|
||||||
|
@ -100,8 +185,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*daemon {
|
if !*daemon {
|
||||||
// remove proc and exec if they are not needed
|
lock("read-config")
|
||||||
pledgeOrPanic("stdio rpath inet dns")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conf, err := wireproxy.ParseConfig(*config)
|
conf, err := wireproxy.ParseConfig(*config)
|
||||||
|
@ -114,6 +198,8 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lockNetwork(conf.Routines, info)
|
||||||
|
|
||||||
if isDaemonProcess {
|
if isDaemonProcess {
|
||||||
os.Stdout, _ = os.Open(os.DevNull)
|
os.Stdout, _ = os.Open(os.DevNull)
|
||||||
os.Stderr, _ = os.Open(os.DevNull)
|
os.Stderr, _ = os.Open(os.DevNull)
|
||||||
|
@ -139,8 +225,7 @@ func main() {
|
||||||
logLevel = device.LogLevelSilent
|
logLevel = device.LogLevelSilent
|
||||||
}
|
}
|
||||||
|
|
||||||
// no file access is allowed from now on, only networking
|
lock("ready")
|
||||||
pledgeOrPanic("stdio inet dns")
|
|
||||||
|
|
||||||
tun, err := wireproxy.StartWireguard(conf.Device, logLevel)
|
tun, err := wireproxy.StartWireguard(conf.Device, logLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -15,6 +15,7 @@ require (
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.2 // indirect
|
||||||
|
github.com/landlock-lsm/go-landlock v0.0.0-20240216195629-efb66220540a // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
golang.org/x/crypto v0.19.0 // indirect
|
golang.org/x/crypto v0.19.0 // indirect
|
||||||
golang.org/x/net v0.21.0 // indirect
|
golang.org/x/net v0.21.0 // indirect
|
||||||
|
@ -22,4 +23,5 @@ require (
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
|
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
|
||||||
|
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69 // indirect
|
||||||
)
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -8,6 +8,8 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
|
github.com/landlock-lsm/go-landlock v0.0.0-20240216195629-efb66220540a h1:dz+a1MiMQksVhejeZwqJuzPawYQBwug74J8PPtkLl9U=
|
||||||
|
github.com/landlock-lsm/go-landlock v0.0.0-20240216195629-efb66220540a/go.mod h1:1NY/VPO8xm3hXw3f+M65z+PJDLUaZA5cu7OfanxoUzY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
|
@ -33,5 +35,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
|
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
|
||||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
|
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=
|
||||||
|
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69 h1:IdrOs1ZgwGw5CI+BH6GgVVlOt+LAXoPyh7enr8lfaXs=
|
||||||
|
kernel.org/pub/linux/libs/security/libcap/psx v1.2.69/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
|
||||||
suah.dev/protect v1.2.3 h1:aHeoNwZ9YPp64hrYaN0g0djNE1eRujgH63CrfRrUKdc=
|
suah.dev/protect v1.2.3 h1:aHeoNwZ9YPp64hrYaN0g0djNE1eRujgH63CrfRrUKdc=
|
||||||
suah.dev/protect v1.2.3/go.mod h1:n1R3XIbsnryKX7C1PO88i5Wgo0v8OTXm9K9FIKt4rfs=
|
suah.dev/protect v1.2.3/go.mod h1:n1R3XIbsnryKX7C1PO88i5Wgo0v8OTXm9K9FIKt4rfs=
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue