lnrpc: lets encrypt
This commit enables lnd to request and renew a Let's Encrypt certificate. This certificate is used both for the grpc as well as the rest listeners. It allows clients to connect without having a copy of the (public) server certificate. Co-authored-by: Vegard Engen <vegard@engen.priv.no>
This commit is contained in:
parent
999ffffa37
commit
403d72b468
@ -5,6 +5,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -74,13 +75,24 @@ func getClientConn(ctx *cli.Context, skipMacaroons bool) *grpc.ClientConn {
|
|||||||
fatal(fmt.Errorf("could not load global options: %v", err))
|
fatal(fmt.Errorf("could not load global options: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the specified TLS certificate and build transport credentials
|
// Load the specified TLS certificate.
|
||||||
// with it.
|
|
||||||
certPool, err := profile.cert()
|
certPool, err := profile.cert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(fmt.Errorf("could not create cert pool: %v", err))
|
fatal(fmt.Errorf("could not create cert pool: %v", err))
|
||||||
}
|
}
|
||||||
creds := credentials.NewClientTLSFromCert(certPool, "")
|
|
||||||
|
// Build transport credentials from the certificate pool. If there is no
|
||||||
|
// certificate pool, we expect the server to use a non-self-signed
|
||||||
|
// certificate such as a certificate obtained from Let's Encrypt.
|
||||||
|
var creds credentials.TransportCredentials
|
||||||
|
if certPool != nil {
|
||||||
|
creds = credentials.NewClientTLSFromCert(certPool, "")
|
||||||
|
} else {
|
||||||
|
// Fallback to the system pool. Using an empty tls config is an
|
||||||
|
// alternative to x509.SystemCertPool(). That call is not
|
||||||
|
// supported on Windows.
|
||||||
|
creds = credentials.NewTLS(&tls.Config{})
|
||||||
|
}
|
||||||
|
|
||||||
// Create a dial options array.
|
// Create a dial options array.
|
||||||
opts := []grpc.DialOption{
|
opts := []grpc.DialOption{
|
||||||
|
@ -36,6 +36,10 @@ type profileEntry struct {
|
|||||||
|
|
||||||
// cert returns the profile's TLS certificate as a x509 certificate pool.
|
// cert returns the profile's TLS certificate as a x509 certificate pool.
|
||||||
func (e *profileEntry) cert() (*x509.CertPool, error) {
|
func (e *profileEntry) cert() (*x509.CertPool, error) {
|
||||||
|
if e.TLSCert == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
cp := x509.NewCertPool()
|
cp := x509.NewCertPool()
|
||||||
if !cp.AppendCertsFromPEM([]byte(e.TLSCert)) {
|
if !cp.AppendCertsFromPEM([]byte(e.TLSCert)) {
|
||||||
return nil, fmt.Errorf("credentials: failed to append " +
|
return nil, fmt.Errorf("credentials: failed to append " +
|
||||||
@ -113,11 +117,16 @@ func profileFromContext(ctx *cli.Context, store bool) (*profileEntry, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the certificate file now. We store it as plain PEM directly.
|
// Load the certificate file now, if specified. We store it as plain PEM
|
||||||
tlsCert, err := ioutil.ReadFile(tlsCertPath)
|
// directly.
|
||||||
if err != nil {
|
var tlsCert []byte
|
||||||
return nil, fmt.Errorf("could not load TLS cert file %s: %v",
|
if lnrpc.FileExists(tlsCertPath) {
|
||||||
tlsCertPath, err)
|
var err error
|
||||||
|
tlsCert, err = ioutil.ReadFile(tlsCertPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not load TLS cert file "+
|
||||||
|
"%s: %v", tlsCertPath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now load and possibly encrypt the macaroon file.
|
// Now load and possibly encrypt the macaroon file.
|
||||||
|
17
config.go
17
config.go
@ -64,6 +64,8 @@ const (
|
|||||||
defaultMaxLogFileSize = 10
|
defaultMaxLogFileSize = 10
|
||||||
defaultMinBackoff = time.Second
|
defaultMinBackoff = time.Second
|
||||||
defaultMaxBackoff = time.Hour
|
defaultMaxBackoff = time.Hour
|
||||||
|
defaultLetsEncryptDirname = "letsencrypt"
|
||||||
|
defaultLetsEncryptPort = 80
|
||||||
|
|
||||||
defaultTorSOCKSPort = 9050
|
defaultTorSOCKSPort = 9050
|
||||||
defaultTorDNSHost = "soa.nodes.lightning.directory"
|
defaultTorDNSHost = "soa.nodes.lightning.directory"
|
||||||
@ -127,8 +129,9 @@ var (
|
|||||||
|
|
||||||
defaultTowerDir = filepath.Join(defaultDataDir, defaultTowerSubDirname)
|
defaultTowerDir = filepath.Join(defaultDataDir, defaultTowerSubDirname)
|
||||||
|
|
||||||
defaultTLSCertPath = filepath.Join(DefaultLndDir, defaultTLSCertFilename)
|
defaultTLSCertPath = filepath.Join(DefaultLndDir, defaultTLSCertFilename)
|
||||||
defaultTLSKeyPath = filepath.Join(DefaultLndDir, defaultTLSKeyFilename)
|
defaultTLSKeyPath = filepath.Join(DefaultLndDir, defaultTLSKeyFilename)
|
||||||
|
defaultLetsEncryptDir = filepath.Join(DefaultLndDir, defaultLetsEncryptDirname)
|
||||||
|
|
||||||
defaultBtcdDir = btcutil.AppDataDir("btcd", false)
|
defaultBtcdDir = btcutil.AppDataDir("btcd", false)
|
||||||
defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
|
defaultBtcdRPCCertFile = filepath.Join(defaultBtcdDir, "rpc.cert")
|
||||||
@ -179,6 +182,10 @@ type Config struct {
|
|||||||
MaxLogFileSize int `long:"maxlogfilesize" description:"Maximum logfile size in MB"`
|
MaxLogFileSize int `long:"maxlogfilesize" description:"Maximum logfile size in MB"`
|
||||||
AcceptorTimeout time.Duration `long:"acceptortimeout" description:"Time after which an RPCAcceptor will time out and return false if it hasn't yet received a response"`
|
AcceptorTimeout time.Duration `long:"acceptortimeout" description:"Time after which an RPCAcceptor will time out and return false if it hasn't yet received a response"`
|
||||||
|
|
||||||
|
LetsEncryptDir string `long:"letsencryptdir" description:"The directory to store Let's Encrypt certificates within"`
|
||||||
|
LetsEncryptPort int `long:"letsencryptport" description:"The port on which lnd will listen for Let's Encrypt challenges. Let's Encrypt will always try to contact on port 80. Often non-root processes are not allowed to bind to ports lower than 1024. This configuration option allows a different port to be used, but must be used in combination with port forwarding from port 80."`
|
||||||
|
LetsEncryptDomain string `long:"letsencryptdomain" description:"Request a Let's Encrypt certificate for this domain. Note that the certicate is only requested and stored when the first rpc connection comes in."`
|
||||||
|
|
||||||
// We'll parse these 'raw' string arguments into real net.Addrs in the
|
// We'll parse these 'raw' string arguments into real net.Addrs in the
|
||||||
// loadConfig function. We need to expose the 'raw' strings so the
|
// loadConfig function. We need to expose the 'raw' strings so the
|
||||||
// command line library can access them.
|
// command line library can access them.
|
||||||
@ -318,6 +325,8 @@ func DefaultConfig() Config {
|
|||||||
DebugLevel: defaultLogLevel,
|
DebugLevel: defaultLogLevel,
|
||||||
TLSCertPath: defaultTLSCertPath,
|
TLSCertPath: defaultTLSCertPath,
|
||||||
TLSKeyPath: defaultTLSKeyPath,
|
TLSKeyPath: defaultTLSKeyPath,
|
||||||
|
LetsEncryptDir: defaultLetsEncryptDir,
|
||||||
|
LetsEncryptPort: defaultLetsEncryptPort,
|
||||||
LogDir: defaultLogDir,
|
LogDir: defaultLogDir,
|
||||||
MaxLogFiles: defaultMaxLogFiles,
|
MaxLogFiles: defaultMaxLogFiles,
|
||||||
MaxLogFileSize: defaultMaxLogFileSize,
|
MaxLogFileSize: defaultMaxLogFileSize,
|
||||||
@ -520,6 +529,9 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) {
|
|||||||
lndDir := CleanAndExpandPath(cfg.LndDir)
|
lndDir := CleanAndExpandPath(cfg.LndDir)
|
||||||
if lndDir != DefaultLndDir {
|
if lndDir != DefaultLndDir {
|
||||||
cfg.DataDir = filepath.Join(lndDir, defaultDataDirname)
|
cfg.DataDir = filepath.Join(lndDir, defaultDataDirname)
|
||||||
|
cfg.LetsEncryptDir = filepath.Join(
|
||||||
|
lndDir, defaultLetsEncryptDirname,
|
||||||
|
)
|
||||||
cfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
|
cfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
|
||||||
cfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
|
cfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
|
||||||
cfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
|
cfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
|
||||||
@ -558,6 +570,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) {
|
|||||||
cfg.DataDir = CleanAndExpandPath(cfg.DataDir)
|
cfg.DataDir = CleanAndExpandPath(cfg.DataDir)
|
||||||
cfg.TLSCertPath = CleanAndExpandPath(cfg.TLSCertPath)
|
cfg.TLSCertPath = CleanAndExpandPath(cfg.TLSCertPath)
|
||||||
cfg.TLSKeyPath = CleanAndExpandPath(cfg.TLSKeyPath)
|
cfg.TLSKeyPath = CleanAndExpandPath(cfg.TLSKeyPath)
|
||||||
|
cfg.LetsEncryptDir = CleanAndExpandPath(cfg.LetsEncryptDir)
|
||||||
cfg.AdminMacPath = CleanAndExpandPath(cfg.AdminMacPath)
|
cfg.AdminMacPath = CleanAndExpandPath(cfg.AdminMacPath)
|
||||||
cfg.ReadMacPath = CleanAndExpandPath(cfg.ReadMacPath)
|
cfg.ReadMacPath = CleanAndExpandPath(cfg.ReadMacPath)
|
||||||
cfg.InvoiceMacPath = CleanAndExpandPath(cfg.InvoiceMacPath)
|
cfg.InvoiceMacPath = CleanAndExpandPath(cfg.InvoiceMacPath)
|
||||||
|
84
lnd.go
84
lnd.go
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/btcsuite/btcwallet/wallet"
|
"github.com/btcsuite/btcwallet/wallet"
|
||||||
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
|
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||||
"github.com/lightninglabs/neutrino"
|
"github.com/lightninglabs/neutrino"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"gopkg.in/macaroon-bakery.v2/bakery"
|
"gopkg.in/macaroon-bakery.v2/bakery"
|
||||||
@ -264,13 +265,15 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
|||||||
defer cleanUp()
|
defer cleanUp()
|
||||||
|
|
||||||
// Only process macaroons if --no-macaroons isn't set.
|
// Only process macaroons if --no-macaroons isn't set.
|
||||||
tlsCfg, restCreds, restProxyDest, err := getTLSConfig(cfg)
|
tlsCfg, restCreds, restProxyDest, cleanUp, err := getTLSConfig(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("unable to load TLS credentials: %v", err)
|
err := fmt.Errorf("unable to load TLS credentials: %v", err)
|
||||||
ltndLog.Error(err)
|
ltndLog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
serverCreds := credentials.NewTLS(tlsCfg)
|
serverCreds := credentials.NewTLS(tlsCfg)
|
||||||
serverOpts := []grpc.ServerOption{grpc.Creds(serverCreds)}
|
serverOpts := []grpc.ServerOption{grpc.Creds(serverCreds)}
|
||||||
|
|
||||||
@ -748,7 +751,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
|||||||
// getTLSConfig returns a TLS configuration for the gRPC server and credentials
|
// getTLSConfig returns a TLS configuration for the gRPC server and credentials
|
||||||
// and a proxy destination for the REST reverse proxy.
|
// and a proxy destination for the REST reverse proxy.
|
||||||
func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||||
string, error) {
|
string, func(), error) {
|
||||||
|
|
||||||
// Ensure we create TLS key and certificate if they don't exist.
|
// Ensure we create TLS key and certificate if they don't exist.
|
||||||
if !fileExists(cfg.TLSCertPath) && !fileExists(cfg.TLSKeyPath) {
|
if !fileExists(cfg.TLSCertPath) && !fileExists(cfg.TLSKeyPath) {
|
||||||
@ -759,7 +762,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
|||||||
cfg.TLSDisableAutofill, cert.DefaultAutogenValidity,
|
cfg.TLSDisableAutofill, cert.DefaultAutogenValidity,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", nil, err
|
||||||
}
|
}
|
||||||
rpcsLog.Infof("Done generating TLS certificates")
|
rpcsLog.Infof("Done generating TLS certificates")
|
||||||
}
|
}
|
||||||
@ -768,7 +771,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
|||||||
cfg.TLSCertPath, cfg.TLSKeyPath,
|
cfg.TLSCertPath, cfg.TLSKeyPath,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// We check whether the certifcate we have on disk match the IPs and
|
// We check whether the certifcate we have on disk match the IPs and
|
||||||
@ -782,7 +785,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
|||||||
cfg.TLSExtraDomains, cfg.TLSDisableAutofill,
|
cfg.TLSExtraDomains, cfg.TLSDisableAutofill,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,12 +797,12 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
|||||||
|
|
||||||
err := os.Remove(cfg.TLSCertPath)
|
err := os.Remove(cfg.TLSCertPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Remove(cfg.TLSKeyPath)
|
err = os.Remove(cfg.TLSKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rpcsLog.Infof("Renewing TLS certificates...")
|
rpcsLog.Infof("Renewing TLS certificates...")
|
||||||
@ -809,7 +812,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
|||||||
cfg.TLSDisableAutofill, cert.DefaultAutogenValidity,
|
cfg.TLSDisableAutofill, cert.DefaultAutogenValidity,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", nil, err
|
||||||
}
|
}
|
||||||
rpcsLog.Infof("Done renewing TLS certificates")
|
rpcsLog.Infof("Done renewing TLS certificates")
|
||||||
|
|
||||||
@ -818,14 +821,15 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
|||||||
cfg.TLSCertPath, cfg.TLSKeyPath,
|
cfg.TLSCertPath, cfg.TLSKeyPath,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsCfg := cert.TLSConfFromCert(certData)
|
tlsCfg := cert.TLSConfFromCert(certData)
|
||||||
|
|
||||||
restCreds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "")
|
restCreds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, "", err
|
return nil, nil, "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
restProxyDest := cfg.RPCListeners[0].String()
|
restProxyDest := cfg.RPCListeners[0].String()
|
||||||
@ -841,7 +845,65 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tlsCfg, &restCreds, restProxyDest, nil
|
// If Let's Encrypt is enabled, instantiate autocert to request/renew
|
||||||
|
// the certificates.
|
||||||
|
cleanUp := func() {}
|
||||||
|
if cfg.LetsEncryptDomain != "" {
|
||||||
|
ltndLog.Infof("Using Let's Encrypt certificate for domain %v",
|
||||||
|
cfg.LetsEncryptDomain)
|
||||||
|
|
||||||
|
manager := autocert.Manager{
|
||||||
|
Cache: autocert.DirCache(cfg.LetsEncryptDir),
|
||||||
|
Prompt: autocert.AcceptTOS,
|
||||||
|
HostPolicy: autocert.HostWhitelist(cfg.LetsEncryptDomain),
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := fmt.Sprintf(":%v", cfg.LetsEncryptPort)
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: addr,
|
||||||
|
Handler: manager.HTTPHandler(nil),
|
||||||
|
}
|
||||||
|
shutdownCompleted := make(chan struct{})
|
||||||
|
cleanUp = func() {
|
||||||
|
err := srv.Shutdown(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
ltndLog.Errorf("Autocert listener shutdown "+
|
||||||
|
" error: %v", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
<-shutdownCompleted
|
||||||
|
ltndLog.Infof("Autocert challenge listener stopped")
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ltndLog.Infof("Autocert challenge listener started "+
|
||||||
|
"at %v", addr)
|
||||||
|
|
||||||
|
err := srv.ListenAndServe()
|
||||||
|
if err != http.ErrServerClosed {
|
||||||
|
ltndLog.Errorf("autocert http: %v", err)
|
||||||
|
}
|
||||||
|
close(shutdownCompleted)
|
||||||
|
}()
|
||||||
|
|
||||||
|
getCertificate := func(h *tls.ClientHelloInfo) (
|
||||||
|
*tls.Certificate, error) {
|
||||||
|
|
||||||
|
lecert, err := manager.GetCertificate(h)
|
||||||
|
if err != nil {
|
||||||
|
ltndLog.Errorf("GetCertificate: %v", err)
|
||||||
|
return &certData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return lecert, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The self-signed tls.cert remains available as fallback.
|
||||||
|
tlsCfg.GetCertificate = getCertificate
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlsCfg, &restCreds, restProxyDest, cleanUp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fileExists reports whether the named file or directory exists.
|
// fileExists reports whether the named file or directory exists.
|
||||||
|
@ -50,6 +50,20 @@
|
|||||||
; or want to expose the node at a domain.
|
; or want to expose the node at a domain.
|
||||||
; externalhosts=my-node-domain.com
|
; externalhosts=my-node-domain.com
|
||||||
|
|
||||||
|
; Sets the directory to store Let's Encrypt certificates within
|
||||||
|
; letsencryptdir=~/.lnd/letsencrypt
|
||||||
|
|
||||||
|
; Sets the port on which lnd will listen for Let's Encrypt challenges. Let's
|
||||||
|
; Encrypt will always try to contact on port 80. Often non-root processes are
|
||||||
|
; not allowed to bind to ports lower than 1024. This configuration option allows
|
||||||
|
; a different port to be used, but must be used in combination with port
|
||||||
|
; forwarding from port 80.
|
||||||
|
; letsencryptport=8080
|
||||||
|
|
||||||
|
; Request a Let's Encrypt certificate for this domain. Note that the certicate
|
||||||
|
; is only requested and stored when the first rpc connection comes in.
|
||||||
|
; letsencryptdomain=example.com
|
||||||
|
|
||||||
; Disable macaroon authentication. Macaroons are used are bearer credentials to
|
; Disable macaroon authentication. Macaroons are used are bearer credentials to
|
||||||
; authenticate all RPC access. If one wishes to opt out of macaroons, uncomment
|
; authenticate all RPC access. If one wishes to opt out of macaroons, uncomment
|
||||||
; the line below.
|
; the line below.
|
||||||
|
@ -119,10 +119,11 @@ func TestTLSAutoRegeneration(t *testing.T) {
|
|||||||
TLSKeyPath: keyPath,
|
TLSKeyPath: keyPath,
|
||||||
RPCListeners: rpcListeners,
|
RPCListeners: rpcListeners,
|
||||||
}
|
}
|
||||||
_, _, _, err = getTLSConfig(cfg)
|
_, _, _, cleanUp, err := getTLSConfig(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("couldn't retrieve TLS config")
|
t.Fatalf("couldn't retrieve TLS config")
|
||||||
}
|
}
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
// Grab the certificate to test that getTLSConfig did its job correctly
|
// Grab the certificate to test that getTLSConfig did its job correctly
|
||||||
// and generated a new cert.
|
// and generated a new cert.
|
||||||
|
Loading…
Reference in New Issue
Block a user