diff --git a/Dockerfile b/Dockerfile index b0d9332..64264f8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN go build -mod=readonly -o=/tracetrout -ldflags='-s -w' FROM alpine:3.8 ARG IPV6_SUPPORT -RUN apk add --no-cache iptables libnetfilter_queue ${IPV6_SUPPORT:+ip6tables} +RUN apk add --no-cache ca-certificates iptables libnetfilter_queue ${IPV6_SUPPORT:+ip6tables} WORKDIR /tracetrout COPY entrypoint.sh . COPY --from=builder /tracetrout . diff --git a/go.mod b/go.mod index 684345b..abca984 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,5 @@ require ( github.com/jtacoma/uritemplates v1.0.0 github.com/kelseyhightower/envconfig v1.3.0 github.com/rs/cors v1.3.0 + golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 ) diff --git a/go.sum b/go.sum index fac0eaf..9053917 100644 --- a/go.sum +++ b/go.sum @@ -10,3 +10,5 @@ github.com/kelseyhightower/envconfig v1.3.0 h1:IvRS4f2VcIQy6j4ORGIf9145T/AsUB+oY github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/rs/cors v1.3.0 h1:R0sy4XekGcOFoby9D76NXXg2birJ3WFkzGvXF9Kn3xE= github.com/rs/cors v1.3.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/main.go b/main.go index b97786d..09387fd 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ import ( "github.com/jtacoma/uritemplates" "github.com/kelseyhightower/envconfig" "github.com/rs/cors" + "golang.org/x/crypto/acme" + "golang.org/x/crypto/acme/autocert" ) var ( @@ -467,16 +469,19 @@ func (st *StreamTracker) Get(id StreamID) *Stream { } type settings struct { - Host string - Port uint16 `default:"8080"` - HopTimeout time.Duration `default:"1s" split_words:"true"` - HopRetries uint `default:"5" split_words:"true"` - HopOffset byte `default:"0" split_words:"true"` - FilterQueue uint16 `default:"0" split_words:"true"` - HTTPSEnabled bool `default:"false" envconfig:"HTTPS_ENABLED"` - HTTPSCertFile string `default:"" envconfig:"HTTPS_CERT_FILE"` - HTTPSKeyFile string `default:"" envconfig:"HTTPS_KEY_FILE"` - InfoUriTemplate string `default:"" split_words:"true"` + Host string + Port uint16 `default:"8080"` + HopTimeout time.Duration `default:"1s" split_words:"true"` + HopRetries uint `default:"5" split_words:"true"` + HopOffset byte `default:"0" split_words:"true"` + FilterQueue uint16 `default:"0" split_words:"true"` + HTTPSEnabled bool `default:"false" envconfig:"HTTPS_ENABLED"` + HTTPSCertFile string `default:"" envconfig:"HTTPS_CERT_FILE"` + HTTPSKeyFile string `default:"" envconfig:"HTTPS_KEY_FILE"` + HTTPSAutocertEnabled bool `default:"" envconfig:"HTTPS_AUTOCERT_ENABLED"` + HTTPSAutocertHosts []string `default:"" envconfig:"HTTPS_AUTOCERT_HOSTS"` + HTTPSAutocertDirCache string `default:"" envconfig:"HTTPS_AUTOCERT_DIR_CACHE"` + InfoUriTemplate string `default:"" split_words:"true"` } func (s settings) HostPort() string { @@ -506,11 +511,18 @@ func main() { if err := envconfig.Process("", &s); err != nil { log.Fatal(err) } - if s.HTTPSEnabled && (s.HTTPSCertFile == "" || s.HTTPSKeyFile == "") { - log.Fatal("HTTPS_ENABLED=true requires HTTPS_CERT_FILE and HTTPS_KEY_FILE") + + httpsEnabled := s.HTTPSEnabled + autocertEnabled := s.HTTPSAutocertEnabled + certsDefined := s.HTTPSCertFile != "" && s.HTTPSKeyFile != "" + if httpsEnabled && !autocertEnabled && !certsDefined { + log.Fatal("HTTPS_ENABLED=true requires either HTTPS_CERT_FILE and HTTPS_KEY_FILE or HTTPS_AUTOCERT_ENABLED=true") + } + if !httpsEnabled && (autocertEnabled || certsDefined) { + log.Fatal("HTTPS_AUTOCERT_ENABLED=true, HTTPS_CERT_FILE and HTTPS_KEY_FILE require HTTPS_ENABLED=true") } - if !s.HTTPSEnabled && (s.HTTPSCertFile != "" || s.HTTPSKeyFile != "") { - log.Fatal("HTTPS_CERT_FILE and HTTPS_KEY_FILE require HTTPS_ENABLED=true") + if autocertEnabled && certsDefined { + log.Fatal("HTTPS_AUTOCERT_ENABLED=true can't be defined when HTTPS_CERT_FILE and HTTPS_KEY_FILE are set") } var infoUriTmpl *uritemplates.UriTemplate @@ -658,10 +670,34 @@ func main() { TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){}, } server.SetKeepAlivesEnabled(false) - if s.HTTPSEnabled { + + if !httpsEnabled { + log.Fatal(server.ListenAndServe()) + } else if !autocertEnabled { log.Fatal(server.ListenAndServeTLS(s.HTTPSCertFile, s.HTTPSKeyFile)) } else { - log.Fatal(server.ListenAndServe()) + var cache autocert.Cache + if s.HTTPSAutocertDirCache != "" { + cache = autocert.DirCache(s.HTTPSAutocertDirCache) + } + var hostPolicy autocert.HostPolicy + if s.HTTPSAutocertHosts != nil { + autocert.HostWhitelist(s.HTTPSAutocertHosts...) + } + manager := &autocert.Manager{ + Cache: cache, + Prompt: autocert.AcceptTOS, + HostPolicy: hostPolicy, + } + server.TLSConfig = &tls.Config{ + GetCertificate: manager.GetCertificate, + NextProtos: []string{ + "http/1.1", + acme.ALPNProto, + }, + } + fmt.Println("Autocert enabled") + log.Fatal(server.ListenAndServeTLS("", "")) } }