173 lines
4.6 KiB
Go
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))
|
|
}
|
|
}
|