Files
simplecert/utils.go
2019-09-29 14:17:08 +02:00

173 lines
4.6 KiB
Go

//
// simplecert
//
// Created by Philipp Mieden
// Contact: dreadl0ck@protonmail.ch
// Copyright © 2018 bestbytes. All rights reserved.
//
package simplecert
import (
"crypto/tls"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"strings"
"github.com/foomo/tlsconfig"
)
// internal date of the backup to allow restoring in case of an error
// even if renewal happens just before midnight and restoring afterwards
var backupDate string
const localhost = "127.0.0.1"
/*
* Utils
*/
// ListenAndServeTLSCustom allows to specify the simplecert and TLS configuration
// and does not redirect the traffic arriving at port 80
func ListenAndServeTLSCustom(addr string, handler http.Handler, cfg *Config, tlsconf *tls.Config, cleanup func(), domains ...string) error {
certReloader, err := Init(cfg, cleanup)
if err != nil {
log.Fatal("[FATAL] simplecert init failed: ", err)
}
// now set GetCertificate to the reloaders GetCertificateFunc to enable hot reload
tlsconf.GetCertificate = certReloader.GetCertificateFunc()
// init server
s := &http.Server{
Addr: addr,
TLSConfig: tlsconf,
Handler: handler,
}
log.Println("serving: https://" + cfg.Domains[0])
// lets go
return s.ListenAndServeTLS("", "")
}
// ListenAndServeTLSLocal is a util to use simplecert for local development
func ListenAndServeTLSLocal(addr string, handler http.Handler, cleanup func(), domains ...string) error {
cfg := Default
cfg.Domains = domains
cfg.CacheDir = "simplecert"
cfg.Local = true
certReloader, err := Init(cfg, cleanup)
if err != nil {
log.Fatal("[FATAL] simplecert init failed: ", err)
}
// redirect HTTP to HTTPS
log.Println("starting HTTP Listener on Port 80")
go http.ListenAndServe(":80", http.HandlerFunc(Redirect))
// init strict tlsConfig with certReloader
tlsconf := tlsconfig.NewServerTLSConfig(tlsconfig.TLSModeServerStrict)
// now set GetCertificate to the reloaders GetCertificateFunc to enable hot reload
tlsconf.GetCertificate = certReloader.GetCertificateFunc()
// init server
s := &http.Server{
Addr: addr,
TLSConfig: tlsconf,
Handler: handler,
}
log.Println("serving: https://" + cfg.Domains[0])
// lets go
return s.ListenAndServeTLS("", "")
}
// ListenAndServeTLS is a util to use simplecert in production
func ListenAndServeTLS(addr string, handler http.Handler, mail string, cleanup func(), domains ...string) error {
cfg := Default
cfg.Domains = domains
cfg.CacheDir = "simplecert"
cfg.SSLEmail = mail
certReloader, err := Init(cfg, cleanup)
if err != nil {
log.Fatal("[FATAL] simplecert init failed: ", err)
}
// redirect HTTP to HTTPS
log.Println("starting HTTP Listener on Port 80")
go http.ListenAndServe(":80", http.HandlerFunc(Redirect))
// init strict tlsConfig with certReloader
tlsconf := tlsconfig.NewServerTLSConfig(tlsconfig.TLSModeServerStrict)
// now set GetCertificate to the reloaders GetCertificateFunc to enable hot reload
tlsconf.GetCertificate = certReloader.GetCertificateFunc()
// init server
s := &http.Server{
Addr: addr,
TLSConfig: tlsconf,
Handler: handler,
}
log.Println("serving: https://" + cfg.Domains[0])
// lets go
return s.ListenAndServeTLS("", "")
}
// Redirect a request to HTTPS and strips the www. subdomain
func Redirect(w http.ResponseWriter, req *http.Request) {
target := "https://" + strings.TrimPrefix(req.Host, "www.") + req.URL.Path
if len(req.URL.RawQuery) > 0 {
target += "?" + req.URL.RawQuery
}
fmt.Println("redirecting client to https: ", target, " ("+req.Host+")", "UserAgent:", req.UserAgent())
http.Redirect(w, req, target, http.StatusTemporaryRedirect)
}
////////////////////
// Private
///////////////////
// ensures the cacheDir exists, fatals on error
func ensureCacheDirExists(cacheDir string) {
log.Println("[INFO] simplecert: checking if cacheDir " + cacheDir + " exists...")
// create cacheDir if necessary
info, err := os.Stat(cacheDir)
if err != nil {
log.Println("[INFO] simplecert: cacheDir does not exist - creating it")
err = os.MkdirAll(c.CacheDir, c.CacheDirPerm)
if err != nil {
log.Fatal("[FATAL] simplecert: could not create cacheDir: ", err)
}
} else {
// exists. make sure its a directory
if !info.IsDir() {
log.Fatal("[FATAL] simplecert: cacheDir: expected a directory but got a file?!")
}
}
}
// runCommand executes the named command with the supplied arguments
// and fatals on error
func runCommand(cmd string, args ...string) {
out, err := exec.Command(cmd, args...).CombinedOutput()
if err != nil {
log.Println("[ERROR] failed to run command: ", cmd+strings.Join(args, " "))
log.Fatal("[FATAL] simplecert: error: ", err, ", output: ", string(out))
}
}