From f7b1df0505265aa3c0fc2da3c309aeaa0ae4b4b0 Mon Sep 17 00:00:00 2001 From: "Willem@105.pve1.lan" Date: Wed, 2 Mar 2022 09:11:44 +0200 Subject: [PATCH] =?UTF-8?q?Created=20separate=20config=20with=20shared=20s?= =?UTF-8?q?tructures=20for=20server=20&=20client=20flags=20Standardize=20s?= =?UTF-8?q?erver=20&=20client=20flag=20names=20Auto=20accept=20terms=20if?= =?UTF-8?q?=20=E2=80=98acmeEmail=E2=80=99=20is=20provided=20Added=20?= =?UTF-8?q?=E2=80=98defaultCA=E2=80=99=20flag=20Added=20=E2=80=98autoCerts?= =?UTF-8?q?=E2=80=99=20flag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- boringproxy.go | 101 +++++++++++++----------------- client.go | 31 +++++---- cmd/boringproxy/main.go | 39 +----------- config.go | 135 ++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 + 6 files changed, 198 insertions(+), 111 deletions(-) create mode 100644 config.go diff --git a/boringproxy.go b/boringproxy.go index 06deff4..99f578c 100644 --- a/boringproxy.go +++ b/boringproxy.go @@ -4,7 +4,6 @@ import ( "bufio" "context" "crypto/tls" - "flag" "fmt" "io" "log" @@ -42,29 +41,11 @@ type Server struct { httpListener *PassthroughListener } -func Listen() { - flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError) - newAdminDomain := flagSet.String("admin-domain", "", "Admin Domain") - sshServerPort := flagSet.Int("ssh-server-port", 22, "SSH Server Port") - dbDir := flagSet.String("db-dir", "", "Database file directory") - certDir := flagSet.String("cert-dir", "", "TLS cert directory") - printLogin := flagSet.Bool("print-login", false, "Prints admin login information") - httpPort := flagSet.Int("http-port", 80, "HTTP (insecure) port") - httpsPort := flagSet.Int("https-port", 443, "HTTPS (secure) port") - allowHttp := flagSet.Bool("allow-http", false, "Allow unencrypted (HTTP) requests") - publicIp := flagSet.String("public-ip", "", "Public IP") - behindProxy := flagSet.Bool("behind-proxy", false, "Whether we're running behind another reverse proxy") - acmeEmail := flagSet.String("acme-email", "", "Email for ACME (ie Let's Encrypt)") - acmeUseStaging := flagSet.Bool("acme-use-staging", false, "Use ACME (ie Let's Encrypt) staging servers") - acceptCATerms := flagSet.Bool("accept-ca-terms", false, "Automatically accept CA terms") - err := flagSet.Parse(os.Args[2:]) - if err != nil { - fmt.Fprintf(os.Stderr, "%s: parsing flags: %s\n", os.Args[0], err) - } +func Listen(config *ServerConfig) { log.Println("Starting up") - db, err := NewDatabase(*dbDir) + db, err := NewDatabase(config.dbDir) if err != nil { log.Fatal(err) } @@ -73,8 +54,8 @@ func Listen() { var ip string - if *publicIp != "" { - ip = *publicIp + if config.publicIp != "" { + ip = config.publicIp } else { ip, err = namedropClient.GetPublicIp() if err != nil { @@ -82,45 +63,49 @@ func Listen() { } } - err = namedrop.CheckPublicAddress(ip, *httpPort) + err = namedrop.CheckPublicAddress(ip, config.httpPort) if err != nil { - fmt.Printf("WARNING: Failed to access %s:%d from the internet\n", ip, *httpPort) + fmt.Printf("WARNING: Failed to access %s:%d from the internet\n", ip, config.httpPort) } - err = namedrop.CheckPublicAddress(ip, *httpsPort) + err = namedrop.CheckPublicAddress(ip, config.httpsPort) if err != nil { - fmt.Printf("WARNING: Failed to access %s:%d from the internet\n", ip, *httpsPort) + fmt.Printf("WARNING: Failed to access %s:%d from the internet\n", ip, config.httpsPort) } - autoCerts := true - if *httpPort != 80 || *httpsPort != 443 { - fmt.Printf("WARNING: LetsEncrypt only supports HTTP/HTTPS ports 80/443. You are using %d/%d. Disabling automatic certificate management\n", *httpPort, *httpsPort) + var autoCerts bool + if config.httpPort != 80 || config.httpsPort != 443 { + fmt.Printf("WARNING: LetsEncrypt only supports HTTP/HTTPS ports 80/443. You are using %d/%d. Disabling automatic certificate management\n", config.httpPort, config.httpsPort) autoCerts = false + } else { + autoCerts = config.myCertConfig.autoCerts } - if *certDir != "" { - certmagic.Default.Storage = &certmagic.FileStorage{*certDir} - } - //certmagic.DefaultACME.DisableHTTPChallenge = true - //certmagic.DefaultACME.DisableTLSALPNChallenge = true - - if *acmeEmail != "" { - certmagic.DefaultACME.Email = *acmeEmail + if config.myCertConfig.certDir != "" { + certmagic.Default.Storage = &certmagic.FileStorage{config.myCertConfig.certDir} } - if *acceptCATerms { + if config.myCertConfig.acmeEmail != "" { + certmagic.DefaultACME.Email = config.myCertConfig.acmeEmail certmagic.DefaultACME.Agreed = true - log.Print(fmt.Sprintf("Automatic agreement to CA terms with email (%s)", *acmeEmail)) + log.Print(fmt.Sprintf("Automatic agreement to CA terms with email (%s)", config.myCertConfig.acmeEmail)) } - if *acmeUseStaging { + switch config.myCertConfig.defaultCA { + case "production": + certmagic.DefaultACME.CA = certmagic.LetsEncryptProductionCA + + case "staging": certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA + + default: + certmagic.DefaultACME.CA = config.myCertConfig.defaultCA } certConfig := certmagic.NewDefault() - if *newAdminDomain != "" { - db.SetAdminDomain(*newAdminDomain) + if config.adminDomain != "" { + db.SetAdminDomain(config.adminDomain) } adminDomain := db.GetAdminDomain() @@ -152,29 +137,29 @@ func Listen() { } - if *printLogin { + if config.printLogin { for token, tokenData := range db.GetTokens() { if tokenData.Owner == "admin" && tokenData.Client == "" { - printLoginInfo(token, db.GetAdminDomain(), *httpsPort) + printLoginInfo(token, db.GetAdminDomain(), config.httpsPort) break } } } - config := &Config{ - SshServerPort: *sshServerPort, + TunnelManagerConfig := &Config{ + SshServerPort: config.sshServerPort, PublicIp: ip, namedropClient: namedropClient, autoCerts: autoCerts, } - tunMan := NewTunnelManager(config, db, certConfig) + tunMan := NewTunnelManager(TunnelManagerConfig, db, certConfig) auth := NewAuth(db) - api := NewApi(config, db, auth, tunMan) + api := NewApi(TunnelManagerConfig, db, auth, tunMan) - webUiHandler := NewWebUiHandler(config, db, api, auth) + webUiHandler := NewWebUiHandler(TunnelManagerConfig, db, api, auth) httpClient := &http.Client{ // Don't follow redirects @@ -232,7 +217,7 @@ func Listen() { host := namedropTokenData.Scopes[0].Host recordType := "AAAA" - if IsIPv4(config.PublicIp) { + if IsIPv4(TunnelManagerConfig.PublicIp) { recordType = "A" } @@ -240,7 +225,7 @@ func Listen() { Domain: domain, Host: host, Type: recordType, - Value: config.PublicIp, + Value: TunnelManagerConfig.PublicIp, TTL: 300, } @@ -299,23 +284,23 @@ func Listen() { return } - proxyRequest(w, r, tunnel, httpClient, "localhost", tunnel.TunnelPort, *behindProxy) + proxyRequest(w, r, tunnel, httpClient, "localhost", tunnel.TunnelPort, config.behindProxy) } }) go func() { - if *allowHttp { - if err := http.ListenAndServe(fmt.Sprintf(":%d", *httpPort), nil); err != nil { + if config.allowHttp { + if err := http.ListenAndServe(fmt.Sprintf(":%d", config.httpPort), nil); err != nil { log.Fatalf("ListenAndServe error: %v", err) } } else { redirectTLS := func(w http.ResponseWriter, r *http.Request) { - url := fmt.Sprintf("https://%s:%d%s", r.Host, *httpsPort, r.RequestURI) + url := fmt.Sprintf("https://%s:%d%s", r.Host, config.httpsPort, r.RequestURI) http.Redirect(w, r, url, http.StatusMovedPermanently) } - if err := http.ListenAndServe(fmt.Sprintf(":%d", *httpPort), http.HandlerFunc(redirectTLS)); err != nil { + if err := http.ListenAndServe(fmt.Sprintf(":%d", config.httpPort), http.HandlerFunc(redirectTLS)); err != nil { log.Fatalf("ListenAndServe error: %v", err) } } @@ -324,7 +309,7 @@ func Listen() { go http.Serve(tlsListener, nil) - listener, err := net.Listen("tcp", fmt.Sprintf(":%d", *httpsPort)) + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", config.httpsPort)) if err != nil { log.Fatal(err) } diff --git a/client.go b/client.go index 9cf2d45..9f5199b 100644 --- a/client.go +++ b/client.go @@ -31,18 +31,6 @@ type Client struct { behindProxy bool } -type ClientConfig struct { - ServerAddr string `json:"serverAddr,omitempty"` - Token string `json:"token,omitempty"` - ClientName string `json:"clientName,omitempty"` - User string `json:"user,omitempty"` - CertDir string `json:"certDir,omitempty"` - AcmeEmail string `json:"acmeEmail,omitempty"` - AcmeUseStaging bool `json:"acmeUseStaging,omitempty"` - DnsServer string `json:"dnsServer,omitempty"` - BehindProxy bool `json:"behindProxy,omitempty"` -} - func NewClient(config *ClientConfig) (*Client, error) { if config.DnsServer != "" { @@ -72,16 +60,25 @@ func NewClient(config *ClientConfig) (*Client, error) { certmagic.DefaultACME.DisableHTTPChallenge = true - if config.CertDir != "" { - certmagic.Default.Storage = &certmagic.FileStorage{config.CertDir} + if config.myCertConfig.certDir != "" { + certmagic.Default.Storage = &certmagic.FileStorage{config.myCertConfig.certDir} } - if config.AcmeEmail != "" { - certmagic.DefaultACME.Email = config.AcmeEmail + if config.myCertConfig.acmeEmail != "" { + certmagic.DefaultACME.Email = config.myCertConfig.acmeEmail + certmagic.DefaultACME.Agreed = true + log.Print(fmt.Sprintf("Automatic agreement to CA terms with email (%s)", config.myCertConfig.acmeEmail)) } - if config.AcmeUseStaging { + switch config.myCertConfig.defaultCA { + case "production": + certmagic.DefaultACME.CA = certmagic.LetsEncryptProductionCA + + case "staging": certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA + + default: + certmagic.DefaultACME.CA = config.myCertConfig.defaultCA } certConfig := certmagic.NewDefault() diff --git a/cmd/boringproxy/main.go b/cmd/boringproxy/main.go index 5331dc1..2798bc2 100644 --- a/cmd/boringproxy/main.go +++ b/cmd/boringproxy/main.go @@ -83,43 +83,10 @@ func main() { } } case "server": - boringproxy.Listen() + config := boringproxy.SetServerConfig() + boringproxy.Listen(config) case "client": - flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError) - server := flagSet.String("server", "", "boringproxy server") - token := flagSet.String("token", "", "Access token") - name := flagSet.String("client-name", "", "Client name") - user := flagSet.String("user", "", "user") - certDir := flagSet.String("cert-dir", "", "TLS cert directory") - acmeEmail := flagSet.String("acme-email", "", "Email for ACME (ie Let's Encrypt)") - acmeUseStaging := flagSet.Bool("acme-use-staging", false, "Use ACME (ie Let's Encrypt) staging servers") - dnsServer := flagSet.String("dns-server", "", "Custom DNS server") - behindProxy := flagSet.Bool("behind-proxy", false, "Whether we're running behind another reverse proxy") - - err := flagSet.Parse(os.Args[2:]) - if err != nil { - fmt.Fprintf(os.Stderr, "%s: parsing flags: %s\n", os.Args[0], err) - } - - if *server == "" { - fail("-server is required") - } - - if *token == "" { - fail("-token is required") - } - - config := &boringproxy.ClientConfig{ - ServerAddr: *server, - Token: *token, - ClientName: *name, - User: *user, - CertDir: *certDir, - AcmeEmail: *acmeEmail, - AcmeUseStaging: *acmeUseStaging, - DnsServer: *dnsServer, - BehindProxy: *behindProxy, - } + config := boringproxy.SetClientConfig() ctx := context.Background() diff --git a/config.go b/config.go new file mode 100644 index 0000000..c2e9b59 --- /dev/null +++ b/config.go @@ -0,0 +1,135 @@ +package boringproxy + +import ( + "flag" + "fmt" + "os" +) + +type myCertConfig struct { + certDir string + acmeEmail string + defaultCA string + autoCerts bool +} + +type ServerConfig struct { + adminDomain string + sshServerPort int + dbDir string + printLogin bool + httpPort int + httpsPort int + allowHttp bool + publicIp string + behindProxy bool + myCertConfig myCertConfig +} + +type ClientConfig struct { + ServerAddr string `json:"serverAddr,omitempty"` + Token string `json:"token,omitempty"` + ClientName string `json:"clientName,omitempty"` + User string `json:"user,omitempty"` + CertDir string `json:"certDir,omitempty"` + AcmeEmail string `json:"acmeEmail,omitempty"` + AcmeUseStaging bool `json:"acmeUseStaging,omitempty"` + DnsServer string `json:"dnsServer,omitempty"` + BehindProxy bool `json:"behindProxy,omitempty"` + myCertConfig myCertConfig +} + +func fail(msg string) { + fmt.Fprintln(os.Stderr, msg) + os.Exit(1) +} + +func SetServerConfig() *ServerConfig { + flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError) + adminDomain := flagSet.String("admin-domain", "BP_ADMIN_DOMAIN", "Admin Domain") + sshServerPort := flagSet.Int("ssh-server-port", 22, "SSH Server Port") + dbDir := flagSet.String("db-dir", "", "Database file directory") + printLogin := flagSet.Bool("print-login", false, "Prints admin login information") + httpPort := flagSet.Int("http-port", 80, "HTTP (insecure) port") + httpsPort := flagSet.Int("https-port", 443, "HTTPS (secure) port") + allowHttp := flagSet.Bool("allow-http", false, "Allow unencrypted (HTTP) requests") + publicIp := flagSet.String("public-ip", "", "Public IP") + behindProxy := flagSet.Bool("behind-proxy", false, "Whether we're running behind another reverse proxy") + certDir := flagSet.String("cert-dir", "", "TLS cert directory") + acmeEmail := flagSet.String("acme-email", "", "Email for ACME (ie Let's Encrypt)") + defaultCA := flagSet.String("ca", "production", "Default ACME CA") + autoCerts := flagSet.Bool("autocert", true, "Enable/Disable auto certs") + + err := flagSet.Parse(os.Args[2:]) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: parsing flags: %s\n", os.Args[0], err) + } + + var myCertConfig = &myCertConfig{ + certDir: *certDir, + acmeEmail: *acmeEmail, + defaultCA: *defaultCA, + autoCerts: *autoCerts, + } + + var config = &ServerConfig{ + adminDomain: *adminDomain, + sshServerPort: *sshServerPort, + dbDir: *dbDir, + printLogin: *printLogin, + httpPort: *httpPort, + httpsPort: *httpsPort, + allowHttp: *allowHttp, + publicIp: *publicIp, + behindProxy: *behindProxy, + myCertConfig: *myCertConfig, + } + + return config +} + +func SetClientConfig() *ClientConfig { + flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError) + server := flagSet.String("server", "", "boringproxy server") + token := flagSet.String("token", "", "Access token") + name := flagSet.String("client-name", "", "Client name") + user := flagSet.String("user", "", "user") + dnsServer := flagSet.String("dns-server", "", "Custom DNS server") + behindProxy := flagSet.Bool("behind-proxy", false, "Whether we're running behind another reverse proxy") + certDir := flagSet.String("cert-dir", "", "TLS cert directory") + acmeEmail := flagSet.String("acme-email", "", "Email for ACME (ie Let's Encrypt)") + defaultCA := flagSet.String("ca", "production", "Default ACME CA") + autoCerts := flagSet.Bool("autocert", true, "Enable/Disable auto certs") + + err := flagSet.Parse(os.Args[2:]) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: parsing flags: %s\n", os.Args[0], err) + } + + if *server == "" { + fail("-server is required") + } + + if *token == "" { + fail("-token is required") + } + + var myCertConfig = &myCertConfig{ + certDir: *certDir, + acmeEmail: *acmeEmail, + defaultCA: *defaultCA, + autoCerts: *autoCerts, + } + + var config = &ClientConfig{ + ServerAddr: *server, + Token: *token, + ClientName: *name, + User: *user, + DnsServer: *dnsServer, + BehindProxy: *behindProxy, + myCertConfig: *myCertConfig, + } + + return config +} diff --git a/go.mod b/go.mod index 8d4033c..a9f5671 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ go 1.17 require ( github.com/caddyserver/certmagic v0.15.2 + github.com/joho/godotenv v1.4.0 github.com/mdp/qrterminal/v3 v3.0.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/takingnames/namedrop-go v0.7.0 diff --git a/go.sum b/go.sum index 72c370b..d4d2842 100644 --- a/go.sum +++ b/go.sum @@ -101,6 +101,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m 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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=