Documentation ¶
Overview ¶
Package restrict is a library to secure your application. Targeting mainly OpenBSD and Linux (amd64) with musl-libc. On other operating systems the functions are no-ops to provide seamless compatibility besides Trust() and the Resolver() variants.
- Jail ability: Using Trust() and Resolver() enables your application to be independent from the systems typical rootCA bundle and allows the usage from system independent dns servers.
- Syscall allow-listing: Using Syscalls() gives you the ability to use the comfort of OpenBSD's pledge or seccomp on Linux in a pledge like variation.
- Restricting the filesystem view: Using Access()/AccessLock() lets you fine tune the accessibility of your filesystem with OpenBSD's unveil or Landlock on Linux in a unveil like mode.
We are only supporting the Landlock ABI version (3, 4 is not implmented yet) and seccomp API version (6+). Kernels before version 6.x (formerly 5.13) and without Landlock¹ as LSM and seccomp² are not supported and will print warnings via DefaultLogger or result in an immediate abort on errors (code 255).
Linux support is AMD64 only and musl-libc mainly. Happens to work with glibc as well. Glibc or other libc implementations are not tested or used in production by myself.
ARM64 and/or RISCV64 support may come later at some point.
¹ https://www.kernel.org/doc/html/latest/userspace-api/landlock.html ² https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html
Sets aka promises available on linux:
- stdio
- rpath
- wpath
- cpath
- dpath
- tmppath
- inet
- mcast (stub)
- fattr
- chown
- flock
- unix
- dns
- getpw
- sendfd
- recvfd
- tape (stub)
- tty
- proc
- exec
- prot_exec (openbsd only)
- settime
- ps
- vminfo
- id
- pf (openbsd only)
- route (stub)
- wroute (stub)
- audio
- video
- bpf
- unveil (alias landlock)
- error
See https://man.openbsd.org/pledge.2 for more info.
Linux only:
- capabilities
- namespaces
Index ¶
- Constants
- Variables
- func Access(path string, flags string) error
- func AccessCerts() error
- func AccessLock() error
- func Debug()
- func Disable() error
- func Expert()
- func Resolver(dns string) error
- func ResolverTCP(dns string) error
- func ResolverTLS(dns string) error
- func ResolverX(dns, port string, dot, udp bool) error
- func State()
- func Syscalls(allow string) error
- func SyscallsExec(allow string) errordeprecated
- func Trust(pool *x509.CertPool) *tls.Config
- type IP
Constants ¶
const ( // Resolver DNS connection types UDP proto = iota TCP TLS )
Variables ¶
var ( // ResolverPrefer indicates an IP type to resolve to, if a dns name is passed to Resolver(). ResolverPrefer IP = IPv6 // With DNS over TLS acitvated your DNS server needs a certificate that is valid in the CA pool for the destination IP address. ResolverTLSVerify bool = true // ResolverDialer enables you to set your Timeouts/Deadlines as wanted. ResolverDialer *net.Dialer = &net.Dialer{Timeout: time.Second * 2} )
var ( // Logger defaults to Stderr and adds a prefix of "restrict: " DefaultLogger = log.New(os.Stderr, "restrict: ", 0) )
Functions ¶
func Access ¶
Access tries to behave similar to OpenBSD's unveil syscall.
It limits the filesystem view for that application by defining a path (directory or file) and flags (crwx) as appropiate but independent of the fs permissions. You can call Access() as often as needed, until you call AccessLock() once. A path has to exist prior to the Access() call. If that path is a directory it will enable all filesystem access underneath that path using the given flags.
Beware! The underlaying Landlock works unfortunately unlike the unveil implementation. Know that the first call to Access() does NOT remove visibility of the entire filesystem. You have to call AccessLock() to see that effect. Until then you'll allow full (openat) access by applying unveil/landlock sets to Syscalls(). After AccessLock() you can't take access away by reducing sets/promises like on OpenBSD.
Unlike on unveil on OpenBSD, the landlock implementation on linux passes the restrictions to forked/cloned or execve'd child processes. See SyscallsExec() on linux for more information.
func AccessCerts ¶
func AccessCerts() error
AccessCerts is a convenience function. It allows the typical paths to your operating systems CA and certificate locations to be read.
It represents an alias to Access("/etc/ssl/cert.pem", "r") on OpenBSD to allow reads. See below for files used on Linux/others. You must use AccessLock() to actually lock the filesystem view on Linux. See Access() for details on Linux documentation.
OpenBSD: "/etc/ssl/cert.pem" Linux: "/etc/ssl/certs", "/etc/ca-certificates", "/etc/ssl/cert.pem", "/etc/pki" Others: no-op
func AccessLock ¶
func AccessLock() error
AccessLock removes the ability to allow Access() to filesystem objects. You must call it once. If you call it without calling Access() before, then you will essentially disallow the whole filesystem. (Besides the implicit Access() calls through the other given sets if applicable.)
func Disable ¶
func Disable() error
Disable gives you the ability to let all restrict function calls be nil returned, except the Trust function that returns an empty *tls.Config.
Beware! It has to be called before the first restrict function call of Syscalls/Access/Trust/Resolver in the application and all protections are off. A notice will be printed to Logger:
"restrict: All subsequent restrict calls are disabled on your demand!"
func Expert ¶
func Expert()
Expert enables a mode to use restrict outside of main().
Beware! Do not use this unless you really know what you are doing.
func Resolver ¶
Resolver takes an IP address or name of a DNS server and sets it as the DefaultResolver.
Calls are processed via UDP on port 53 with the given IP version (see ResolverPrefer). It is an alias for ResolverX(dns, "53", false, true).
A more advanced approach for usage with non-standard ports or TLS connections that can be accomplished via ResolverX.
This mainly targets standard library functions.
func ResolverTCP ¶
ResolverTCP takes an IP address of a DNS server and sets it as the DefaultResolver. Calls are processed via TCP on port 53 with the given IP version (see ResolverPrefer). It is an alias for ResolverX(dns, "53", false, false).
func ResolverTLS ¶
ResolverTCP takes an IP address of a DNS server and sets it as the DefaultResolver. Calls are processed via TCP on port 853 with the given IP version (see ResolverPrefer). You can control the TLS verification via ResolverTLSVerify global variable. It is an alias for ResolverX(dns, "853", true, false).
func ResolverX ¶
ResolverX takes a dns server (ip/hostname), a target port and a boolean to activate DNS over TLS to use TCP and your systems root CA (rpath / AccessCerts()) or given Trust() configuration and lastly a udp boolean indicator that is incompatible with the previous dot boolean. DOT wins if both are set to true.
Using a hostname instead of an IP address triggers a workaround to determine an adequate IP for that host in the prefered (see ResolverPrefer global variable that defaults to IPv6) IP version via the systems nameservers (UDP/53) for that single lookup.
TLS verification is only available for IP addresses, not for hostnames. If your DNS server's IP is not listed in the certficates X509v3 Subject Alternative Name, then you need to disable ResolverTLSVerify and verify your trust otherwise to establish a connection. You can skip verify via global variable ResolverTLSVerify to set as false.
Most public dns providers are equipped like dns.quad9.net, dns.sb, dns0.eu (only IPv4), one.one.one.one, dns.google, dns.opendns.com.
func State ¶
func State()
State prints the actual state of all available restrictions to DefaultLogger().
restrict: Kernel: 7.4 restrict: Expert: false restrict: Access: true restrict: · Implicit: false restrict: · Lock: true restrict: Syscalls: true restrict: Resolver: true restrict: · Target: dns.sb:853 [2a09::] (TLS) restrict: Trust: false restrict: · Local: false restrict: Sets: [stdio rpath dns inet] restrict: Paths: restrict: → /home (r)
func Syscalls ¶
Syscalls tries to behave similar to OpenBSD's pledge syscall.
It forces that application in a syscall restricted operating mode by defining sets (a collection of syscalls, see promises on OpenBSD).
You can call Syscalls() as often as needed, but you can only reduce the allowed sets from the previous call.
The use of an unallowed syscall results in the default action to kill the process and notify about the incident in your kernel syslog. You can avoid this by applying the error set which will return ENOSYS to the calling function.
SyscallsExec is a no-op on linux since Syscalls and Access are inheriting the restriction to it's childs.
func SyscallsExec
deprecated
SyscallsExec is a no-op on linux.
Deprecated: Landlock¹ and seccomp² calls are inherited on Syscalls() with linux.
¹ https://www.kernel.org/doc/html/latest/userspace-api/landlock.html#inheritance ² https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html#usage
func Trust ¶
Trust sets the RootCA to your given pool and sets default http transport TLS config and returns a pointer to that tls.Config object for you to use it.
Beware! A previous Disable() call will return a barely initialized TLS config object. A call from outside main() without Expert() mode enabled, will print an error message to the DefaultLogger but still return a valid pointer to that tls.Config object.