Skip to content

feat(crypto): enabled the use of certificate/key pairs from disk vs. … #66

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 88 additions & 50 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ var (
// The version is set by the build command.
version string

// The TLS Certificate/Key pair
tlsCertificate string
tlsKey string

// httpd
httpAddr string
httpHost string
Expand Down Expand Up @@ -90,6 +94,8 @@ func init() {
cli.StringVar(&backlink, "backlink", "", "backlink (optional)")
cli.StringVar(&httpHost, "http-host", "", "HTTP host")
cli.StringVar(&httpAddr, "http-addr", ":80", "HTTP listen address")
cli.StringVar(&tlsCertificate, "certificate", "", "enable TLS using a custom certificate/key pair")
cli.StringVar(&tlsKey, "key", "", "enable TLS using a custom certificate/key pair")
cli.BoolVar(&httpInsecure, "http-insecure", false, "enable sessions cookies for http (no https) not recommended")
cli.BoolVar(&letsencrypt, "letsencrypt", true, "enable TLS using Let's Encrypt on port 443")
cli.BoolVar(&showVersion, "version", false, "display version and exit")
Expand Down Expand Up @@ -199,12 +205,16 @@ func main() {
//
// Server
//

httpTimeout := 10 * time.Minute
maxHeaderBytes := 10 * (1024 * 1024)

if tlsCertificate != "" && tlsKey != "" {
logger.Info("Unable to leverage both Let's Encrypt and certificate/key flags at the same time - using the certificate/key pair...")
letsencrypt = false
}

// Plain text web server for use behind a reverse proxy.
if !letsencrypt {
if !letsencrypt && tlsCertificate == "" || tlsKey == "" {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if !letsencrypt && tlsCertificate == "" || tlsKey == "" {
if !letsencrypt || tlsCertificate == "" || tlsKey == "" {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when:
  le == false 
  kp == true

(!le || kp) then pass (which we don't want, correct?)

when:
  le == false 
  kp == true

(!le && cert) then fail (which is what we want, no?)

So it should stay a && and not be changed to a || - or am I missing it?

httpd := &http.Server{
Handler: r,
Addr: net.JoinHostPort(httpIP, httpPort),
Expand All @@ -213,6 +223,7 @@ func main() {
MaxHeaderBytes: maxHeaderBytes,
}
hostport := net.JoinHostPort(httpHost, httpPort)

if httpPort == "80" {
hostport = httpHost
}
Expand All @@ -224,48 +235,8 @@ func main() {
logger.Fatal(httpd.ListenAndServe())
}

// Let's Encrypt TLS mode

// autocert
certmanager := autocert.Manager{
Prompt: autocert.AcceptTOS,
Cache: autocert.DirCache(filepath.Join(datadir, "letsencrypt")),
HostPolicy: func(_ context.Context, host string) error {
host = strings.TrimPrefix(host, "www.")
if host == httpHost {
return nil
}
if host == config.FindInfo().Domain {
return nil
}
return fmt.Errorf("autocert: host %q not permitted by HostPolicy", host)
},
}

// http redirect to https and Let's Encrypt auth
go func() {
redir := httprouter.New()
redir.GET("/*path", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
r.URL.Scheme = "https"
r.URL.Host = httpHost
http.Redirect(w, r, r.URL.String(), http.StatusFound)
})

httpd := &http.Server{
Handler: certmanager.HTTPHandler(redir),
Addr: net.JoinHostPort(httpIP, "80"),
WriteTimeout: httpTimeout,
ReadTimeout: httpTimeout,
MaxHeaderBytes: maxHeaderBytes,
}
if err := httpd.ListenAndServe(); err != nil {
logger.Fatalf("http server on port 80 failed: %s", err)
}
}()

// TLS
tlsConfig := tls.Config{
GetCertificate: certmanager.GetCertificate,
NextProtos: []string{"http/1.1"},
Rand: rand.Reader,
PreferServerCipherSuites: true,
Expand All @@ -281,29 +252,86 @@ func main() {
},
}

// Let's Encrypt TLS mode
if letsencrypt {
// autocert
certmanager := autocert.Manager{
Prompt: autocert.AcceptTOS,
Cache: autocert.DirCache(filepath.Join(datadir, "letsencrypt")),
HostPolicy: func(_ context.Context, host string) error {
host = strings.TrimPrefix(host, "www.")

if host == httpHost {
return nil
}

if host == config.FindInfo().Domain {
return nil
}
return fmt.Errorf("autocert: host %q not permitted by HostPolicy", host)
},
}

tlsConfig.GetCertificate = certmanager.GetCertificate

// http redirect to https and Let's Encrypt auth
go func() {
redir := httprouter.New()
redir.GET("/*path", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
r.URL.Scheme = "https"
r.URL.Host = httpHost
http.Redirect(w, r, r.URL.String(), http.StatusFound)
})

httpd := &http.Server{
Handler: certmanager.HTTPHandler(redir),
Addr: net.JoinHostPort(httpIP, "80"),
WriteTimeout: httpTimeout,
ReadTimeout: httpTimeout,
MaxHeaderBytes: maxHeaderBytes,
}

if err := httpd.ListenAndServe(); err != nil {
logger.Fatalf("http server on port 80 failed: %s", err)
}
}()
}

// Using a certificate/key pair from disk
if tlsCertificate != "" && tlsKey != "" {
var certificates []tls.Certificate
cert, err := tls.LoadX509KeyPair(tlsCertificate, tlsKey)

if err != nil {
logger.Fatal(err.Error())
os.Exit(20)
}

certificates = append(certificates, cert)

tlsConfig.Certificates = certificates
} else if tlsCertificate != "" || tlsKey != "" {
logger.Fatal("You must define both a certificate and a key")
os.Exit(30)
}

// Override default for TLS.
if httpPort == "80" {
httpPort = "443"
httpAddr = net.JoinHostPort(httpIP, httpPort)
}

httpsd := &http.Server{
Handler: r,
Addr: httpAddr,
WriteTimeout: httpTimeout,
ReadTimeout: httpTimeout,
MaxHeaderBytes: maxHeaderBytes,
}

// Enable TCP keep alives on the TLS connection.
tcpListener, err := net.Listen("tcp", httpAddr)

if err != nil {
logger.Fatalf("listen failed: %s", err)
return
}
tlsListener := tls.NewListener(tcpKeepAliveListener{tcpListener.(*net.TCPListener)}, &tlsConfig)

hostport := net.JoinHostPort(httpHost, httpPort)

if httpPort == "443" {
hostport = httpHost
}
Expand All @@ -312,6 +340,15 @@ func main() {
Host: hostport,
Path: "/",
})

httpsd := &http.Server{
Handler: r,
Addr: httpAddr,
WriteTimeout: httpTimeout,
ReadTimeout: httpTimeout,
MaxHeaderBytes: maxHeaderBytes,
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to check out the PR on my own machine but this looks like it's in the wrong place.

logger.Fatal(httpsd.Serve(tlsListener))
}

Expand Down Expand Up @@ -397,6 +434,7 @@ func configureSAML() error {
return nil
}

// BestDomain will attempt to isloate the domain this binary is running from
func BestDomain() string {
domain := config.FindInfo().Domain
if domain != "" {
Expand Down