From 03ca12d0c1c5152d8371f7db4928e91a7157d7b2 Mon Sep 17 00:00:00 2001 From: kvothe Date: Mon, 14 Sep 2020 22:21:05 -0400 Subject: [PATCH 1/5] First pass at a pledge/unveil implementation for OpenBSD. --- main.go | 3 +++ security.go | 14 +++++++++++++ security_openbsd.go | 51 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 security.go create mode 100644 security_openbsd.go diff --git a/main.go b/main.go index 7f4d48f..d98d859 100644 --- a/main.go +++ b/main.go @@ -69,6 +69,9 @@ func main() { } }() + // Restrict access to the files specified in config + enableSecurityRestrictions(config, errorLog) + // Infinite serve loop for { conn, err := listener.Accept() diff --git a/security.go b/security.go new file mode 100644 index 0000000..1ae9423 --- /dev/null +++ b/security.go @@ -0,0 +1,14 @@ +// +build !openbsd + +package main + +import ( + "log" +) + +// Restrict access to the files specified in config in an OS-dependent way. +// This is intended to be called immediately prior to accepting client +// connections and may be used to establish a security "jail" for the molly +// brown executable. +func enableSecurityRestrictions(config Config, errorLog *log.Logger) { +} diff --git a/security_openbsd.go b/security_openbsd.go new file mode 100644 index 0000000..a82c545 --- /dev/null +++ b/security_openbsd.go @@ -0,0 +1,51 @@ +package main + +import ( + "golang.org/x/sys/unix" + "log" +) + +// Restrict access to the files specified in config in an OS-dependent way. +// The OpenBSD implementation uses pledge(2) and unveil(2) to restrict the +// operations available to the molly brown executable. +func enableSecurityRestrictions(config Config, errorLog *log.Logger) { + // Pledge to only use stdio, inet, rpath, and unveil syscalls. + // If (S)CGI paths have been specified, also allow exec syscalls. + // Please note that execpromises haven't been specified, meaning that + // (S)CGI applications spawned by molly brown should pledge their own + // restrictions. + promises := "stdio inet rpath unveil" + if len(config.CGIPaths) > 0 || len(config.SCGIPaths) > 0 { + promises += " exec" + } + err := unix.PledgePromises(promises) + if err != nil { + errorLog.Println("Could not pledge: " + err.Error()) + log.Fatal(err) + } + // Unveil a specific list of files that we are allowed to access. + err = unix.Unveil(config.DocBase, "r") + if err != nil { + errorLog.Println("Could not unveil DocBase: " + err.Error()) + log.Fatal(err) + } + for _, cgiPath := range config.CGIPaths { + err = unix.Unveil(cgiPath, "rx") + if err != nil { + errorLog.Println("Could not unveil CGIPath: " + err.Error()) + log.Fatal(err) + } + } + for _, scgiPath := range config.SCGIPaths { + err = unix.Unveil(scgiPath, "rx") + if err != nil { + errorLog.Println("Could not unveil SCGIPaths: " + err.Error()) + log.Fatal(err) + } + } + err = unix.UnveilBlock() + if err != nil { + errorLog.Println("Could not block unveil: " + err.Error()) + log.Fatal(err) + } +} From 69a253f8208318d8560659476e4df61481ba6758 Mon Sep 17 00:00:00 2001 From: kvothe Date: Tue, 15 Sep 2020 22:14:12 -0400 Subject: [PATCH 2/5] Tested unveiling CGI dirs and globs as executable. --- security_openbsd.go | 52 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/security_openbsd.go b/security_openbsd.go index a82c545..bf17af9 100644 --- a/security_openbsd.go +++ b/security_openbsd.go @@ -3,44 +3,29 @@ package main import ( "golang.org/x/sys/unix" "log" + "path/filepath" ) // Restrict access to the files specified in config in an OS-dependent way. // The OpenBSD implementation uses pledge(2) and unveil(2) to restrict the // operations available to the molly brown executable. func enableSecurityRestrictions(config Config, errorLog *log.Logger) { - // Pledge to only use stdio, inet, rpath, and unveil syscalls. - // If (S)CGI paths have been specified, also allow exec syscalls. - // Please note that execpromises haven't been specified, meaning that - // (S)CGI applications spawned by molly brown should pledge their own - // restrictions. - promises := "stdio inet rpath unveil" - if len(config.CGIPaths) > 0 || len(config.SCGIPaths) > 0 { - promises += " exec" - } - err := unix.PledgePromises(promises) - if err != nil { - errorLog.Println("Could not pledge: " + err.Error()) - log.Fatal(err) - } + // Unveil a specific list of files that we are allowed to access. - err = unix.Unveil(config.DocBase, "r") + err := unix.Unveil(config.DocBase, "r") if err != nil { errorLog.Println("Could not unveil DocBase: " + err.Error()) log.Fatal(err) } for _, cgiPath := range config.CGIPaths { - err = unix.Unveil(cgiPath, "rx") - if err != nil { - errorLog.Println("Could not unveil CGIPath: " + err.Error()) - log.Fatal(err) - } - } - for _, scgiPath := range config.SCGIPaths { - err = unix.Unveil(scgiPath, "rx") - if err != nil { - errorLog.Println("Could not unveil SCGIPaths: " + err.Error()) - log.Fatal(err) + cgiGlobbedPaths, err := filepath.Glob(cgiPath) + for _, cgiGlobbedPath := range cgiGlobbedPaths { + log.Println("Unveiling \"" + cgiGlobbedPath + "\" as executable.") + err = unix.Unveil(cgiGlobbedPath, "rx") + if err != nil { + errorLog.Println("Could not unveil CGIPaths: " + err.Error()) + log.Fatal(err) + } } } err = unix.UnveilBlock() @@ -48,4 +33,19 @@ func enableSecurityRestrictions(config Config, errorLog *log.Logger) { errorLog.Println("Could not block unveil: " + err.Error()) log.Fatal(err) } + + // Pledge to only use stdio, inet, and rpath syscalls. + // If CGI paths have been specified, also allow exec syscalls. + // Please note that execpromises haven't been specified, meaning that + // CGI applications spawned by molly brown should pledge their own + // restrictions and unveil their own files. + promises := "stdio inet rpath" + if len(config.CGIPaths) > 0 { + promises += " exec proc" + } + err = unix.PledgePromises(promises) + if err != nil { + errorLog.Println("Could not pledge: " + err.Error()) + log.Fatal(err) + } } From fb77a13088cca4af6e04d5c9ce3d252a8f35d148 Mon Sep 17 00:00:00 2001 From: kvothe Date: Wed, 16 Sep 2020 23:24:41 -0400 Subject: [PATCH 3/5] Finished the OpenBSD pledge/unveil implementation after testing SCGI procs. --- security_openbsd.go | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/security_openbsd.go b/security_openbsd.go index bf17af9..705e455 100644 --- a/security_openbsd.go +++ b/security_openbsd.go @@ -8,15 +8,20 @@ import ( // Restrict access to the files specified in config in an OS-dependent way. // The OpenBSD implementation uses pledge(2) and unveil(2) to restrict the -// operations available to the molly brown executable. +// operations available to the molly brown executable. Please note that (S)CGI +// processes that molly brown spawns or communicates with are unrestristricted +// and should pledge their own restrictions and unveil their own files. func enableSecurityRestrictions(config Config, errorLog *log.Logger) { - // Unveil a specific list of files that we are allowed to access. + // Unveil the configured document base as readable. + log.Println("Unveiling \"" + config.DocBase + "\" as readable.") err := unix.Unveil(config.DocBase, "r") if err != nil { errorLog.Println("Could not unveil DocBase: " + err.Error()) log.Fatal(err) } + + // Unveil cgi path globs as executable. for _, cgiPath := range config.CGIPaths { cgiGlobbedPaths, err := filepath.Glob(cgiPath) for _, cgiGlobbedPath := range cgiGlobbedPaths { @@ -28,6 +33,15 @@ func enableSecurityRestrictions(config Config, errorLog *log.Logger) { } } } + + // Unveil scgi socket paths as readable and writeable. + for _, scgiSocket := range config.SCGIPaths { + log.Println("Unveiling \"" + scgiSocket + "\" as read/write.") + err = unix.Unveil(scgiSocket, "rw") + } + + // Finalize the unveil list. + // Any files not whitelisted above won't be accessible to molly brown. err = unix.UnveilBlock() if err != nil { errorLog.Println("Could not block unveil: " + err.Error()) @@ -35,14 +49,15 @@ func enableSecurityRestrictions(config Config, errorLog *log.Logger) { } // Pledge to only use stdio, inet, and rpath syscalls. - // If CGI paths have been specified, also allow exec syscalls. - // Please note that execpromises haven't been specified, meaning that - // CGI applications spawned by molly brown should pledge their own - // restrictions and unveil their own files. promises := "stdio inet rpath" if len(config.CGIPaths) > 0 { + // If CGI paths have been specified, also allow exec syscalls. promises += " exec proc" } + if len(config.SCGIPaths) > 0 { + // If SCGI paths have been specified, also allow unix sockets. + promises += " unix" + } err = unix.PledgePromises(promises) if err != nil { errorLog.Println("Could not pledge: " + err.Error()) From a8f59868f3acf508577dc695b10cc56cd269f26d Mon Sep 17 00:00:00 2001 From: kvothe Date: Wed, 16 Sep 2020 23:32:35 -0400 Subject: [PATCH 4/5] Update requirements list for OpenBSD. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index fcffc8a..9ee6a52 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,12 @@ Molly Brown only has a single dependency beyond the Go standard library, which is [this TOML parsing library](https://github.com/BurntSushi/toml). +The OpenBSD implementation also uses the [golang.org/x/sys/unix +package](https://godoc.org/golang.org/x/sys/unix) to provide the +[pledge(2)](https://man.openbsd.org/pledge.2) and +[unveil(2)](https://man.openbsd.org/unveil.2) system calls to provide +additional security features. + ## Installation The easiest way for now to install Molly Brown is to use the standard From 1c0fb0d856c46cdab382b08407c4cad003b9ac5a Mon Sep 17 00:00:00 2001 From: kvothe Date: Wed, 16 Sep 2020 23:49:03 -0400 Subject: [PATCH 5/5] Fixed a typo in the OpenBSD enableSecurityRestrictions docs. --- security_openbsd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security_openbsd.go b/security_openbsd.go index 705e455..bfcd1ce 100644 --- a/security_openbsd.go +++ b/security_openbsd.go @@ -9,7 +9,7 @@ import ( // Restrict access to the files specified in config in an OS-dependent way. // The OpenBSD implementation uses pledge(2) and unveil(2) to restrict the // operations available to the molly brown executable. Please note that (S)CGI -// processes that molly brown spawns or communicates with are unrestristricted +// processes that molly brown spawns or communicates with are unrestricted // and should pledge their own restrictions and unveil their own files. func enableSecurityRestrictions(config Config, errorLog *log.Logger) {