Skip to content

Commit

Permalink
Merge pull request #41 from acoshift/auto-gen-tls
Browse files Browse the repository at this point in the history
auto generate self sign cert
  • Loading branch information
acoshift committed Aug 2, 2018
2 parents e75e7bd + 246f478 commit 1a49333
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 43 deletions.
35 changes: 12 additions & 23 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ type App struct {
templateFuncs []template.FuncMap

gracefulShutdown *gracefulShutdown

certFile, keyFile string
}

var (
Expand Down Expand Up @@ -91,8 +89,6 @@ func (app *App) Clone() *App {
template: cloneTmpl(app.template),
templateFuncs: cloneFuncMaps(app.templateFuncs),
gracefulShutdown: &*app.gracefulShutdown,
certFile: app.certFile,
keyFile: app.keyFile,
}
if app.TLSConfig != nil {
x.TLSConfig = app.TLSConfig.Clone()
Expand Down Expand Up @@ -155,21 +151,15 @@ func (app *App) configServer() {
func (app *App) listenAndServe() error {
app.configServer()

return app.srv.ListenAndServe()
}

func (app *App) listenAndServeTLS(certFile, keyFile string) error {
app.configServer()
if app.srv.TLSConfig != nil {
return app.srv.ListenAndServeTLS("", "")
}

return app.srv.ListenAndServeTLS(certFile, keyFile)
return app.srv.ListenAndServe()
}

// ListenAndServe starts web server
func (app *App) ListenAndServe() error {
if app.certFile != "" && app.keyFile != "" {
return app.ListenAndServeTLS(app.certFile, app.keyFile)
}

if app.gracefulShutdown != nil {
return app.GracefulShutdown().ListenAndServe()
}
Expand All @@ -179,17 +169,16 @@ func (app *App) ListenAndServe() error {

// TLS sets cert and key file
func (app *App) TLS(certFile, keyFile string) *App {
app.certFile, app.keyFile = certFile, keyFile
return app
}

// ListenAndServeTLS starts web server in tls mode
func (app *App) ListenAndServeTLS(certFile, keyFile string) error {
if app.gracefulShutdown != nil {
return app.GracefulShutdown().ListenAndServeTLS(certFile, keyFile)
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
panic("hime: load key pair error; " + err.Error())
}

return app.listenAndServeTLS(certFile, keyFile)
if app.TLSConfig == nil {
app.TLSConfig = &tls.Config{}
}
app.TLSConfig.Certificates = append(app.TLSConfig.Certificates, cert)
return app
}

// GracefulShutdown returns graceful shutdown server
Expand Down
116 changes: 109 additions & 7 deletions config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package hime

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"io/ioutil"
"log"
"math/big"
"net"
"strings"
"time"

Expand All @@ -26,6 +35,14 @@ type AppConfig struct {
Wait string `yaml:"wait" json:"wait"`
} `yaml:"gracefulShutdown" json:"gracefulShutdown"`
TLS *struct {
SelfSign *struct {
Key struct {
Algo string `yaml:"algo" json:"algo"`
Size int `yaml:"size" json:"size"`
} `yaml:"key" json:"key"`
CN string `yaml:"cn" json:"cn"`
Hosts []string `yaml:"host" json:"host"`
} `yaml:"selfSign" json:"selfSign"`
CertFile string `yaml:"certFile" json:"certFile"`
KeyFile string `yaml:"keyFile" json:"keyFile"`
Profile string `yaml:"profile" json:"profile"`
Expand Down Expand Up @@ -154,15 +171,100 @@ func (app *App) Config(config AppConfig) *App {
}
}

// TODO: auto generate self-signed tls if cert file, key file empty
if t.CertFile != "" {
app.certFile = t.CertFile
}
if t.KeyFile != "" {
app.keyFile = t.KeyFile
if t.CertFile != "" && t.KeyFile != "" {
cert, err := tls.LoadX509KeyPair(t.CertFile, t.KeyFile)
if err != nil {
panic("hime: load key pair error; " + err.Error())
}
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
} else if c := t.SelfSign; c != nil {
var priv interface{}
var pub interface{}

switch c.Key.Algo {
case "ecdsa", "":
var curve elliptic.Curve
switch c.Key.Size {
case 224:
curve = elliptic.P224()
case 256, 0:
curve = elliptic.P256()
case 384:
curve = elliptic.P384()
case 521:
curve = elliptic.P521()
default:
panic("hime: invalid self-sign key size")
}

pri, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
panic("hime: generate private key error;" + err.Error())
}
priv, pub = pri, &pri.PublicKey
case "rsa":
// TODO: make rsa key size configurable
pri, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic("hime: generate private key error; " + err.Error())
}
priv, pub = pri, &pri.PublicKey
default:
panic("hime: invalid self-sign key algo")
}

sn, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
panic("hime: generate serial number error; " + err.Error())
}

cert := x509.Certificate{
SerialNumber: sn,
Subject: pkix.Name{
Organization: []string{"Acme Co"},
},
NotAfter: time.Now().AddDate(1, 0, 0), // TODO: make not before configurable
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}

for _, h := range c.Hosts {
if ip := net.ParseIP(h); ip != nil {
cert.IPAddresses = append(cert.IPAddresses, ip)
} else {
cert.DNSNames = append(cert.DNSNames, h)
}
}

certBytes, err := x509.CreateCertificate(rand.Reader, &cert, &cert, pub, priv)
if err != nil {
panic("hime: create cvertificate error; " + err.Error())
}

certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})

var keyPemBlock pem.Block
switch k := priv.(type) {
case *ecdsa.PrivateKey:
b, err := x509.MarshalECPrivateKey(k)
if err != nil {
panic("hime: marshal ec private key error; " + err.Error())
}
keyPemBlock = pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
case *rsa.PrivateKey:
keyPemBlock = pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
}
keyPem := pem.EncodeToMemory(&keyPemBlock)

tlsCert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
panic("hime: load key pair error; " + err.Error())
}
tlsConfig.Certificates = append(tlsConfig.Certificates, tlsCert)
}

app.srv.TLSConfig = tlsConfig
app.TLSConfig = tlsConfig
}

if gs := server.GracefulShutdown; gs != nil {
Expand Down
3 changes: 1 addition & 2 deletions config_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ func TestConfig(t *testing.T) {
assert.Equal(t, 5*time.Second, app.ReadHeaderTimeout)
assert.Equal(t, 6*time.Second, app.WriteTimeout)
assert.Equal(t, 30*time.Second, app.IdleTimeout)
assert.Equal(t, "tls.crt", app.certFile)
assert.Equal(t, "tls.key", app.keyFile)
assert.Len(t, app.TLSConfig.Certificates, 1)

// graceful
assert.NotNil(t, app.gracefulShutdown)
Expand Down
24 changes: 24 additions & 0 deletions examples/selfsign/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"log"
"net/http"

"github.com/acoshift/hime"
)

var config = []byte(`
server:
addr: :8080
tls:
selfSign: {}
`)

func main() {
app := hime.New()
app.ParseConfig(config)
app.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}))
log.Fatal(app.ListenAndServe())
}
9 changes: 0 additions & 9 deletions graceful.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,5 @@ func (gs *GracefulShutdownApp) start(listenAndServe func() error) (err error) {

// ListenAndServe starts web server in graceful shutdown mode
func (gs *GracefulShutdownApp) ListenAndServe() error {
if gs.App.certFile != "" && gs.App.keyFile != "" {
return gs.ListenAndServeTLS(gs.App.certFile, gs.App.keyFile)
}

return gs.start(gs.App.listenAndServe)
}

// ListenAndServeTLS starts web server in graceful shutdown and tls mode
func (gs *GracefulShutdownApp) ListenAndServeTLS(certFile, keyFile string) error {
return gs.start(func() error { return gs.App.listenAndServeTLS(certFile, keyFile) })
}
3 changes: 1 addition & 2 deletions testdata/config1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@ server:
timeout: 1m
wait: 5s
tls:
certFile: tls.crt
keyFile: tls.key
selfSign: {}
profile: modern

0 comments on commit 1a49333

Please sign in to comment.