add tls config to server (#1202)

* add tls config

* add TLSConfig to acme provider
This commit is contained in:
Asim Aslam 2020-02-15 15:10:26 +00:00 committed by GitHub
parent 158949d0d0
commit 964b7dee3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 114 additions and 18 deletions

View File

@ -2,6 +2,7 @@
package acme package acme
import ( import (
"crypto/tls"
"errors" "errors"
"net" "net"
) )
@ -14,7 +15,10 @@ var (
// Provider is a ACME provider interface // Provider is a ACME provider interface
type Provider interface { type Provider interface {
NewListener(...string) (net.Listener, error) // Listen returns a new listener
Listen(...string) (net.Listener, error)
// TLSConfig returns a tls config
TLSConfig(...string) (*tls.Config, error)
} }
// The Let's Encrypt ACME endpoints // The Let's Encrypt ACME endpoints

View File

@ -3,7 +3,10 @@
package autocert package autocert
import ( import (
"crypto/tls"
"log"
"net" "net"
"os"
"github.com/micro/go-micro/v2/api/server/acme" "github.com/micro/go-micro/v2/api/server/acme"
"golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/acme/autocert"
@ -12,12 +15,30 @@ import (
// autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert // autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert
type autocertProvider struct{} type autocertProvider struct{}
// NewListener implements acme.Provider // Listen implements acme.Provider
func (a *autocertProvider) NewListener(ACMEHosts ...string) (net.Listener, error) { func (a *autocertProvider) Listen(hosts ...string) (net.Listener, error) {
return autocert.NewListener(ACMEHosts...), nil return autocert.NewListener(hosts...), nil
}
// TLSConfig returns a new tls config
func (a *autocertProvider) TLSConfig(hosts ...string) (*tls.Config, error) {
// create a new manager
m := &autocert.Manager{
Prompt: autocert.AcceptTOS,
}
if len(hosts) > 0 {
m.HostPolicy = autocert.HostWhitelist(hosts...)
}
dir := cacheDir()
if err := os.MkdirAll(dir, 0700); err != nil {
log.Printf("warning: autocert not using a cache: %v", err)
} else {
m.Cache = autocert.DirCache(dir)
}
return m.TLSConfig(), nil
} }
// New returns an autocert acme.Provider // New returns an autocert acme.Provider
func New() acme.Provider { func NewProvider() acme.Provider {
return &autocertProvider{} return &autocertProvider{}
} }

View File

@ -5,9 +5,9 @@ import (
) )
func TestAutocert(t *testing.T) { func TestAutocert(t *testing.T) {
l := New() l := NewProvider()
if _, ok := l.(*autocertProvider); !ok { if _, ok := l.(*autocertProvider); !ok {
t.Error("New() didn't return an autocertProvider") t.Error("NewProvider() didn't return an autocertProvider")
} }
// TODO: Travis CI doesn't let us bind :443 // TODO: Travis CI doesn't let us bind :443
// if _, err := l.NewListener(); err != nil { // if _, err := l.NewListener(); err != nil {

View File

@ -0,0 +1,37 @@
package autocert
import (
"os"
"path/filepath"
"runtime"
)
func homeDir() string {
if runtime.GOOS == "windows" {
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
}
if h := os.Getenv("HOME"); h != "" {
return h
}
return "/"
}
func cacheDir() string {
const base = "golang-autocert"
switch runtime.GOOS {
case "darwin":
return filepath.Join(homeDir(), "Library", "Caches", base)
case "windows":
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
if v := os.Getenv(ev); v != "" {
return filepath.Join(v, base)
}
}
// Worst case:
return filepath.Join(homeDir(), base)
}
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
return filepath.Join(xdg, base)
}
return filepath.Join(homeDir(), ".cache", base)
}

View File

@ -2,6 +2,7 @@
package certmagic package certmagic
import ( import (
"crypto/tls"
"log" "log"
"math/rand" "math/rand"
"net" "net"
@ -16,7 +17,8 @@ type certmagicProvider struct {
opts acme.Options opts acme.Options
} }
func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, error) { // TODO: set self-contained options
func (c *certmagicProvider) setup() {
certmagic.Default.CA = c.opts.CA certmagic.Default.CA = c.opts.CA
if c.opts.ChallengeProvider != nil { if c.opts.ChallengeProvider != nil {
// Enabling DNS Challenge disables the other challenges // Enabling DNS Challenge disables the other challenges
@ -34,12 +36,20 @@ func (c *certmagicProvider) NewListener(ACMEHosts ...string) (net.Listener, erro
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
randomDuration := (7 * 24 * time.Hour) + (time.Duration(rand.Intn(504)) * time.Hour) randomDuration := (7 * 24 * time.Hour) + (time.Duration(rand.Intn(504)) * time.Hour)
certmagic.Default.RenewDurationBefore = randomDuration certmagic.Default.RenewDurationBefore = randomDuration
}
return certmagic.Listen(ACMEHosts) func (c *certmagicProvider) Listen(hosts ...string) (net.Listener, error) {
c.setup()
return certmagic.Listen(hosts)
}
func (c *certmagicProvider) TLSConfig(hosts ...string) (*tls.Config, error) {
c.setup()
return certmagic.TLS(hosts)
} }
// New returns a certmagic provider // New returns a certmagic provider
func New(options ...acme.Option) acme.Provider { func NewProvider(options ...acme.Option) acme.Provider {
opts := acme.DefaultOptions() opts := acme.DefaultOptions()
for _, o := range options { for _, o := range options {

View File

@ -19,7 +19,7 @@ func TestCertMagic(t *testing.T) {
if len(os.Getenv("IN_TRAVIS_CI")) != 0 { if len(os.Getenv("IN_TRAVIS_CI")) != 0 {
t.Skip("Travis doesn't let us bind :443") t.Skip("Travis doesn't let us bind :443")
} }
l, err := New().NewListener() l, err := NewProvider().Listen()
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
@ -36,10 +36,10 @@ func TestCertMagic(t *testing.T) {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
l, err = New(acme.AcceptToS(true), l, err = NewProvider(acme.AcceptToS(true),
acme.CA(acme.LetsEncryptStagingCA), acme.CA(acme.LetsEncryptStagingCA),
acme.ChallengeProvider(p), acme.ChallengeProvider(p),
).NewListener() ).Listen()
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
@ -180,7 +180,7 @@ func TestStorageImplementation(t *testing.T) {
// New interface doesn't return an error, so call it in case any log.Fatal // New interface doesn't return an error, so call it in case any log.Fatal
// happens // happens
New(acme.Cache(s)) NewProvider(acme.Cache(s))
} }
// Full test with a real zone, with against LE staging // Full test with a real zone, with against LE staging
@ -207,7 +207,7 @@ func TestE2e(t *testing.T) {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
testProvider := New( testProvider := NewProvider(
acme.AcceptToS(true), acme.AcceptToS(true),
acme.Cache(testStorage), acme.Cache(testStorage),
acme.CA(acme.LetsEncryptStagingCA), acme.CA(acme.LetsEncryptStagingCA),
@ -215,7 +215,7 @@ func TestE2e(t *testing.T) {
acme.OnDemand(false), acme.OnDemand(false),
) )
listener, err := testProvider.NewListener("*.micro.mu", "micro.mu") listener, err := testProvider.Listen("*.micro.mu", "micro.mu")
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }

View File

@ -54,7 +54,7 @@ func (s *httpServer) Start() error {
if s.opts.EnableACME && s.opts.ACMEProvider != nil { if s.opts.EnableACME && s.opts.ACMEProvider != nil {
// should we check the address to make sure its using :443? // should we check the address to make sure its using :443?
l, err = s.opts.ACMEProvider.NewListener(s.opts.ACMEHosts...) l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...)
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil { } else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig) l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig)
} else { } else {

View File

@ -805,7 +805,14 @@ func (g *grpcServer) Start() error {
ts = l ts = l
} else { } else {
var err error var err error
ts, err = net.Listen("tcp", config.Address)
// check the tls config for secure connect
if tc := config.TLSConfig; tc != nil {
ts, err = tls.Listen("tcp", config.Address, tc)
// otherwise just plain tcp listener
} else {
ts, err = net.Listen("tcp", config.Address)
}
if err != nil { if err != nil {
return err return err
} }

View File

@ -2,6 +2,7 @@ package server
import ( import (
"context" "context"
"crypto/tls"
"sync" "sync"
"time" "time"
@ -39,6 +40,9 @@ type Options struct {
// The router for requests // The router for requests
Router Router Router Router
// TLSConfig specifies tls.Config for secure serving
TLSConfig *tls.Config
// Other options for implementations of the interface // Other options for implementations of the interface
// can be stored in a context // can be stored in a context
Context context.Context Context context.Context
@ -205,6 +209,19 @@ func RegisterInterval(t time.Duration) Option {
} }
} }
// TLSConfig specifies a *tls.Config
func TLSConfig(t *tls.Config) Option {
return func(o *Options) {
// set the internal tls
o.TLSConfig = t
// set the transport tls
o.Transport.Init(
transport.Secure(true),
transport.TLSConfig(t),
)
}
}
// WithRouter sets the request router // WithRouter sets the request router
func WithRouter(r Router) Option { func WithRouter(r Router) Option {
return func(o *Options) { return func(o *Options) {