Avoid use of log.Fatal() or os.Exit() in main so defers are guaranteed to run.
This commit is contained in:
parent
7fad754ff2
commit
072669a167
37
main.go
37
main.go
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue