From d437b73a8c2dda9884c92d2be44727e66c2289e2 Mon Sep 17 00:00:00 2001 From: "kali kaneko (leap communications)" Date: Fri, 24 Jan 2020 21:19:19 -0600 Subject: refactor into cmd/pkg --- .gitignore | 3 +- Makefile | 2 +- certs.go | 81 ---------------------------- cmd/vpnweb/vpnweb.go | 50 +++++++++++++++++ config/CONFIG | 2 +- go.mod | 9 ++++ go.sum | 12 +++++ main.go | 144 ------------------------------------------------- pkg/auth/middleware.go | 12 +++++ pkg/config/main.go | 97 +++++++++++++++++++++++++++++++++ pkg/web/certs.go | 84 +++++++++++++++++++++++++++++ pkg/web/handlers.go | 19 +++++++ 12 files changed, 287 insertions(+), 228 deletions(-) delete mode 100644 certs.go create mode 100644 cmd/vpnweb/vpnweb.go create mode 100644 go.sum delete mode 100644 main.go create mode 100644 pkg/auth/middleware.go create mode 100644 pkg/config/main.go create mode 100644 pkg/web/certs.go create mode 100644 pkg/web/handlers.go diff --git a/.gitignore b/.gitignore index f352413..2f043b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -vpnweb deploy/* .mypy_cache *.swp *.swo +vpnweb +public/* diff --git a/Makefile b/Makefile index d34d541..50e53ab 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ build: - go build + go build cmd/vpnweb/vpnweb.go demo: . config/CONFIG && ./vpnweb -notls clean: diff --git a/certs.go b/certs.go deleted file mode 100644 index 1138ff1..0000000 --- a/certs.go +++ /dev/null @@ -1,81 +0,0 @@ -package main - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "io" - "math/big" - mrand "math/rand" - "time" -) - -const certPrefix = "UNLIMITED" - -var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz") - -func randStringRunes(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = letterRunes[mrand.Intn(len(letterRunes))] - } - return string(b) -} - -type caInfo struct { - cacrt, cakey string -} - -func newCaInfo(cacrt string, cakey string) caInfo { - return caInfo{cacrt, cakey} -} - -// CertWriter main handler -func (ci *caInfo) CertWriter(out io.Writer) { - catls, err := tls.LoadX509KeyPair(ci.cacrt, ci.cakey) - - if err != nil { - panic(err) - } - ca, err := x509.ParseCertificate(catls.Certificate[0]) - if err != nil { - panic(err) - } - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - - subjectKeyID := make([]byte, 20) - rand.Read(subjectKeyID) - - _ = randStringRunes(25) - // Prepare certificate - cert := &x509.Certificate{ - SerialNumber: serialNumber, - - Subject: pkix.Name{ - //CommonName: certPrefix + randStringRunes(25), - CommonName: certPrefix, - }, - NotBefore: time.Now().AddDate(0, 0, -7), - NotAfter: time.Now().AddDate(0, 0, expiryDays), - - SubjectKeyId: subjectKeyID, - - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - KeyUsage: x509.KeyUsageDigitalSignature, - } - priv, _ := rsa.GenerateKey(rand.Reader, keySize) - pub := &priv.PublicKey - - // Sign the certificate - certB, err := x509.CreateCertificate(rand.Reader, cert, ca, pub, catls.PrivateKey) - - // Write the private Key - pem.Encode(out, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) - - // Write the public key - pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: certB}) -} diff --git a/cmd/vpnweb/vpnweb.go b/cmd/vpnweb/vpnweb.go new file mode 100644 index 0000000..86fc38c --- /dev/null +++ b/cmd/vpnweb/vpnweb.go @@ -0,0 +1,50 @@ +package main + +import ( + "log" + "net/http" + + //"0xacab.org/leap/pkg/auth" + "0xacab.org/leap/vpnweb/pkg/config" + "0xacab.org/leap/vpnweb/pkg/web" +) + +func main() { + opts := new(config.Opts) + config.InitializeFlags(opts) + config.CheckConfigurationOptions(opts) + + ci := web.NewCaInfo(opts.CaCrt, opts.CaKey) + ch := web.CertHandler{ci} + + /* protected routes */ + + /* TODO ---- + http.HandleFunc("/3/auth", auth.AuthMiddleware(opts.Auth)) + http.HandleFunc("/3/refresh-token", auth.RefreshAuthMiddleware(opts.Auth)) + http.HandleFunc("/3/cert", jwtMiddleware.Handler(ch.certResponder)) + */ + + http.HandleFunc("/3/cert", ch.CertResponder) + + /* static files */ + + /* TODO -- pass static file path in options */ + + web.HttpFileHandler("/3/configs.json", "./public/3/configs.json") + web.HttpFileHandler("/3/service.json", "./public/3/service.json") + web.HttpFileHandler("/3/config/eip-service.json", "./public/3/eip-service.json") + web.HttpFileHandler("/3/ca.crt", "./public/ca.crt") + web.HttpFileHandler("/provider.json", "./public/provider.json") + web.HttpFileHandler("/ca.crt", "./public/ca.crt") + + pstr := ":" + opts.Port + log.Println("Listening in port", opts.Port) + + if opts.Notls == true { + log.Fatal(http.ListenAndServe(pstr, nil)) + } else { + log.Fatal(http.ListenAndServeTLS(pstr, opts.TlsCrt, opts.TlsKey, nil)) + + } +} diff --git a/config/CONFIG b/config/CONFIG index d21bd09..04b4496 100755 --- a/config/CONFIG +++ b/config/CONFIG @@ -1,4 +1,4 @@ export VPNWEB_AUTH=sip export VPNWEB_CAKEY=test/files/ca.key export VPNWEB_CACRT=test/files/ca.crt -export VPNWEB_PORT=3000 +export VPNWEB_PORT=8000 diff --git a/go.mod b/go.mod index cb75645..11be102 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,12 @@ module 0xacab.org/leap/vpnweb go 1.12 + +require ( + github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b + github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab // indirect + github.com/gorilla/mux v1.7.3 // indirect + github.com/urfave/negroni v1.0.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7211e92 --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b h1:CvoEHGmxWl5kONC5icxwqV899dkf4VjOScbxLpllEnw= +github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab h1:xveKWz2iaueeTaUgdetzel+U7exyigDYBryyVfV/rZk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= diff --git a/main.go b/main.go deleted file mode 100644 index 3259d1f..0000000 --- a/main.go +++ /dev/null @@ -1,144 +0,0 @@ -package main - -import ( - "flag" - "log" - "net/http" - "os" - "reflect" -) - -const keySize = 2048 -const expiryDays = 28 -const DefaultAuthenticationModule = "anonymous" - -type certHandler struct { - cainfo caInfo -} - -func (ch *certHandler) certResponder(w http.ResponseWriter, r *http.Request) { - ch.cainfo.CertWriter(w) -} - -type Opts struct { - Notls bool - CaCrt string - CaKey string - TlsCrt string - TlsKey string - Port string - Auth string -} - -func (o *Opts) fallbackToEnv(field string, envVar string, defaultVal string) { - r := reflect.ValueOf(o) - f := reflect.Indirect(r).FieldByName(field) - - if f.String() == "" { - val, exists := os.LookupEnv(envVar) - if exists && val != "" { - f.SetString(val) - } else { - f.SetString(defaultVal) - } - } -} - -func doCaFilesSanityCheck(caCrt string, caKey string) { - if _, err := os.Stat(caCrt); os.IsNotExist(err) { - log.Fatal("cannot find caCrt file") - } - if _, err := os.Stat(caKey); os.IsNotExist(err) { - log.Fatal("cannot find caKey file") - } -} - -func doTlsFilesSanityCheck(tlsCrt string, tlsKey string) { - if _, err := os.Stat(tlsCrt); os.IsNotExist(err) { - log.Fatal("cannot find tlsCrt file") - } - if _, err := os.Stat(tlsKey); os.IsNotExist(err) { - log.Fatal("cannot find tlsKey file") - } -} - -func httpFileHandler(route string, path string) { - http.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) { - http.ServeFile(w, r, path) - }) -} - -func initializeFlags(opts *Opts) { - - flag.BoolVar(&opts.Notls, "notls", false, "disable TLS on the service") - flag.StringVar(&opts.CaCrt, "caCrt", "", "path to the CA public key") - flag.StringVar(&opts.CaKey, "caKey", "", "path to the CA private key") - flag.StringVar(&opts.TlsCrt, "tls_crt", "", "path to the cert file for TLS") - flag.StringVar(&opts.TlsKey, "tls_key", "", "path to the key file for TLS") - flag.StringVar(&opts.Port, "port", "", "port where the server will listen (default: 8000)") - flag.StringVar(&opts.Auth, "auth", "", "authentication module (anonymous, sip)") - flag.Parse() - - opts.fallbackToEnv("CaCrt", "VPNWEB_CACRT", "") - opts.fallbackToEnv("CaKey", "VPNWEB_CAKEY", "") - opts.fallbackToEnv("TlsCrt", "VPNWEB_TLSCRT", "") - opts.fallbackToEnv("TlsKey", "VPNWEB_TLSKEY", "") - opts.fallbackToEnv("Port", "VPNWEB_PORT", "8000") - opts.fallbackToEnv("Auth", "VPNWEB_AUTH", DefaultAuthenticationModule) -} - -func checkConfigurationOptions(opts *Opts) { - - if opts.CaCrt == "" { - log.Fatal("missing caCrt parameter") - } - if opts.CaKey == "" { - log.Fatal("missing caKey parameter") - } - - if opts.Notls == false { - if opts.TlsCrt == "" { - log.Fatal("missing tls_crt parameter. maybe use -notls?") - } - if opts.TlsKey == "" { - log.Fatal("missing tls_key parameter. maybe use -notls?") - } - } - - doCaFilesSanityCheck(opts.CaCrt, opts.CaKey) - if opts.Notls == false { - doTlsFilesSanityCheck(opts.TlsCrt, opts.TlsKey) - } - - log.Println("authentication module:", opts.Auth) - - // TODO -- check authentication module is valud, bail out otherwise -} - -func main() { - opts := new(Opts) - initializeFlags(opts) - checkConfigurationOptions(opts) - - ci := newCaInfo(opts.CaCrt, opts.CaKey) - ch := certHandler{ci} - - // add routes here - http.HandleFunc("/3/cert", ch.certResponder) - httpFileHandler("/3/configs.json", "./public/3/configs.json") - httpFileHandler("/3/service.json", "./public/3/service.json") - httpFileHandler("/3/config/eip-service.json", "./public/3/eip-service.json") - httpFileHandler("/provider.json", "./public/provider.json") - httpFileHandler("/ca.crt", "./public/ca.crt") - httpFileHandler("/3/ca.crt", "./public/ca.crt") - - pstr := ":" + opts.Port - log.Println("serving vpnweb in port", opts.Port) - - if opts.Notls == true { - log.Fatal(http.ListenAndServe(pstr, nil)) - } else { - log.Fatal(http.ListenAndServeTLS(pstr, opts.TlsCrt, opts.TlsKey, nil)) - - } -} diff --git a/pkg/auth/middleware.go b/pkg/auth/middleware.go new file mode 100644 index 0000000..a3a955c --- /dev/null +++ b/pkg/auth/middleware.go @@ -0,0 +1,12 @@ +package auth + +import () + +import ( + "github.com/auth0/go-jwt-middleware" + jwt "github.com/dgrijalva/jwt-go" +) + +func getProtectedHandler() { + jwtMiddleware.Handler(CertHandler) +} diff --git a/pkg/config/main.go b/pkg/config/main.go new file mode 100644 index 0000000..142738d --- /dev/null +++ b/pkg/config/main.go @@ -0,0 +1,97 @@ +package config + +import ( + "flag" + "log" + "os" + "reflect" +) + +const DefaultAuthenticationModule = "anonymous" + +type Opts struct { + Notls bool + CaCrt string + CaKey string + TlsCrt string + TlsKey string + Port string + Auth string +} + +func (o *Opts) fallbackToEnv(field string, envVar string, defaultVal string) { + r := reflect.ValueOf(o) + f := reflect.Indirect(r).FieldByName(field) + + if f.String() == "" { + val, exists := os.LookupEnv(envVar) + if exists && val != "" { + f.SetString(val) + } else { + f.SetString(defaultVal) + } + } +} + +func doCaFilesSanityCheck(caCrt string, caKey string) { + if _, err := os.Stat(caCrt); os.IsNotExist(err) { + log.Fatal("cannot find caCrt file") + } + if _, err := os.Stat(caKey); os.IsNotExist(err) { + log.Fatal("cannot find caKey file") + } +} + +func doTlsFilesSanityCheck(tlsCrt string, tlsKey string) { + if _, err := os.Stat(tlsCrt); os.IsNotExist(err) { + log.Fatal("cannot find tlsCrt file") + } + if _, err := os.Stat(tlsKey); os.IsNotExist(err) { + log.Fatal("cannot find tlsKey file") + } +} + +func InitializeFlags(opts *Opts) { + flag.BoolVar(&opts.Notls, "notls", false, "disable TLS on the service") + flag.StringVar(&opts.CaCrt, "caCrt", "", "path to the CA public key") + flag.StringVar(&opts.CaKey, "caKey", "", "path to the CA private key") + flag.StringVar(&opts.TlsCrt, "tls_crt", "", "path to the cert file for TLS") + flag.StringVar(&opts.TlsKey, "tls_key", "", "path to the key file for TLS") + flag.StringVar(&opts.Port, "port", "", "port where the server will listen (default: 8000)") + flag.StringVar(&opts.Auth, "auth", "", "authentication module (anonymous, sip)") + flag.Parse() + + opts.fallbackToEnv("CaCrt", "VPNWEB_CACRT", "") + opts.fallbackToEnv("CaKey", "VPNWEB_CAKEY", "") + opts.fallbackToEnv("TlsCrt", "VPNWEB_TLSCRT", "") + opts.fallbackToEnv("TlsKey", "VPNWEB_TLSKEY", "") + opts.fallbackToEnv("Port", "VPNWEB_PORT", "8000") + opts.fallbackToEnv("Auth", "VPNWEB_AUTH", DefaultAuthenticationModule) +} + +func CheckConfigurationOptions(opts *Opts) { + if opts.CaCrt == "" { + log.Fatal("missing caCrt parameter") + } + if opts.CaKey == "" { + log.Fatal("missing caKey parameter") + } + + if opts.Notls == false { + if opts.TlsCrt == "" { + log.Fatal("missing tls_crt parameter. maybe use -notls?") + } + if opts.TlsKey == "" { + log.Fatal("missing tls_key parameter. maybe use -notls?") + } + } + + doCaFilesSanityCheck(opts.CaCrt, opts.CaKey) + if opts.Notls == false { + doTlsFilesSanityCheck(opts.TlsCrt, opts.TlsKey) + } + + log.Println("Authentication module:", opts.Auth) + + // TODO -- check authentication module is valud, bail out otherwise +} diff --git a/pkg/web/certs.go b/pkg/web/certs.go new file mode 100644 index 0000000..8c5d423 --- /dev/null +++ b/pkg/web/certs.go @@ -0,0 +1,84 @@ +package web + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "io" + "math/big" + mrand "math/rand" + "time" +) + +const keySize = 2048 +const expiryDays = 28 +const certPrefix = "UNLIMITED" + +var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz") + +func randStringRunes(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letterRunes[mrand.Intn(len(letterRunes))] + } + return string(b) +} + +type caInfo struct { + cacrt, cakey string +} + +func NewCaInfo(cacrt string, cakey string) caInfo { + return caInfo{cacrt, cakey} +} + +// CertWriter main handler + +func (ci *caInfo) CertWriter(out io.Writer) { + catls, err := tls.LoadX509KeyPair(ci.cacrt, ci.cakey) + + if err != nil { + panic(err) + } + ca, err := x509.ParseCertificate(catls.Certificate[0]) + if err != nil { + panic(err) + } + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + + subjectKeyID := make([]byte, 20) + rand.Read(subjectKeyID) + + _ = randStringRunes(25) + // Prepare certificate + cert := &x509.Certificate{ + SerialNumber: serialNumber, + + Subject: pkix.Name{ + //CommonName: certPrefix + randStringRunes(25), + CommonName: certPrefix, + }, + NotBefore: time.Now().AddDate(0, 0, -7), + NotAfter: time.Now().AddDate(0, 0, expiryDays), + + SubjectKeyId: subjectKeyID, + + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } + priv, _ := rsa.GenerateKey(rand.Reader, keySize) + pub := &priv.PublicKey + + // Sign the certificate + certB, err := x509.CreateCertificate(rand.Reader, cert, ca, pub, catls.PrivateKey) + + // Write the private Key + pem.Encode(out, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) + + // Write the public key + pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: certB}) +} diff --git a/pkg/web/handlers.go b/pkg/web/handlers.go new file mode 100644 index 0000000..c4f2e9a --- /dev/null +++ b/pkg/web/handlers.go @@ -0,0 +1,19 @@ +package web + +import ( + "net/http" +) + +type CertHandler struct { + Cainfo caInfo +} + +func (ch *CertHandler) CertResponder(w http.ResponseWriter, r *http.Request) { + ch.Cainfo.CertWriter(w) +} + +func HttpFileHandler(route string, path string) { + http.HandleFunc(route, func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, path) + }) +} -- cgit v1.2.3