From 7a19a15365e3654fc7f1124efb3aaa0911e0cf11 Mon Sep 17 00:00:00 2001 From: eternal-flame-AD <yume@yumechi.jp> Date: Tue, 15 Nov 2022 10:31:27 -0600 Subject: [PATCH] refactor apparmor confinement --- Makefile | 3 +- cmd/server/server.go | 40 ++++++------- config/config.go | 7 +++ go.mod | 10 +++- go.sum | 21 +++++++ internal/apparmor/api.go | 65 --------------------- internal/apparmor/apparmor.c | 21 ------- internal/apparmor/apparmor.h | 8 --- internal/apparmor/magic.go | 17 ------ internal/util/apparmor_header.go | 43 ++++++++++++++ internal/version/version.go | 10 +++- server/server.go | 77 +++++++++++++++++++------ server/webroot/log.go | 17 +++++- webroot/twilio/voice/entrypoint.tpl.xml | 8 +++ webroot/twilio/voice/voicemail.tpl.xml | 6 ++ 15 files changed, 195 insertions(+), 158 deletions(-) delete mode 100644 internal/apparmor/api.go delete mode 100644 internal/apparmor/apparmor.c delete mode 100644 internal/apparmor/apparmor.h delete mode 100644 internal/apparmor/magic.go create mode 100644 internal/util/apparmor_header.go create mode 100644 webroot/twilio/voice/voicemail.tpl.xml diff --git a/Makefile b/Makefile index 58bdbe9..150b264 100644 --- a/Makefile +++ b/Makefile @@ -43,8 +43,7 @@ clean: dist/%: ${CMD_DIR}/% FORCE go build \ - -ldflags "-X ${MODULE_PATH}/internal/version.Version=$(VERSION) \ - -X ${MODULE_PATH}/internal/version.BuildDate=$(BUILDDATE)" \ + -ldflags "-X ${MODULE_PATH}/internal/version.BuildDate=$(BUILDDATE)" \ -o $@ ${MODULE_PATH}/$< .PHONY: build clean diff --git a/cmd/server/server.go b/cmd/server/server.go index 33479d8..2c7be2a 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -5,8 +5,9 @@ import ( "log" "os" + "github.com/eternal-flame-AD/go-apparmor/apparmor" + "github.com/eternal-flame-AD/go-apparmor/apparmor/magic" "github.com/eternal-flame-AD/yoake/config" - "github.com/eternal-flame-AD/yoake/internal/apparmor" "github.com/eternal-flame-AD/yoake/internal/comm" "github.com/eternal-flame-AD/yoake/internal/db" "github.com/eternal-flame-AD/yoake/server" @@ -40,26 +41,14 @@ func init() { } } -func changeHat() { - profile := config.Config().Listen.AppArmor.Serve - if profile != "" { - token, err := apparmor.GetMagicToken() - if err != nil { - log.Panicf("failed to get apparmor magic token: %v", err) - } - if err := apparmor.ChangeHat(profile, token); err != nil { - log.Panicf("failed to change apparmor hat: %v", err) - } else { - log.Printf("changed apparmor hat to %s", profile) - } - } -} - func main() { listen := config.Config().Listen + + Server := server.New() if listen.Ssl.Use { var sslCertBytes, sslKeyBytes []byte - apparmor.ExecuteInHat(listen.AppArmor.SSL, func() { + + readCerts := func() { var err error sslCertBytes, err = os.ReadFile(listen.Ssl.Cert) if err != nil { @@ -69,16 +58,27 @@ func main() { if err != nil { log.Panicf("failed to read ssl key: %v", err) } - }, true) + } + magic, err := magic.Generate(nil) + if err != nil { + log.Panicf("failed to generate apparmor magic token: %v", err) + } + if listen.AppArmor.SSL != "" { + if err := apparmor.WithHat(listen.AppArmor.SSL, func() uint64 { return magic }, readCerts); err != nil { + log.Panicf("failed to read ssl cert/key with apparmor hat: %v", err) + } + // defensive programming, try read ssl key if _, err := os.ReadFile(listen.Ssl.Key); err == nil { log.Panicf("AppArmor profile set for SSL but I could still read %v!", listen.Ssl.Key) } + } else { + readCerts() } - log.Fatalln(server.Server.StartTLS(listen.Addr, sslCertBytes, sslKeyBytes)) + log.Fatalln(Server.StartTLS(listen.Addr, sslCertBytes, sslKeyBytes)) } else { - log.Fatalln(server.Server.Start(listen.Addr)) + log.Fatalln(Server.Start(listen.Addr)) } } diff --git a/config/config.go b/config/config.go index 2fbd4f5..cbd572b 100644 --- a/config/config.go +++ b/config/config.go @@ -1,11 +1,14 @@ package config import ( + "log" + "github.com/jinzhu/configor" "github.com/labstack/echo/v4/middleware" ) type C struct { + parsed bool Hosts map[string]string Listen struct { Addr string @@ -69,6 +72,9 @@ var parsedC C var c C func Config() C { + if !c.parsed { + log.Panicln("Config() called without calling ParseConfig() first") + } return c } @@ -81,5 +87,6 @@ func MockConfig(freshEnv bool, wrapper func(deployedC *C)) { func ParseConfig(files ...string) { configor.Load(&parsedC, files...) + parsedC.parsed = true c = parsedC } diff --git a/go.mod b/go.mod index 9141876..250cd12 100644 --- a/go.mod +++ b/go.mod @@ -24,17 +24,20 @@ require ( github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/dgraph-io/badger/v3 v3.2103.4 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/duo-labs/webauthn v0.0.0-20220815211337-00c9fb5711f5 // indirect github.com/dustin/go-humanize v1.0.0 // indirect + github.com/eternal-flame-AD/go-apparmor v0.0.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect - github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.3.1 // indirect + github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.3 // indirect github.com/google/flatbuffers v1.12.1 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/securecookie v1.1.1 // indirect + github.com/jsipprell/keyctl v1.0.3 // indirect github.com/klauspost/compress v1.12.3 // indirect github.com/labstack/gommon v0.4.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect @@ -50,6 +53,7 @@ require ( golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect + google.golang.org/protobuf v1.26.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect - gopkg.in/yaml.v2 v2.2.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index b9197a0..c82ab70 100644 --- a/go.sum +++ b/go.sum @@ -32,9 +32,17 @@ github.com/dgraph-io/badger/v3 v3.2103.4/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5js github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/duo-labs/webauthn v0.0.0-20220815211337-00c9fb5711f5 h1:BaeJtFDlto/NjX9t730OebRRJf2P+t9YEDz3ur18824= +github.com/duo-labs/webauthn v0.0.0-20220815211337-00c9fb5711f5/go.mod h1:Jcj7rFNlTknb18v9jpSA58BveX2LDhXqaoy+6YV1N9g= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/eternal-flame-AD/go-apparmor v0.0.1 h1:LBWfkf/Mx0s6inwqurWC8nME7ICg4cDmh2fmOkBeenI= +github.com/eternal-flame-AD/go-apparmor v0.0.1/go.mod h1:K8VSDcvYN18uG+vsnR+3um4t6fX13Km6ci9mgQfDMg8= +github.com/eternal-flame-AD/go-apparmor v0.0.2 h1:sjDN6pyyjXBB+o+oDt6kyo2xiE8vvjZRABZ0fJEzHiE= +github.com/eternal-flame-AD/go-apparmor v0.0.2/go.mod h1:OpqESxf/LXsssooWBPzAoIAC2PtloCT1CmA+glQKYV8= +github.com/eternal-flame-AD/go-apparmor v0.0.3 h1:nFOxi6mbE8wpd5aHbSGvjbnaEjYC156IICWTteSgEIo= +github.com/eternal-flame-AD/go-apparmor v0.0.3/go.mod h1:OpqESxf/LXsssooWBPzAoIAC2PtloCT1CmA+glQKYV8= github.com/eternal-flame-AD/yubigo v0.0.0-20221005082707-ce0c8989e8b1 h1:B+ad4UMWwNAUsZhLLQCCrEx+cfLsbf0+AbbcfG7RIv0= github.com/eternal-flame-AD/yubigo v0.0.0-20221005082707-ce0c8989e8b1/go.mod h1:kRnqsWaIjqWNPoCV14+cxs/B9eClc0hKL/I2a3LKOQ4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -46,12 +54,17 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c h1:iyaGYbCmcYK0Ja9a3OUa2Fo+EaN0cbLu0eKpBwPFzc8= @@ -60,6 +73,7 @@ github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6 github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= @@ -72,6 +86,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko= github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc= +github.com/jsipprell/keyctl v1.0.3 h1:o72tppb3ZhP5B/v9FGUtMqJWx+S1Gs0elQ7AZmiNhsM= +github.com/jsipprell/keyctl v1.0.3/go.mod h1:64s6WpBtruURX3w8W/vhWj1/uh+nOm7vUXSJlK5+KMs= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= @@ -221,6 +237,9 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -231,6 +250,8 @@ gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AW gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/apparmor/api.go b/internal/apparmor/api.go deleted file mode 100644 index 01e805d..0000000 --- a/internal/apparmor/api.go +++ /dev/null @@ -1,65 +0,0 @@ -package apparmor - -import ( - // #cgo LDFLAGS: -lapparmor - // #include "./apparmor.h" - "C" -) -import ( - "runtime" - "syscall" - "unsafe" -) - -func ChangeHat(subprofile string, magicToken uint64) error { - var ret uintptr - if subprofile != "" { - subProfileC := C.CString(subprofile) - defer C.free(unsafe.Pointer(subProfileC)) - ret = uintptr(C.go_aa_change_hat(subProfileC, C.ulong(magicToken))) - } else { - ret = uintptr(C.go_aa_change_hat(nil, C.ulong(magicToken))) - } - - if ret != 0 { - return syscall.Errno(ret) - } - return nil -} - -func ExecuteInHat(subprofile string, fn func(), lockThread bool) error { - if subprofile == "" { - fn() - return nil - } - if lockThread { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - } - token, err := GetMagicToken() - if err != nil { - return err - } - if err := ChangeHat(subprofile, token); err != nil { - return err - } - fn() - return ChangeHat("", token) -} - -func ChangeProfile(subprofile string) error { - var ret uintptr - - if subprofile != "" { - subProfileC := C.CString(subprofile) - defer C.free(unsafe.Pointer(subProfileC)) - ret = uintptr(C.go_aa_change_profile(subProfileC)) - } else { - ret = uintptr(C.go_aa_change_profile(nil)) - } - - if ret != 0 { - return syscall.Errno(ret) - } - return nil -} diff --git a/internal/apparmor/apparmor.c b/internal/apparmor/apparmor.c deleted file mode 100644 index c867b00..0000000 --- a/internal/apparmor/apparmor.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "./apparmor.h" - -int go_aa_change_hat(const char *hat, unsigned long magic) -{ - int ret = aa_change_hat(hat, magic); - if (ret < 0) - { - return errno; - } - return 0; -} - -int go_aa_change_profile(const char *profile) -{ - int ret = aa_change_profile(profile); - if (ret < 0) - { - return errno; - } - return 0; -} \ No newline at end of file diff --git a/internal/apparmor/apparmor.h b/internal/apparmor/apparmor.h deleted file mode 100644 index 7d99e36..0000000 --- a/internal/apparmor/apparmor.h +++ /dev/null @@ -1,8 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <sys/apparmor.h> - -int go_aa_change_hat(const char *hat, unsigned long magic); - -int go_aa_change_profile(const char *profile); \ No newline at end of file diff --git a/internal/apparmor/magic.go b/internal/apparmor/magic.go deleted file mode 100644 index 5f7a282..0000000 --- a/internal/apparmor/magic.go +++ /dev/null @@ -1,17 +0,0 @@ -package apparmor - -import ( - "crypto/rand" - "fmt" -) - -func GetMagicToken() (uint64, error) { - var buf [64 / 8]byte - if _, err := rand.Read(buf[:]); err != nil { - return 0, fmt.Errorf("failed to generate magic token: %v", err) - } - return uint64(buf[0])<<56 | uint64(buf[1])<<48 | - uint64(buf[2])<<40 | uint64(buf[3])<<32 | - uint64(buf[4])<<24 | uint64(buf[5])<<16 | - uint64(buf[6])<<8 | uint64(buf[7]), nil -} diff --git a/internal/util/apparmor_header.go b/internal/util/apparmor_header.go new file mode 100644 index 0000000..ac1fceb --- /dev/null +++ b/internal/util/apparmor_header.go @@ -0,0 +1,43 @@ +package util + +import ( + "fmt" + "log" + "os" + "strings" + + "github.com/eternal-flame-AD/go-apparmor/apparmor" + "github.com/labstack/echo/v4" +) + +type AAConMiddlewareEnforcer func(label string, mode string) (exit int, err error) + +func AAConMiddleware(enforce AAConMiddlewareEnforcer) echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + label, mode, err := apparmor.AAGetCon() + if err != nil { + log.Printf("failed to get apparmor label: %v", err) + label = "[ERROR]" + } + var sanitizedLabel string + if idx := strings.Index(label, "//"); idx == -1 { + sanitizedLabel = "//" + } else { + sanitizedLabel = label[idx:] + } + c.Response().Header().Set("X-App-Con", fmt.Sprintf("%s (%s)", sanitizedLabel, mode)) + if enforce != nil { + if exitCode, err := enforce(label, mode); err != nil { + if exitCode == 0 { + c.Response().After(func() { + os.Exit(exitCode) + }) + } + return err + } + } + return next(c) + } + } +} diff --git a/internal/version/version.go b/internal/version/version.go index 389d5c5..588b687 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,6 +1,14 @@ package version +import "runtime/debug" + var ( - Version = "unknown" + Version = func() string { + info, ok := debug.ReadBuildInfo() + if !ok { + return "unknown" + } + return info.Main.Version + }() BuildDate = "unknown" ) diff --git a/server/server.go b/server/server.go index 8d3c009..a9332e8 100644 --- a/server/server.go +++ b/server/server.go @@ -1,10 +1,14 @@ package server import ( + "errors" + "log" "strings" + "github.com/eternal-flame-AD/go-apparmor/apparmor" + "github.com/eternal-flame-AD/go-apparmor/apparmor/magic" "github.com/eternal-flame-AD/yoake/config" - "github.com/eternal-flame-AD/yoake/internal/apparmor" + "github.com/eternal-flame-AD/yoake/internal/util" "github.com/labstack/echo/v4" ) @@ -14,25 +18,58 @@ type ( } ) -var Server = echo.New() var hosts = map[string]*Host{} -func init() { - hatChanged := false - Server.Any("/*", func(c echo.Context) (err error) { - if !hatChanged { - appArmor := config.Config().Listen.AppArmor - if appArmor.Serve != "" { - if key, err := apparmor.GetMagicToken(); err != nil { - return err - } else { - if err := apparmor.ChangeHat(appArmor.Serve, key); err != nil { - return err - } - } - } - hatChanged = true +func New() *echo.Echo { + var Server = echo.New() + hatServe := config.Config().Listen.AppArmor.Serve + if hatServe != "" { + store, err := magic.NewKeyring(nil) + if err != nil { + log.Panicf("failed to initialize magic token store: %v", err) } + if magic, err := magic.Generate(nil); err != nil { + log.Panicf("failed to generate apparmor magic token: %v", err) + } else { + if err := store.Set(magic); err != nil { + log.Panicf("failed to store apparmor magic token: %v", err) + } + } + hatMagic := func() uint64 { + magic, err := store.Get() + if err != nil { + log.Panicf("failed to get magic token: %v", err) + } + return magic + } + Server.Pre(func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + var err error + if errAppArmor := apparmor.WithHat(hatServe, hatMagic, func() { + err = next(c) + }); errAppArmor != nil { + c.Logger().Errorf("apparmor error: %v", errAppArmor) + return errors.New("apparmor process transition error") + } + return err + } + }) + aaEnforcer := util.AAConMiddleware(func(label string, mode string) (exit int, err error) { + if !strings.HasSuffix(label, "//"+hatServe) { + return 1, errors.New("apparmor process transition error") + } + return 0, nil + }) + + Server.Pre(aaEnforcer) + Server.Use(aaEnforcer) + for _, h := range hosts { + h.Echo.Pre(aaEnforcer) + h.Echo.Use(aaEnforcer) + } + } + + Server.Any("/*", func(c echo.Context) (err error) { req := c.Request() res := c.Response() host := hosts[strings.ToLower(req.Host)] @@ -40,13 +77,17 @@ func init() { if host == nil { host = hosts[""] if host == nil { - return echo.ErrNotFound + err = echo.ErrNotFound + return } } host.Echo.ServeHTTP(res, req) + return }) + + return Server } func RegisterHostname(hostname string, h *Host) { diff --git a/server/webroot/log.go b/server/webroot/log.go index a25513b..631d6cf 100644 --- a/server/webroot/log.go +++ b/server/webroot/log.go @@ -7,6 +7,7 @@ import ( "regexp" "strconv" + "github.com/eternal-flame-AD/go-apparmor/apparmor" "github.com/eternal-flame-AD/yoake/internal/auth" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" @@ -14,9 +15,10 @@ import ( type logEntry struct { middleware.RequestLoggerValues - Categories []string - CleanPath string - Auth auth.RequestAuth + Categories []string + CleanPath string + AppArmorCon string + Auth auth.RequestAuth } func processLoggerValues(c echo.Context, values middleware.RequestLoggerValues) logEntry { @@ -26,11 +28,20 @@ func processLoggerValues(c echo.Context, values middleware.RequestLoggerValues) logSetRequestCategory(c, fmt.Sprintf("status_%s", statusString)) statusString[i] = 'x' } + + aaCon := "" + label, mode, err := apparmor.AAGetCon() + if err != nil { + aaCon = fmt.Sprintf("error: %s", err) + } else { + aaCon = fmt.Sprintf("%s (%s)", label, mode) + } return logEntry{ RequestLoggerValues: values, Categories: logGetCategories(c), CleanPath: path.Clean(c.Request().URL.Path), Auth: auth.GetRequestAuth(c), + AppArmorCon: aaCon, } } diff --git a/webroot/twilio/voice/entrypoint.tpl.xml b/webroot/twilio/voice/entrypoint.tpl.xml index 4ca4197..db3d238 100644 --- a/webroot/twilio/voice/entrypoint.tpl.xml +++ b/webroot/twilio/voice/entrypoint.tpl.xml @@ -4,7 +4,15 @@ {{ $num_visited := (get $session "num_visited") }} {{ if not $num_visited }}{{ $num_visited = 0 }} {{ end }} {{ set $session "num_visited" (math "argv(1) + 1" $num_visited) }} + <Response> + +{{ if $src := (invoke "FormValue" .C "ForwardedFrom") }} + {{ if eq $src "+15122993080" }} + <Redirect method="POST">/twilio/voice/voicemail.xml></Redirect> + {{ end }} +{{ end }} + <Say voice="alice" language="en-US">This is Anne!</Say> <Redirect method="POST">/twilio/voice/menu.xml</Redirect> </Response> \ No newline at end of file diff --git a/webroot/twilio/voice/voicemail.tpl.xml b/webroot/twilio/voice/voicemail.tpl.xml new file mode 100644 index 0000000..19b051b --- /dev/null +++ b/webroot/twilio/voice/voicemail.tpl.xml @@ -0,0 +1,6 @@ +{{ template "/twilio/head.tpl.xml" . }} + +<Response> +<!-- TODO: finish --> + <Hangup /> +</Response> \ No newline at end of file