diff options
-rw-r--r-- | cmd/ponzu/main.go | 22 | ||||
-rw-r--r-- | system/tls/devcerts.go | 143 | ||||
-rw-r--r-- | system/tls/enable.go | 3 | ||||
-rw-r--r-- | system/tls/enabledev.go | 23 |
4 files changed, 183 insertions, 8 deletions
diff --git a/cmd/ponzu/main.go b/cmd/ponzu/main.go index 9900d40..3764984 100644 --- a/cmd/ponzu/main.go +++ b/cmd/ponzu/main.go @@ -20,9 +20,10 @@ import ( ) var ( - usage = usageHeader + usageNew + usageGenerate + usageBuild + usageRun - port int - https bool + usage = usageHeader + usageNew + usageGenerate + usageBuild + usageRun + port int + https bool + devhttps bool // for ponzu internal / core development dev bool @@ -30,15 +31,14 @@ var ( gocmd string ) -func init() { +func main() { flag.Usage = func() { fmt.Println(usage) } -} -func main() { flag.IntVar(&port, "port", 8080, "port for ponzu to bind its listener") flag.BoolVar(&https, "https", false, "enable automatic TLS/SSL certificate management") + flag.BoolVar(&devhttps, "devhttps", false, "[dev environment] enable automatic TLS/SSL certificate management") flag.BoolVar(&dev, "dev", false, "modify environment for Ponzu core development") flag.StringVar(&fork, "fork", "", "modify repo source for Ponzu core development") flag.StringVar(&gocmd, "gocmd", "go", "custom go command if using beta or new release of Go") @@ -116,6 +116,10 @@ func main() { addTLS = "--https=false" } + if devhttps { + addTLS = "--devhttps" + } + var services string if len(args) > 1 { services = args[1] @@ -167,7 +171,11 @@ func main() { } } - if https { + // cannot run production HTTPS and development HTTPS together + if devhttps { + fmt.Println("Enabling self-signed HTTPS...") + tls.EnableDev() + } else if https { fmt.Println("Enabling HTTPS...") tls.Enable() } diff --git a/system/tls/devcerts.go b/system/tls/devcerts.go new file mode 100644 index 0000000..7d07a4a --- /dev/null +++ b/system/tls/devcerts.go @@ -0,0 +1,143 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Modified 2016 by Steve Manuel, Boss Sauce Creative, LLC +// All modifications are relicensed under the same BSD license +// found in the LICENSE file. + +// Generate a self-signed X.509 certificate for a TLS server. Outputs to +// 'devcerts/cert.pem' and 'devcerts/key.pem' and will overwrite existing files. + +package tls + +import ( + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "log" + "math/big" + "net" + "os" + "path/filepath" + "strings" + "time" + + "github.com/ponzu-cms/ponzu/system/db" +) + +func publicKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } +} + +func pemBlockForKey(priv interface{}) *pem.Block { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} + case *ecdsa.PrivateKey: + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) + os.Exit(2) + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} + default: + return nil + } +} + +func setupDev() { + var priv interface{} + var err error + + priv, err = rsa.GenerateKey(rand.Reader, 2048) + + if err != nil { + log.Fatalf("failed to generate private key: %s", err) + } + + notBefore := time.Now() + notAfter := notBefore.Add(time.Hour * 24 * 30) // valid for 30 days + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + log.Fatalf("failed to generate serial number: %s", err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + host := db.ConfigCache("domain") + if host == "" { + host = "localhost, 0.0.0.0" + } + hosts := strings.Split(host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + // make all certs CA + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) + if err != nil { + log.Fatalln("Failed to create certificate:", err) + } + + // overwrite/create directory for devcerts + pwd, err := os.Getwd() + if err != nil { + log.Fatalln("Couldn't find working directory to locate or save dev certificates.") + } + + devcertsPath := filepath.Join(pwd, "system", "tls", "devcerts") + + err = os.Mkdir(devcertsPath, os.ModePerm|os.ModePerm) + if err != nil { + log.Fatalln("Failed to create directory to locate or save dev certificates.") + } + + certOut, err := os.Create(filepath.Join(devcertsPath, "cert.pem")) + if err != nil { + log.Fatalf("failed to open devcerts/cert.pem for writing: %s", err) + } + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + certOut.Close() + log.Print("written devcerts/cert.pem\n") + + keyOut, err := os.OpenFile(filepath.Join(devcertsPath, "key.pem"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + log.Print("failed to open devcerts/key.pem for writing:", err) + return + } + pem.Encode(keyOut, pemBlockForKey(priv)) + keyOut.Close() + log.Print("written devcerts/key.pem\n") +} diff --git a/system/tls/enable.go b/system/tls/enable.go index 04f032a..5e16b92 100644 --- a/system/tls/enable.go +++ b/system/tls/enable.go @@ -65,7 +65,8 @@ func setup() { } -// Enable runs the setup for creating or locating certificates and starts the TLS server +// Enable runs the setup for creating or locating production certificates and +// starts the TLS server func Enable() { setup() diff --git a/system/tls/enabledev.go b/system/tls/enabledev.go new file mode 100644 index 0000000..18da161 --- /dev/null +++ b/system/tls/enabledev.go @@ -0,0 +1,23 @@ +package tls + +import ( + "log" + "net/http" + "path/filepath" +) + +// EnableDev generates self-signed SSL certificates to use HTTPS & HTTP/2 while +// working in a development environment. The certs are saved in a different +// directory than the production certs (from Let's Encrypt), so that the +// acme/autocert package doesn't mistake them for it's own. +// Additionally, a TLS server is started using the default http mux. +func EnableDev() { + setupDev() + + cert := filepath.Join("devcerts", "cert.pem") + key := filepath.Join("devcerts", "key.pem") + err := http.ListenAndServeTLS(":10443", cert, key, nil) + if err != nil { + log.Fatalln(err) + } +} |