diff --git a/.gitignore b/.gitignore index c181e07b2..164675261 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea *.iml -dist/ \ No newline at end of file +dist/ +.env +conf.json \ No newline at end of file diff --git a/cmd/signal.go b/cmd/signal.go index a4c465dc5..04fb8f904 100644 --- a/cmd/signal.go +++ b/cmd/signal.go @@ -48,7 +48,8 @@ var ( var opts []grpc.ServerOption if signalLetsencryptDomain != "" { - transportCredentials := credentials.NewTLS(encryption.EnableLetsEncrypt(signalDataDir, signalLetsencryptDomain)) + certManager := encryption.CreateCertManager(signalDataDir, signalLetsencryptDomain) + transportCredentials := credentials.NewTLS(certManager.TLSConfig()) opts = append(opts, grpc.Creds(transportCredentials)) } diff --git a/encryption/letsencrypt.go b/encryption/letsencrypt.go index 5664b6e7a..0540e190f 100644 --- a/encryption/letsencrypt.go +++ b/encryption/letsencrypt.go @@ -1,17 +1,14 @@ package encryption import ( - "crypto/tls" log "github.com/sirupsen/logrus" "golang.org/x/crypto/acme/autocert" - "net/http" "os" "path/filepath" ) -// EnableLetsEncrypt wraps common logic of generating Let's encrypt certificate. -// Includes a HTTP handler and listener to solve the Let's encrypt challenge -func EnableLetsEncrypt(datadir string, letsencryptDomain string) *tls.Config { +// CreateCertManager wraps common logic of generating Let's encrypt certificate. +func CreateCertManager(datadir string, letsencryptDomain string) *autocert.Manager { certDir := filepath.Join(datadir, "letsencrypt") if _, err := os.Stat(certDir); os.IsNotExist(err) { @@ -23,18 +20,11 @@ func EnableLetsEncrypt(datadir string, letsencryptDomain string) *tls.Config { log.Infof("running with Let's encrypt with domain %s. Cert will be stored in %s", letsencryptDomain, certDir) - certManager := autocert.Manager{ + certManager := &autocert.Manager{ Prompt: autocert.AcceptTOS, Cache: autocert.DirCache(certDir), HostPolicy: autocert.HostWhitelist(letsencryptDomain), } - // listener to handle Let's encrypt certificate challenge - go func() { - if err := http.Serve(certManager.Listener(), certManager.HTTPHandler(nil)); err != nil { - log.Fatalf("failed to serve letsencrypt handler: %v", err) - } - }() - - return &tls.Config{GetCertificate: certManager.GetCertificate} + return certManager } diff --git a/go.mod b/go.mod index e0f694417..f35b34192 100644 --- a/go.mod +++ b/go.mod @@ -4,21 +4,28 @@ go 1.16 require ( github.com/cenkalti/backoff/v4 v4.1.0 + github.com/codegangsta/negroni v1.0.0 + github.com/coreos/go-oidc v2.2.1+incompatible github.com/golang/protobuf v1.5.2 - github.com/google/uuid v1.2.0 // indirect + github.com/google/uuid v1.2.0 + github.com/gorilla/mux v1.8.0 + github.com/gorilla/sessions v1.2.1 + github.com/joho/godotenv v1.3.0 github.com/kardianos/service v1.2.0 github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.13.0 github.com/pion/ice/v2 v2.1.7 + github.com/pquerna/cachecontrol v0.1.0 // indirect github.com/sirupsen/logrus v1.7.0 github.com/spf13/cobra v1.1.3 - github.com/spf13/viper v1.7.0 github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf + golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/sys v0.0.0-20210510120138-977fb7262007 golang.zx2c4.com/wireguard v0.0.0-20210604143328-f9b48a961cd2 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210506160403-92e472f520a5 golang.zx2c4.com/wireguard/windows v0.3.14 google.golang.org/grpc v1.32.0 google.golang.org/protobuf v1.26.0 + gopkg.in/square/go-jose.v2 v2.6.0 // indirect ) diff --git a/go.sum b/go.sum index 1cf8c35ba..7e1eb60ef 100644 --- a/go.sum +++ b/go.sum @@ -11,7 +11,6 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -30,8 +29,12 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY= +github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= +github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -93,8 +96,13 @@ github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= @@ -114,7 +122,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -123,6 +130,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -136,7 +145,6 @@ github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozI github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g= @@ -152,7 +160,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -181,7 +188,6 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -200,7 +206,6 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pion/dtls/v2 v2.0.9 h1:7Ow+V++YSZQMYzggI0P9vLJz/hUFcffsfGMfT/Qy+u8= github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho= @@ -227,6 +232,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= +github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -246,24 +253,18 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -273,7 +274,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= @@ -354,6 +354,7 @@ golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -456,6 +457,7 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -490,9 +492,10 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= diff --git a/management/cmd/management.go b/management/cmd/management.go index 7593778fb..2132495d7 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -1,11 +1,12 @@ package cmd import ( + "context" "flag" "fmt" + "github.com/wiretrustee/wiretrustee/management/http_server" "github.com/wiretrustee/wiretrustee/management/server" "github.com/wiretrustee/wiretrustee/util" - "net" "os" "time" @@ -57,9 +58,15 @@ var ( var opts []grpc.ServerOption + var httpServer *http_server.Server if config.LetsEncryptDomain != "" { - transportCredentials := credentials.NewTLS(encryption.EnableLetsEncrypt(config.Datadir, config.LetsEncryptDomain)) + certManager := encryption.CreateCertManager(config.Datadir, config.LetsEncryptDomain) + transportCredentials := credentials.NewTLS(certManager.TLSConfig()) opts = append(opts, grpc.Creds(transportCredentials)) + + httpServer = http_server.NewHttpsServer(config.HttpConfig, certManager) + } else { + httpServer = http_server.NewHttpServer(config.HttpConfig) } opts = append(opts, grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)) @@ -83,9 +90,24 @@ var ( } }() + go func() { + err = httpServer.Start() + if err != nil { + log.Fatalf("failed to serve http server: %v", err) + } + }() + SetupCloseHandler() <-stopCh log.Println("Receive signal to stop running Management server") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + err = httpServer.Stop(ctx) + if err != nil { + log.Fatalf("failed stopping the http server %v", err) + } + + grpcServer.Stop() }, } ) diff --git a/management/http_server/handler/callback.go b/management/http_server/handler/callback.go new file mode 100644 index 000000000..12a35a06c --- /dev/null +++ b/management/http_server/handler/callback.go @@ -0,0 +1,96 @@ +package handler + +import ( + "context" + "github.com/coreos/go-oidc" + "github.com/gorilla/sessions" + "github.com/wiretrustee/wiretrustee/management/http_server/middleware" + "log" + "net/http" +) + +// Callback handler used to receive a callback from the identity provider +type Callback struct { + authenticator *middleware.Authenticator + sessionStore sessions.Store +} + +func NewCallback(authenticator *middleware.Authenticator, sessionStore sessions.Store) *Callback { + return &Callback{ + authenticator: authenticator, + sessionStore: sessionStore, + } +} + +// ServeHTTP checks the user session, verifies the state, verifies the token, stores user profile in a session, +// and in case of the successful auth redirects user to the main page +func (h *Callback) ServeHTTP(w http.ResponseWriter, r *http.Request) { + session, err := h.sessionStore.Get(r, "auth-session") + if err != nil { + //todo redirect to the error page stating: "error occurred plz try again later and a link to login" + //http.Error(w, err.Error(), http.StatusInternalServerError) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + if r.URL.Query().Get("state") != session.Values["state"] { + //todo redirect to the error page stating: "error authenticating plz try to login once again" + //http.Error(w, "invalid state parameter", http.StatusBadRequest) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + token, err := h.authenticator.Config.Exchange(context.TODO(), r.URL.Query().Get("code")) + if err != nil { + log.Printf("no token found: %v", err) + //todo redirect to the error page stating: "error authenticating plz try to login once again" + //w.WriteHeader(http.StatusUnauthorized) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + rawIDToken, ok := token.Extra("id_token").(string) + if !ok { + //todo redirect to the error page stating: "error occurred plz try again later and a link to login" + //http.Error(w, "no id_token field in oauth2 token.", http.StatusInternalServerError) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + oidcConfig := &oidc.Config{ + ClientID: h.authenticator.Config.ClientID, + } + + idToken, err := h.authenticator.Provider.Verifier(oidcConfig).Verify(context.TODO(), rawIDToken) + + if err != nil { + //todo redirect to the error page stating: "error occurred plz try again later and a link to login" + //http.Error(w, "failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + // get the userInfo from the token + var profile map[string]interface{} + if err := idToken.Claims(&profile); err != nil { + //todo redirect to the error page stating: "error occurred plz try again later and a link to login" + //http.Error(w, err.Error(), http.StatusInternalServerError) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + session.Values["id_token"] = rawIDToken + session.Values["access_token"] = token.AccessToken + session.Values["profile"] = profile + + err = session.Save(r, w) + if err != nil { + //todo redirect to the error page stating: "error occurred plz try again later and a link to login" + //http.Error(w, err.Error(), http.StatusInternalServerError) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + // redirect to logged in page + http.Redirect(w, r, "/dashboard", http.StatusSeeOther) +} diff --git a/management/http_server/handler/dashboard.go b/management/http_server/handler/dashboard.go new file mode 100644 index 000000000..fd0cfe455 --- /dev/null +++ b/management/http_server/handler/dashboard.go @@ -0,0 +1,44 @@ +package handler + +import ( + "fmt" + "github.com/gorilla/sessions" + "io" + "net/http" + "strings" +) + +// Dashboard is a handler of the main page of the app (dashboard) +type Dashboard struct { + sessionStore sessions.Store +} + +func NewDashboard(sessionStore sessions.Store) *Dashboard { + return &Dashboard{ + sessionStore: sessionStore, + } +} + +// ServeHTTP verifies if user is authenticated and returns a user dashboard +func (h *Dashboard) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + session, err := h.sessionStore.Get(r, "auth-session") + if err != nil { + //todo redirect to the error page stating: "error occurred plz try again later and a link to login" + //http.Error(w, err.Error(), http.StatusInternalServerError) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + //todo get user account and relevant data to show + profile := session.Values["profile"].(map[string]interface{}) + name := profile["name"] + w.WriteHeader(200) + _, err = io.Copy(w, strings.NewReader("hello "+fmt.Sprintf("%v", name))) + if err != nil { + return + + } + + //template.RenderTemplate(w, "dashboard", session.Values["profile"]) +} diff --git a/management/http_server/handler/login.go b/management/http_server/handler/login.go new file mode 100644 index 000000000..38e28155d --- /dev/null +++ b/management/http_server/handler/login.go @@ -0,0 +1,58 @@ +package handler + +import ( + "crypto/rand" + "encoding/base64" + "github.com/gorilla/sessions" + "github.com/wiretrustee/wiretrustee/management/http_server/middleware" + "io/fs" + "net/http" +) + +// Login handler used to login a user +type Login struct { + authenticator *middleware.Authenticator + sessionStore sessions.Store +} + +func NewLogin(authenticator *middleware.Authenticator, sessionStore sessions.Store) *Login { + return &Login{ + authenticator: authenticator, + sessionStore: sessionStore, + } +} + +// ServeHTTP generates a new session state for a user and redirects the user to the auth URL +func (h *Login) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + // Generate random state + b := make([]byte, 32) + _, err := rand.Read(b) + if err != nil { + //todo redirect to the error page stating: "error occurred plz try again later and a link to login" + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + state := base64.StdEncoding.EncodeToString(b) + + session, err := h.sessionStore.Get(r, "auth-session") + if err != nil { + switch err.(type) { + case *fs.PathError: + // a case when session doesn't exist in the store but was sent by the client in the cookie -> create new session ID + // it appears that in this case session is always non empty object + session.ID = "" //nolint + default: + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + session.Values["state"] = state //nolint + err = session.Save(r, w) //nolint + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + url := h.authenticator.Config.AuthCodeURL(state) + http.Redirect(w, r, url, http.StatusTemporaryRedirect) +} diff --git a/management/http_server/handler/logout.go b/management/http_server/handler/logout.go new file mode 100644 index 000000000..2144758d4 --- /dev/null +++ b/management/http_server/handler/logout.go @@ -0,0 +1,48 @@ +package handler + +import ( + "net/http" + "net/url" +) + +// Logout logs out a user +type Logout struct { + authDomain string + authClientId string +} + +func NewLogout(authDomain string, authClientId string) *Logout { + return &Logout{authDomain: authDomain, authClientId: authClientId} +} + +// ServeHTTP redirects user to teh auth identity provider logout URL +func (h *Logout) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + logoutUrl, err := url.Parse("https://" + h.authDomain) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + logoutUrl.Path += "/v2/logout" + parameters := url.Values{} + + var scheme string + if r.TLS == nil { + scheme = "http" + } else { + scheme = "https" + } + + returnTo, err := url.Parse(scheme + "://" + r.Host + "/login") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + parameters.Add("returnTo", returnTo.String()) + parameters.Add("client_id", h.authClientId) + logoutUrl.RawQuery = parameters.Encode() + + http.Redirect(w, r, logoutUrl.String(), http.StatusTemporaryRedirect) +} diff --git a/management/http_server/middleware/auth.go b/management/http_server/middleware/auth.go new file mode 100644 index 000000000..2810ede47 --- /dev/null +++ b/management/http_server/middleware/auth.go @@ -0,0 +1,39 @@ +package middleware + +import ( + "context" + "golang.org/x/oauth2" + "log" + + "github.com/coreos/go-oidc" +) + +type Authenticator struct { + Provider *oidc.Provider + Config oauth2.Config + Ctx context.Context +} + +func NewAuthenticator(authDomain string, authClientId string, authClientSecret string, authCallback string) (*Authenticator, error) { + ctx := context.Background() + + provider, err := oidc.NewProvider(ctx, "https://"+authDomain+"/") + if err != nil { + log.Printf("failed to get provider: %v", err) + return nil, err + } + + conf := oauth2.Config{ + ClientID: authClientId, + ClientSecret: authClientSecret, + RedirectURL: authCallback, + Endpoint: provider.Endpoint(), + Scopes: []string{oidc.ScopeOpenID, "profile"}, + } + + return &Authenticator{ + Provider: provider, + Config: conf, + Ctx: ctx, + }, nil +} diff --git a/management/http_server/middleware/authenticated.go b/management/http_server/middleware/authenticated.go new file mode 100644 index 000000000..4d2bd47d4 --- /dev/null +++ b/management/http_server/middleware/authenticated.go @@ -0,0 +1,31 @@ +package middleware + +import ( + "github.com/gorilla/sessions" + "net/http" +) + +type AuthMiddleware struct { + sessionStore sessions.Store +} + +func NewAuth(sessionStore sessions.Store) *AuthMiddleware { + return &AuthMiddleware{sessionStore: sessionStore} +} + +func (am *AuthMiddleware) IsAuthenticated(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + + session, err := am.sessionStore.Get(r, "auth-session") + if err != nil { + //todo redirect to the error page stating: "error occurred plz try again later and a link to login" + //http.Error(w, err.Error(), http.StatusInternalServerError) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + if _, ok := session.Values["profile"]; !ok { + http.Redirect(w, r, "/login", http.StatusSeeOther) + } else { + next(w, r) + } +} diff --git a/management/http_server/server.go b/management/http_server/server.go new file mode 100644 index 000000000..1495f71e5 --- /dev/null +++ b/management/http_server/server.go @@ -0,0 +1,91 @@ +package http_server + +import ( + "context" + "encoding/gob" + log "github.com/sirupsen/logrus" + "github.com/wiretrustee/wiretrustee/management/http_server/handler" + "github.com/wiretrustee/wiretrustee/management/http_server/middleware" + s "github.com/wiretrustee/wiretrustee/management/server" + "golang.org/x/crypto/acme/autocert" + "net/http" + "time" + + "github.com/codegangsta/negroni" + "github.com/gorilla/sessions" +) + +type Server struct { + server *http.Server + config *s.HttpServerConfig + certManager *autocert.Manager +} + +// NewHttpsServer creates a new HTTPs server (with HTTPS support) +// The listening address will be :443 no matter what was specified in s.HttpServerConfig.Address +func NewHttpsServer(config *s.HttpServerConfig, certManager *autocert.Manager) *Server { + server := &http.Server{ + Addr: config.Address, + WriteTimeout: time.Second * 15, + ReadTimeout: time.Second * 15, + IdleTimeout: time.Second * 60, + } + return &Server{server: server, config: config, certManager: certManager} +} + +// NewHttpServer creates a new HTTP server (without HTTPS) +func NewHttpServer(config *s.HttpServerConfig) *Server { + return NewHttpsServer(config, nil) +} + +// Stop stops the http server +func (s *Server) Stop(ctx context.Context) error { + err := s.server.Shutdown(ctx) + if err != nil { + return err + } + return nil +} + +// Start defines http handlers and starts the http server. Blocks until server is shutdown. +func (s *Server) Start() error { + + sessionStore := sessions.NewFilesystemStore("", []byte("something-very-secret")) + authenticator, err := middleware.NewAuthenticator(s.config.AuthDomain, s.config.AuthClientId, s.config.AuthClientSecret, s.config.AuthCallback) + if err != nil { + log.Errorf("failed cerating authentication middleware %v", err) + return err + } + + gob.Register(map[string]interface{}{}) + + r := http.NewServeMux() + s.server.Handler = r + + r.Handle("/login", handler.NewLogin(authenticator, sessionStore)) + r.Handle("/logout", handler.NewLogout(s.config.AuthDomain, s.config.AuthClientId)) + r.Handle("/callback", handler.NewCallback(authenticator, sessionStore)) + r.Handle("/dashboard", negroni.New( + negroni.HandlerFunc(middleware.NewAuth(sessionStore).IsAuthenticated), + negroni.Wrap(handler.NewDashboard(sessionStore))), + ) + http.Handle("/", r) + + if s.certManager != nil { + // if HTTPS is enabled we reuse the listener from the cert manager + listener := s.certManager.Listener() + log.Infof("http server listening on %s", listener.Addr()) + if err = http.Serve(listener, s.certManager.HTTPHandler(r)); err != nil { + log.Errorf("failed to serve https server: %v", err) + return err + } + } else { + log.Infof("http server listening on %s", s.server.Addr) + if err = s.server.ListenAndServe(); err != nil { + log.Errorf("failed to serve http server: %v", err) + return err + } + } + + return nil +} diff --git a/management/http_server/template/templates.go b/management/http_server/template/templates.go new file mode 100644 index 000000000..ad217e614 --- /dev/null +++ b/management/http_server/template/templates.go @@ -0,0 +1,21 @@ +package template + +import ( + "html/template" + "net/http" + "os" + "path/filepath" +) + +func RenderTemplate(w http.ResponseWriter, tmpl string, data interface{}) { + cwd, _ := os.Getwd() + t, err := template.ParseFiles(filepath.Join(cwd, "./routes/"+tmpl+"/"+tmpl+".html")) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + err = t.Execute(w, data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} diff --git a/management/main.go b/management/main.go index 64ff15ccd..370fe491c 100644 --- a/management/main.go +++ b/management/main.go @@ -1,12 +1,12 @@ package main import ( - cmd2 "github.com/wiretrustee/wiretrustee/management/cmd" + "github.com/wiretrustee/wiretrustee/management/cmd" "os" ) func main() { - if err := cmd2.Execute(); err != nil { + if err := cmd.Execute(); err != nil { os.Exit(1) } } diff --git a/management/server/config.go b/management/server/config.go index bb8059de6..c11b2c92b 100644 --- a/management/server/config.go +++ b/management/server/config.go @@ -18,6 +18,16 @@ type Config struct { Datadir string LetsEncryptDomain string + + HttpConfig *HttpServerConfig +} + +type HttpServerConfig struct { + Address string + AuthDomain string + AuthClientId string + AuthClientSecret string + AuthCallback string } // Host represents a Wiretrustee host (e.g. STUN, TURN, Signal)