add tls config to server (#1202)
* add tls config * add TLSConfig to acme provider
This commit is contained in:
parent
158949d0d0
commit
964b7dee3f
@ -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
|
||||||
|
@ -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{}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
37
api/server/acme/autocert/cache.go
Normal file
37
api/server/acme/autocert/cache.go
Normal 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)
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -805,7 +805,14 @@ func (g *grpcServer) Start() error {
|
|||||||
ts = l
|
ts = l
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// 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)
|
ts, err = net.Listen("tcp", config.Address)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user