Skip to content

Commit 9eef23f

Browse files
committed
config+shushtar: add letsencrypt options
1 parent b1c7fc9 commit 9eef23f

File tree

4 files changed

+105
-31
lines changed

4 files changed

+105
-31
lines changed

config.go

Lines changed: 84 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package shushtar
22

33
import (
44
"crypto/tls"
5+
"errors"
56
"fmt"
67
"io/ioutil"
78
"net"
9+
"net/http"
810
"os"
911
"path/filepath"
1012
"strings"
@@ -17,6 +19,7 @@ import (
1719
"github.com/lightningnetwork/lnd/cert"
1820
"github.com/lightningnetwork/lnd/lncfg"
1921
"github.com/mwitkow/go-conntrack/connhelpers"
22+
"golang.org/x/crypto/acme/autocert"
2023
)
2124

2225
const (
@@ -25,17 +28,41 @@ const (
2528
uiPasswordMinLength = 8
2629
)
2730

31+
var (
32+
lndDefaultConfig = lnd.DefaultConfig()
33+
faradayDefaultConfig = faraday.DefaultConfig()
34+
loopDefaultConfig = loopd.DefaultConfig()
35+
36+
defaultLetsEncryptDir = filepath.Join(lnd.DefaultLndDir, "letsencrypt")
37+
)
38+
2839
// Config is the main configuration struct of shushtar. It contains all config
2940
// items of its enveloping subservers, each prefixed with their daemon's short
3041
// name.
3142
type Config struct {
32-
HTTPSListen string `long:"httpslisten" description:"host:port to listen for incoming HTTP/2 connections on"`
33-
UIPassword string `long:"uipassword" description:"the password that must be entered when using the loop UI. use a strong password to protect your node from unauthorized access through the web UI"`
34-
UIPasswordFile string `long:"uipassword_file" description:"same as uipassword but instead of passing in the value directly, read the password from the specified file"`
35-
UIPasswordEnv string `long:"uipassword_env" description:"same as uipassword but instead of passing in the value directly, read the password from the specified environment variable"`
36-
Lnd *lnd.Config `group:"lnd" namespace:"lnd"`
37-
Faraday *faraday.Config `group:"faraday" namespace:"faraday"`
38-
Loop *loopd.Config `group:"loop" namespace:"loop"`
43+
HTTPSListen string `long:"httpslisten" description:"host:port to listen for incoming HTTP/2 connections on"`
44+
UIPassword string `long:"uipassword" description:"the password that must be entered when using the loop UI. use a strong password to protect your node from unauthorized access through the web UI"`
45+
UIPasswordFile string `long:"uipassword_file" description:"same as uipassword but instead of passing in the value directly, read the password from the specified file"`
46+
UIPasswordEnv string `long:"uipassword_env" description:"same as uipassword but instead of passing in the value directly, read the password from the specified environment variable"`
47+
48+
LetsEncrypt bool `long:"letsencrypt" description:"use Let's Encrypt to create a TLS certificate for the UI instead of using lnd's TLS certificate. port 80 must be free to listen on and must be reachable from the internet for this to work"`
49+
LetsEncryptHost string `long:"letsencrypthost" description:"the host name to create a Let's Encrypt certificate for'"`
50+
LetsEncryptDir string `long:"letsencryptdir" description:"the directory where the Let's Encrypt library will store its key and certificate"`
51+
52+
Lnd *lnd.Config `group:"lnd" namespace:"lnd"`
53+
Faraday *faraday.Config `group:"faraday" namespace:"faraday"`
54+
Loop *loopd.Config `group:"loop" namespace:"loop"`
55+
}
56+
57+
// defaultConfig returns a configuration struct with all default values set.
58+
func defaultConfig() *Config {
59+
return &Config{
60+
HTTPSListen: defaultHTTPSListen,
61+
LetsEncryptDir: defaultLetsEncryptDir,
62+
Lnd: &lndDefaultConfig,
63+
Faraday: &faradayDefaultConfig,
64+
Loop: &loopDefaultConfig,
65+
}
3966
}
4067

4168
// loadLndConfig loads and sanitizes the lnd main configuration and hooks up all
@@ -163,18 +190,57 @@ func readUIPassword(config *Config) error {
163190
"variable that contains the password")
164191
}
165192

166-
func buildTLSConfigForHttp2(config *lnd.Config) (*tls.Config, error) {
167-
tlsCert, _, err := cert.LoadCert(config.TLSCertPath, config.TLSKeyPath)
168-
if err != nil {
169-
return nil, fmt.Errorf("failed reading TLS server keys: %v",
170-
err)
193+
func buildTLSConfigForHttp2(config *Config) (*tls.Config, error) {
194+
var tlsConfig *tls.Config
195+
196+
switch {
197+
case config.LetsEncrypt:
198+
serverName := config.LetsEncryptHost
199+
if serverName == "" {
200+
return nil, errors.New("let's encrypt host name " +
201+
"option is required for using let's encrypt")
202+
}
203+
204+
log.Infof("Setting up Let's Encrypt for server %v", serverName)
205+
206+
certDir := config.LetsEncryptDir
207+
log.Infof("Setting up Let's Encrypt with cache dir %v", certDir)
208+
209+
manager := autocert.Manager{
210+
Cache: autocert.DirCache(certDir),
211+
Prompt: autocert.AcceptTOS,
212+
HostPolicy: autocert.HostWhitelist(serverName),
213+
}
214+
215+
go func() {
216+
err := http.ListenAndServe(
217+
":http", manager.HTTPHandler(nil),
218+
)
219+
if err != nil {
220+
log.Errorf("Error starting Let's Encrypt "+
221+
"HTTP listener on port 80: %v", err)
222+
}
223+
}()
224+
tlsConfig = &tls.Config{
225+
GetCertificate: manager.GetCertificate,
226+
}
227+
228+
default:
229+
tlsCert, _, err := cert.LoadCert(
230+
config.Lnd.TLSCertPath, config.Lnd.TLSKeyPath,
231+
)
232+
if err != nil {
233+
return nil, fmt.Errorf("failed reading TLS server keys: %v",
234+
err)
235+
}
236+
tlsConfig = cert.TLSConfFromCert(tlsCert)
237+
tlsConfig.CipherSuites = append(
238+
tlsConfig.CipherSuites,
239+
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
240+
)
171241
}
172-
tlsConfig := cert.TLSConfFromCert(tlsCert)
173-
tlsConfig.CipherSuites = append(
174-
tlsConfig.CipherSuites,
175-
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
176-
)
177-
tlsConfig, err = connhelpers.TlsConfigWithHttp2Enabled(tlsConfig)
242+
243+
tlsConfig, err := connhelpers.TlsConfigWithHttp2Enabled(tlsConfig)
178244
if err != nil {
179245
return nil, fmt.Errorf("can't configure h2 handling: %v", err)
180246
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ require (
1616
github.com/prometheus/client_golang v1.5.1 // indirect
1717
github.com/rakyll/statik v0.1.7
1818
github.com/rs/cors v1.7.0 // indirect
19+
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
1920
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
2021
golang.org/x/sys v0.0.0-20200406155108-e3b113bbe6a4 // indirect
2122
google.golang.org/grpc v1.28.0

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
1919
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
2020
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
2121
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
22+
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
2223
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
2324
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
2425
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -108,8 +109,11 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
108109
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
109110
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
110111
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
112+
github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY=
111113
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
114+
github.com/go-openapi/strfmt v0.19.5 h1:0utjKrw+BAh8s57XE9Xz8DUBsVvPmRUB6styvl9wWIM=
112115
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
116+
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
113117
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
114118
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
115119
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
@@ -154,6 +158,7 @@ github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc
154158
github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
155159
github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad h1:heFfj7z0pGsNCekUlsFhO2jstxO4b5iQ665LjwM5mDc=
156160
github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
161+
github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo=
157162
github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag=
158163
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
159164
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
@@ -202,6 +207,7 @@ github.com/lightninglabs/loop v0.6.2-beta.0.20200528104150-c281cab8a036/go.mod h
202207
github.com/lightninglabs/neutrino v0.11.0/go.mod h1:CuhF0iuzg9Sp2HO6ZgXgayviFTn1QHdSTJlMncK80wg=
203208
github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200 h1:j4iZ1XlUAPQmW6oSzMcJGILYsRHNs+4O3Gk+2Ms5Dww=
204209
github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200/go.mod h1:MlZmoKa7CJP3eR1s5yB7Rm5aSyadpKkxqAwLQmog7N0=
210+
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d h1:QWD/5MPnaZfUVP7P8wLa4M8Td2DI7XXHXt2vhVtUgGI=
205211
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI=
206212
github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea h1:oCj48NQ8u7Vz+MmzHqt0db6mxcFZo3Ho7M5gCJauY/k=
207213
github.com/lightningnetwork/lightning-onion v1.0.2-0.20200501022730-3c8c8d0b89ea/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4=
@@ -218,11 +224,13 @@ github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXj
218224
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
219225
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY=
220226
github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA=
227+
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
221228
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
222229
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
223230
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
224231
github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8 h1:PRMAcldsl4mXKJeRNB/KVNz6TlbS6hk2Rs42PqgU3Ws=
225232
github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
233+
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
226234
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
227235
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
228236
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@@ -297,11 +305,13 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1
297305
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 h1:tcJ6OjwOMvExLlzrAVZute09ocAGa7KqOON60++Gz4E=
298306
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY=
299307
github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
308+
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
300309
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
301310
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
302311
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
303312
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
304313
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
314+
go.mongodb.org/mongo-driver v1.0.3 h1:GKoji1ld3tw2aC+GX1wbr/J2fX13yNacEYoJ8Nhr0yU=
305315
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
306316
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
307317
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=

shushtar.go

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ import (
1717
restProxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
1818
"github.com/improbable-eng/grpc-web/go/grpcweb"
1919
"github.com/jessevdk/go-flags"
20-
"github.com/lightninglabs/faraday"
2120
"github.com/lightninglabs/faraday/frdrpc"
2221
"github.com/lightninglabs/loop/lndclient"
2322
"github.com/lightninglabs/loop/loopd"
2423
"github.com/lightninglabs/loop/looprpc"
2524
"github.com/lightningnetwork/lnd"
25+
"github.com/lightningnetwork/lnd/lncfg"
2626
"github.com/lightningnetwork/lnd/lnrpc"
2727
"github.com/lightningnetwork/lnd/lntest/wait"
2828
"github.com/lightningnetwork/lnd/macaroons"
@@ -45,6 +45,7 @@ import (
4545

4646
const (
4747
defaultServerTimeout = 10 * time.Second
48+
defaultConnectTimeout = 5 * time.Second
4849
defaultStartupTimeout = 5 * time.Second
4950
)
5051

@@ -56,10 +57,6 @@ var (
5657
authError = status.Error(
5758
codes.Unauthenticated, "authentication required",
5859
)
59-
60-
lndDefaultConfig = lnd.DefaultConfig()
61-
faradayDefaultConfig = faraday.DefaultConfig()
62-
loopDefaultConfig = loopd.DefaultConfig()
6360
)
6461

6562
// Shushtar is the main grand unified binary instance. Its task is to start an
@@ -88,12 +85,7 @@ type Shushtar struct {
8885
// New creates a new instance of the shushtar daemon.
8986
func New() *Shushtar {
9087
return &Shushtar{
91-
cfg: &Config{
92-
HTTPSListen: defaultHTTPSListen,
93-
Lnd: &lndDefaultConfig,
94-
Faraday: &faradayDefaultConfig,
95-
Loop: &loopDefaultConfig,
96-
},
88+
cfg: defaultConfig(),
9789
lndErrChan: make(chan error, 1),
9890
}
9991
}
@@ -108,6 +100,11 @@ func (g *Shushtar) Run() error {
108100
return err
109101
}
110102

103+
// Validate the shushtar config options.
104+
g.cfg.LetsEncryptDir = lncfg.CleanAndExpandPath(g.cfg.LetsEncryptDir)
105+
if g.cfg.LetsEncrypt && g.cfg.LetsEncryptHost == "" {
106+
return fmt.Errorf("host must be set when using let's encrypt")
107+
}
111108
err = readUIPassword(g.cfg)
112109
if err != nil {
113110
return fmt.Errorf("could not read UI password: %v", err)
@@ -472,7 +469,7 @@ func (g *Shushtar) startGrpcWebProxy() error {
472469
return fmt.Errorf("unable to listen on %v: %v",
473470
g.cfg.HTTPSListen, err)
474471
}
475-
tlsConfig, err := buildTLSConfigForHttp2(g.cfg.Lnd)
472+
tlsConfig, err := buildTLSConfigForHttp2(g.cfg)
476473
if err != nil {
477474
return fmt.Errorf("unable to create TLS config: %v", err)
478475
}
@@ -590,7 +587,7 @@ func dialLnd(lndAddr string, config *lnd.Config) (*grpc.ClientConn, error) {
590587
grpc.WithDefaultCallOptions(maxMsgRecvSize),
591588
grpc.WithConnectParams(grpc.ConnectParams{
592589
Backoff: backoff.DefaultConfig,
593-
MinConnectTimeout: 5 * time.Second,
590+
MinConnectTimeout: defaultConnectTimeout,
594591
}),
595592
}
596593

0 commit comments

Comments
 (0)