1
1
Fork 0
mirror of https://tildegit.org/solderpunk/molly-brown synced 2024-05-12 16:06:03 +02:00

Avoid use of log.Fatal() or os.Exit() in main so defers are guaranteed to run.

This commit is contained in:
Solderpunk 2023-02-19 14:40:54 +01:00
parent 7fad754ff2
commit 072669a167
6 changed files with 62 additions and 30 deletions

37
main.go
View File

@ -41,15 +41,26 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
// Run server and exit
os.Exit(do_main(config))
}
func do_main(config Config) int {
// If we are running as root, find the UID of the "nobody" user, before a // If we are running as root, find the UID of the "nobody" user, before a
// chroot() possibly stops seeing /etc/passwd // chroot() possibly stops seeing /etc/passwd
privInfo := getUserInfo(config) privInfo, err := getUserInfo(config)
if err != nil {
errorLog.Println("Exiting due to failure to apply security restrictions.")
return 1
}
// Chroot, if asked // Chroot, if asked
if config.ChrootDir != "" { if config.ChrootDir != "" {
err := syscall.Chroot(config.ChrootDir) err := syscall.Chroot(config.ChrootDir)
if err != nil { if err != nil {
log.Fatal("Could not chroot to " + config.ChrootDir + ": " + err.Error()) log.Println("Could not chroot to " + config.ChrootDir + ": " + err.Error())
return 1
} }
} }
@ -60,7 +71,8 @@ func main() {
} else { } else {
errorLogFile, err = os.OpenFile(config.ErrorLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) errorLogFile, err = os.OpenFile(config.ErrorLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
log.Fatal(err) log.Println(err)
return 1
} }
defer errorLogFile.Close() defer errorLogFile.Close()
} }
@ -73,7 +85,8 @@ func main() {
accessLogFile, err = os.OpenFile(config.AccessLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) accessLogFile, err = os.OpenFile(config.AccessLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
errorLog.Println("Error opening access log file: " + err.Error()) errorLog.Println("Error opening access log file: " + err.Error())
log.Fatal(err) log.Println(err)
return 1
} }
defer accessLogFile.Close() defer accessLogFile.Close()
} }
@ -83,16 +96,16 @@ func main() {
info, err := os.Stat(config.KeyPath) info, err := os.Stat(config.KeyPath)
if err != nil { if err != nil {
errorLog.Println("Error opening TLS key file: " + err.Error()) errorLog.Println("Error opening TLS key file: " + err.Error())
log.Fatal(err) return 1
} }
if uint64(info.Mode().Perm())&0444 == 0444 { if uint64(info.Mode().Perm())&0444 == 0444 {
errorLog.Println("Refusing to use world-readable TLS key file " + config.KeyPath) errorLog.Println("Refusing to use world-readable TLS key file " + config.KeyPath)
os.Exit(0) return 1
} }
cert, err := tls.LoadX509KeyPair(config.CertPath, config.KeyPath) cert, err := tls.LoadX509KeyPair(config.CertPath, config.KeyPath)
if err != nil { if err != nil {
errorLog.Println("Error loading TLS keypair: " + err.Error()) errorLog.Println("Error loading TLS keypair: " + err.Error())
log.Fatal(err) return 1
} }
tlscfg := &tls.Config{ tlscfg := &tls.Config{
Certificates: []tls.Certificate{cert}, Certificates: []tls.Certificate{cert},
@ -108,13 +121,17 @@ func main() {
} }
// Apply security restrictions // Apply security restrictions
enableSecurityRestrictions(config, privInfo, errorLog) err = enableSecurityRestrictions(config, privInfo, errorLog)
if err != nil {
errorLog.Println("Exiting due to failure to apply security restrictions.")
return 1
}
// Create TLS listener // Create TLS listener
listener, err := tls.Listen("tcp", ":"+strconv.Itoa(config.Port), tlscfg) listener, err := tls.Listen("tcp", ":"+strconv.Itoa(config.Port), tlscfg)
if err != nil { if err != nil {
errorLog.Println("Error creating TLS listener: " + err.Error()) errorLog.Println("Error creating TLS listener: " + err.Error())
log.Fatal(err) return 1
} }
defer listener.Close() defer listener.Close()
@ -164,4 +181,6 @@ func main() {
wg.Wait() wg.Wait()
errorLog.Println("Exiting.") errorLog.Println("Exiting.")
// Exit successfully
return 0
} }

View File

@ -10,5 +10,5 @@ import (
// This is intended to be called immediately prior to accepting client // This is intended to be called immediately prior to accepting client
// connections and may be used to establish a security "jail" for the molly // connections and may be used to establish a security "jail" for the molly
// brown executable. // brown executable.
func enableSecurityRestrictions(config Config, errorLog *log.Logger) { func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) error {
} }

View File

@ -26,7 +26,7 @@ type userInfo struct {
unpriv_gid int unpriv_gid int
} }
func getUserInfo(config Config) userInfo { func getUserInfo(config Config) (userInfo, error) {
var ui userInfo var ui userInfo
ui.uid = os.Getuid() ui.uid = os.Getuid()
ui.euid = os.Geteuid() ui.euid = os.Geteuid()
@ -34,7 +34,8 @@ func getUserInfo(config Config) userInfo {
ui.egid = os.Getegid() ui.egid = os.Getegid()
supp_groups, err := os.Getgroups() supp_groups, err := os.Getgroups()
if err != nil { if err != nil {
log.Fatal("Could not get supplementary groups: ", err.Error()) log.Println("Could not get supplementary groups: ", err.Error())
return ui, err
} }
ui.supp_groups = supp_groups ui.supp_groups = supp_groups
ui.unpriv_uid = -1 ui.unpriv_uid = -1
@ -55,22 +56,24 @@ func getUserInfo(config Config) userInfo {
if ui.need_drop { if ui.need_drop {
nobody_user, err := user.Lookup(config.UnprivUsername) nobody_user, err := user.Lookup(config.UnprivUsername)
if err != nil { if err != nil {
log.Fatal("Running as root but could not lookup UID for user " + config.UnprivUsername + ": " + err.Error()) log.Println("Running as root but could not lookup UID for user " + config.UnprivUsername + ": " + err.Error())
return ui, err
} }
ui.unpriv_uid, err = strconv.Atoi(nobody_user.Uid) ui.unpriv_uid, err = strconv.Atoi(nobody_user.Uid)
ui.unpriv_gid, err = strconv.Atoi(nobody_user.Gid) ui.unpriv_gid, err = strconv.Atoi(nobody_user.Gid)
if err != nil { if err != nil {
log.Fatal("Running as root but could not lookup UID for user " + config.UnprivUsername + ": " + err.Error()) log.Println("Running as root but could not lookup UID for user " + config.UnprivUsername + ": " + err.Error())
return ui, err
} }
} }
return ui return ui, nil
} }
func DropPrivs(ui userInfo, errorLog *log.Logger) { func DropPrivs(ui userInfo, errorLog *log.Logger) error {
// If we're already unprivileged, all good // If we're already unprivileged, all good
if !ui.need_drop { if !ui.need_drop {
return return nil
} }
// Drop supplementary groups // Drop supplementary groups
@ -78,7 +81,7 @@ func DropPrivs(ui userInfo, errorLog *log.Logger) {
err := syscall.Setgroups([]int{}) err := syscall.Setgroups([]int{})
if err != nil { if err != nil {
errorLog.Println("Could not unset supplementary groups: " + err.Error()) errorLog.Println("Could not unset supplementary groups: " + err.Error())
log.Fatal(err) return err
} }
} }
@ -87,7 +90,7 @@ func DropPrivs(ui userInfo, errorLog *log.Logger) {
err := syscall.Setgid(ui.unpriv_gid) err := syscall.Setgid(ui.unpriv_gid)
if err != nil { if err != nil {
errorLog.Println("Could not setgid to " + strconv.Itoa(ui.unpriv_gid) + ": " + err.Error()) errorLog.Println("Could not setgid to " + strconv.Itoa(ui.unpriv_gid) + ": " + err.Error())
log.Fatal(err) return err
} }
} }
@ -96,8 +99,9 @@ func DropPrivs(ui userInfo, errorLog *log.Logger) {
err := syscall.Setuid(ui.unpriv_uid) err := syscall.Setuid(ui.unpriv_uid)
if err != nil { if err != nil {
errorLog.Println("Could not setuid to " + strconv.Itoa(ui.unpriv_uid) + ": " + err.Error()) errorLog.Println("Could not setuid to " + strconv.Itoa(ui.unpriv_uid) + ": " + err.Error())
log.Fatal(err) return err
} }
} }
return nil
} }

View File

@ -7,7 +7,7 @@ import (
"os" "os"
) )
func enableSecurityRestrictions(config Config, errorLog *log.Logger) { func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) error {
// Prior to Go 1.6, setuid did not work reliably on Linux // Prior to Go 1.6, setuid did not work reliably on Linux
// So, absolutely refuse to run as root // So, absolutely refuse to run as root
@ -16,6 +16,8 @@ func enableSecurityRestrictions(config Config, errorLog *log.Logger) {
if uid == 0 || euid == 0 { if uid == 0 || euid == 0 {
setuid_err := "Refusing to run with root privileges when setuid() will not work!" setuid_err := "Refusing to run with root privileges when setuid() will not work!"
errorLog.Println(setuid_err) errorLog.Println(setuid_err)
log.Fatal(setuid_err) return error.New(setuid_err)
} }
return nil
} }

View File

@ -11,17 +11,20 @@ import (
// operations available to the molly brown executable. Please note that (S)CGI // operations available to the molly brown executable. Please note that (S)CGI
// processes that molly brown spawns or communicates with are unrestricted // processes that molly brown spawns or communicates with are unrestricted
// and should pledge their own restrictions and unveil their own files. // and should pledge their own restrictions and unveil their own files.
func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) { func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) error {
// Setuid to an unprivileged user // Setuid to an unprivileged user
DropPrivs(ui, errorLog) err := DropPrivs(ui, errorLog)
if err != nil {
return err
}
// Unveil the configured document base as readable. // Unveil the configured document base as readable.
log.Println("Unveiling \"" + config.DocBase + "\" as readable.") log.Println("Unveiling \"" + config.DocBase + "\" as readable.")
err := unix.Unveil(config.DocBase, "r") err := unix.Unveil(config.DocBase, "r")
if err != nil { if err != nil {
errorLog.Println("Could not unveil DocBase: " + err.Error()) errorLog.Println("Could not unveil DocBase: " + err.Error())
log.Fatal(err) return err
} }
// Unveil cgi path globs as executable. // Unveil cgi path globs as executable.
@ -32,7 +35,7 @@ func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger
err = unix.Unveil(cgiGlobbedPath, "rx") err = unix.Unveil(cgiGlobbedPath, "rx")
if err != nil { if err != nil {
errorLog.Println("Could not unveil CGIPaths: " + err.Error()) errorLog.Println("Could not unveil CGIPaths: " + err.Error())
log.Fatal(err) return err
} }
} }
} }
@ -41,6 +44,9 @@ func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger
for _, scgiSocket := range config.SCGIPaths { for _, scgiSocket := range config.SCGIPaths {
log.Println("Unveiling \"" + scgiSocket + "\" as read/write.") log.Println("Unveiling \"" + scgiSocket + "\" as read/write.")
err = unix.Unveil(scgiSocket, "rw") err = unix.Unveil(scgiSocket, "rw")
if err != nil {
return err
}
} }
// Finalize the unveil list. // Finalize the unveil list.
@ -48,7 +54,7 @@ func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger
err = unix.UnveilBlock() err = unix.UnveilBlock()
if err != nil { if err != nil {
errorLog.Println("Could not block unveil: " + err.Error()) errorLog.Println("Could not block unveil: " + err.Error())
log.Fatal(err) return err
} }
// Pledge to only use stdio, inet, and rpath syscalls. // Pledge to only use stdio, inet, and rpath syscalls.
@ -64,6 +70,6 @@ func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger
err = unix.PledgePromises(promises) err = unix.PledgePromises(promises)
if err != nil { if err != nil {
errorLog.Println("Could not pledge: " + err.Error()) errorLog.Println("Could not pledge: " + err.Error())
log.Fatal(err) return err
} }
} }

View File

@ -6,8 +6,9 @@ import (
"log" "log"
) )
func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) { func enableSecurityRestrictions(config Config, ui userInfo, errorLog *log.Logger) error {
// Setuid to an unprivileged user // Setuid to an unprivileged user
DropPrivs(ui, errorLog) return DropPrivs(ui, errorLog)
} }