Repository navigation
Expand file tree
/
Copy pathconfig.go
More file actions
397 lines (314 loc) · 16.4 KB
/
Copy pathconfig.go
File metadata and controls
397 lines (314 loc) · 16.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
package aperture
import (
"errors"
"fmt"
"path/filepath"
"time"
"github.com/btcsuite/btcd/btcutil/v2"
"github.com/lightninglabs/aperture/aperturedb"
"github.com/lightninglabs/aperture/proxy"
"github.com/lightningnetwork/lnd/build"
)
var (
apertureDataDir = btcutil.AppDataDir("aperture", false)
defaultConfigFilename = "aperture.yaml"
defaultTLSKeyFilename = "tls.key"
defaultTLSCertFilename = "tls.cert"
defaultLogLevel = "info"
defaultLogFilename = "aperture.log"
defaultInvoiceBatchSize = 100000
defaultStrictVerify = false
defaultSqliteDatabaseFileName = "aperture.db"
// defaultSqliteDatabasePath is the default path under which we store
// the SQLite database file.
defaultSqliteDatabasePath = filepath.Join(
apertureDataDir, defaultSqliteDatabaseFileName,
)
)
const (
defaultIdleTimeout = time.Minute * 2
defaultReadTimeout = time.Second * 15
defaultWriteTimeout = time.Second * 30
// defaultWsPingInterval is the default interval at which we send
// WebSocket-level pings to connected clients. This keeps the
// connection alive through intermediary proxies and load balancers
// that may drop idle connections.
defaultWsPingInterval = 30 * time.Second
// defaultWsPongWait is the default duration we wait for a pong
// response after sending a WebSocket ping before considering the
// connection dead.
defaultWsPongWait = 15 * time.Second
)
type EtcdConfig struct {
Host string `long:"host" description:"host:port of an active etcd instance"`
User string `long:"user" description:"user authorized to access the etcd host"`
Password string `long:"password" description:"password of the etcd user"`
}
type AuthConfig struct {
Network string `long:"network" description:"The network LND is connected to." choice:"regtest" choice:"simnet" choice:"testnet" choice:"mainnet" choice:"signet"`
Disable bool `long:"disable" description:"Whether to disable auth."`
// LndHost is the hostname of the LND instance to connect to.
LndHost string `long:"lndhost" description:"Hostname of the LND instance to connect to"`
// WavelengthGateway is the base URL of a Wavelength wallet daemon's
// HTTP/JSON gateway to mint challenge invoices from, instead of an lnd
// node. Invoices from that wallet are backed by an inbound swap, so a
// seller configured this way needs no Lightning node, no channels and
// no inbound liquidity in order to be paid.
WavelengthGateway string `long:"wavelengthgateway" description:"Base URL of a Wavelength wallet daemon's HTTP/JSON gateway to mint invoices from, e.g. http://localhost:10061"`
// WavelengthMacaroonPath is the path to the macaroon authorizing calls
// to that gateway. A wallet daemon requires one unless it was started
// with rpc.no-macaroons, which it refuses to accept on mainnet, so
// leaving this empty only works against a daemon deliberately running
// without authentication.
WavelengthMacaroonPath string `long:"wavelengthmacaroonpath" description:"Path to the macaroon authorizing calls to the Wavelength wallet daemon's gateway"`
TLSPath string `long:"tlspath" description:"Path to LND instance's tls certificate"`
MacDir string `long:"macdir" description:"Directory containing LND instance's macaroons"`
// The one-time-use passphrase used to set up the connection. This field
// identifies the connection that will be used.
Passphrase string `long:"passphrase" description:"the lnc passphrase"`
// MailboxAddress is the address of the mailbox that the client will
// use for the LNC connection.
MailboxAddress string `long:"mailboxaddress" description:"the host:port of the mailbox server to be used"`
// DevServer set to true to skip verification of the mailbox server's
// tls cert.
DevServer bool `long:"devserver" description:"set to true to skip verification of the server's tls cert."`
// EnableMPP enables the Payment HTTP Authentication Scheme (MPP)
// alongside the existing L402 scheme.
EnableMPP bool `long:"enablempp" description:"Enable the Payment HTTP Authentication Scheme (MPP)"`
// MPPRealm is the realm string used in MPP challenges. Defaults to
// the server's listen address if empty.
MPPRealm string `long:"mpprealm" description:"Realm string for MPP challenges"`
// EnableSessions enables the MPP session intent, which provides
// prepaid sessions with deposit, bearer, top-up, and close
// operations.
EnableSessions bool `long:"enablesessions" description:"Enable MPP session intent (requires payment sending capability)"`
// SessionDepositMultiplier is the number of service units per deposit.
// Defaults to 20 if not set.
SessionDepositMultiplier int `long:"sessiondepositmultiplier" description:"Number of service units per session deposit" default-mask:"20"`
// SessionIdleTimeout is the idle timeout for sessions in seconds.
// Defaults to 300 (5 minutes) if not set.
SessionIdleTimeout int `long:"sessionidletimeout" description:"Session idle timeout in seconds" default-mask:"300"`
}
func (a *AuthConfig) validate() error {
// If we're disabled, we don't mind what these values are.
if a.Disable {
return nil
}
// Sessions require MPP to be enabled.
if a.EnableSessions && !a.EnableMPP {
return errors.New("enablesessions requires enablempp " +
"to be set")
}
switch {
// If WavelengthGateway is set we mint invoices from a Wavelength
// wallet daemon and there is no Lightning node in the picture at all.
case a.WavelengthGateway != "":
log.Info("Validating wavelength configuration")
// The lnd and lnc fields describe a node this backend does not
// use. Silently ignoring them would let a half-migrated config
// look like it was still talking to the node it names.
if a.LndHost != "" || a.Passphrase != "" {
return errors.New("lndhost and passphrase cannot be " +
"set when minting invoices from a wavelength " +
"wallet")
}
return nil
// If LndHost is set we connect directly to the LND node.
case a.LndHost != "":
log.Info("Validating lnd configuration")
if a.Passphrase != "" {
return errors.New("passphrase field cannot be set " +
"when connecting directly to the lnd node")
}
return a.validateLNDAuth()
// If Passphrase is set we connect to the LND node through LNC.
case a.Passphrase != "":
log.Info("Validating lnc configuration")
return a.validateLNCAuth()
default:
return errors.New("invalid authenticator configuration")
}
}
// validateLNDAuth validates the direct LND auth configuration.
func (a *AuthConfig) validateLNDAuth() error {
if a.LndHost == "" {
return errors.New("lnd host required")
}
if a.TLSPath == "" {
return errors.New("lnd tls required")
}
if a.MacDir == "" {
return errors.New("lnd mac dir required")
}
return nil
}
// validateLNCAuth validates the LNC auth configuration.
func (a *AuthConfig) validateLNCAuth() error {
switch {
case a.Passphrase == "":
return errors.New("lnc passphrase required")
case a.MailboxAddress == "":
return errors.New("lnc mailbox address required")
case a.Network == "":
return errors.New("lnc network required")
}
return nil
}
type HashMailConfig struct {
Enabled bool `long:"enabled"`
MessageRate time.Duration `long:"messagerate" description:"The average minimum time that should pass between each message."`
MessageBurstAllowance int `long:"messageburstallowance" description:"The burst rate we allow for messages."`
StaleTimeout time.Duration `long:"staletimeout" description:"The time after the last activity that a mailbox should be removed. Set to -1s to disable. "`
}
type TorConfig struct {
Control string `long:"control" description:"The host:port of the Tor instance."`
ListenPort uint16 `long:"listenport" description:"The port we should listen on for client requests over Tor. Note that this port should not be exposed to the outside world, it is only intended to be reached by clients through the onion service."`
VirtualPort uint16 `long:"virtualport" description:"The port through which the onion services created can be reached at."`
V3 bool `long:"v3" description:"Whether we should listen for client requests through a v3 onion service."`
}
// AdminConfig holds the configuration for the admin gRPC/REST API.
type AdminConfig struct {
// Enabled determines whether the admin API is active.
Enabled bool `long:"enabled" description:"Enable the admin API."`
// MacaroonPath is the path where the admin macaroon will be written.
// Defaults to admin.macaroon in the aperture data directory.
MacaroonPath string `long:"macaroonpath" description:"Path to write the admin macaroon."`
// CORSOrigins controls which origins are allowed to call the admin REST
// API. If empty, CORS is disabled and browsers can only use same-origin.
CORSOrigins []string `long:"corsorigin" description:"Allowed CORS origins for the admin REST API."`
}
type Config struct {
// ListenAddr is the listening address that we should use to allow Aperture
// to listen for requests.
ListenAddr string `long:"listenaddr" description:"The interface we should listen on for client requests."`
// ServerName can be set to a fully qualifying domain name that should
// be used while creating a certificate through Let's Encrypt.
ServerName string `long:"servername" description:"Server name (FQDN) to use for the TLS certificate."`
// AutoCert can be set to true if aperture should try to create a valid
// certificate through Let's Encrypt using ServerName.
AutoCert bool `long:"autocert" description:"Automatically create a Let's Encrypt cert using ServerName."`
// Insecure can be set to disable TLS on incoming connections.
Insecure bool `long:"insecure" description:"Listen on an insecure connection, disabling TLS for incoming connections."`
// StaticRoot is the folder where the static content served by the proxy
// is located.
StaticRoot string `long:"staticroot" description:"The folder where the static content is located."`
// ServeStatic defines if static content should be served from the
// directory defined by StaticRoot.
ServeStatic bool `long:"servestatic" description:"Flag to enable or disable static content serving."`
// DatabaseBackend is the database backend to be used by the server.
DatabaseBackend string `long:"dbbackend" description:"The database backend to use for storing all asset related data." choice:"sqlite" choice:"postgres" yaml:"dbbackend"`
// Sqlite is the configuration section for the SQLite database backend.
Sqlite *aperturedb.SqliteConfig `group:"sqlite" namespace:"sqlite"`
// Postgres is the configuration section for the Postgres database backend.
Postgres *aperturedb.PostgresConfig `group:"postgres" namespace:"postgres"`
// Etcd is the configuration section for the Etcd database backend.
Etcd *EtcdConfig `group:"etcd" namespace:"etcd"`
// Authenticator is the configuration section for connecting directly
// to the LND node.
Authenticator *AuthConfig `group:"authenticator" namespace:"authenticator"`
Tor *TorConfig `group:"tor" namespace:"tor"`
// Services is a list of JSON objects in string format, which specify
// each backend service to Aperture.
Services []*proxy.Service `long:"service" description:"Configurations for each Aperture backend service."`
// HashMail is the configuration section for configuring the Lightning
// Node Connect mailbox server.
HashMail *HashMailConfig `group:"hashmail" namespace:"hashmail" description:"Configuration for the Lightning Node Connect mailbox server."`
// Prometheus is the config for setting up an endpoint for a Prometheus
// server to scrape metrics from.
Prometheus *PrometheusConfig `group:"prometheus" namespace:"prometheus" description:"Configuration setting up an endpoint that a Prometheus server can scrape."`
// Admin is the configuration for the admin REST API.
Admin *AdminConfig `group:"admin" namespace:"admin" description:"Configuration for the admin REST API."`
// DebugLevel is a string defining the log level for the service either
// for all subsystems the same or individual level by subsystem.
DebugLevel string `long:"debuglevel" description:"Debug level for the Aperture application and its subsystems."`
// ConfigFile points aperture to an alternative config file.
ConfigFile string `long:"configfile" description:"Custom path to a config file."`
// BaseDir is a custom directory to store all aperture flies.
BaseDir string `long:"basedir" description:"Directory to place all of aperture's files in."`
// Profile is the port on which the pprof profile will be served.
Profile uint16 `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65535"`
// IdleTimeout is the maximum amount of time a connection may be idle.
IdleTimeout time.Duration `long:"idletimeout" description:"The maximum amount of time a connection may be idle before being closed."`
// ReadTimeout is the maximum amount of time to wait for a request to
// be fully read.
ReadTimeout time.Duration `long:"readtimeout" description:"The maximum amount of time to wait for a request to be fully read."`
// WriteTimeout is the maximum amount of time to wait for a response to
// be fully written.
WriteTimeout time.Duration `long:"writetimeout" description:"The maximum amount of time to wait for a response to be fully written."`
// InvoiceBatchSize is the number of invoices to fetch in a single
// request.
InvoiceBatchSize int `long:"invoicebatchsize" description:"The number of invoices to fetch in a single request."`
// StrictVerify is a flag that indicates whether we should verify the
// invoice status strictly or not. If set to true, then this requires
// all invoices to be read from disk at start up.
StrictVerify bool `long:"strictverify" description:"Whether to verify the invoice status strictly or not."`
// Logging controls various aspects of aperture logging.
Logging *build.LogConfig `group:"logging" namespace:"logging"`
// WsPingInterval is the interval at which WebSocket-level pings are
// sent to connected clients. This keeps the underlying WebSocket
// connection alive through intermediary proxies and load balancers.
// Set to 0 to disable.
WsPingInterval time.Duration `long:"wspinginterval" description:"Interval for WebSocket-level ping messages to keep connections alive through proxies."`
// WsPongWait is the duration to wait for a pong response after
// sending a WebSocket-level ping. If no pong is received within this
// duration, the WebSocket connection is considered dead. Must be
// strictly less than WsPingInterval when pings are enabled.
WsPongWait time.Duration `long:"wspongwait" description:"Duration to wait for a WebSocket pong response before closing the connection."`
// Blocklist is a list of IPs to deny access to.
Blocklist []string `long:"blocklist" description:"List of IP addresses to block from accessing the proxy."`
}
func (c *Config) validate() error {
if !c.Authenticator.Disable {
if err := c.Authenticator.validate(); err != nil {
return err
}
}
if c.ListenAddr == "" {
return fmt.Errorf("missing listen address for server")
}
if c.InvoiceBatchSize <= 0 {
return fmt.Errorf("invoice batch size must be greater than 0")
}
if c.WsPingInterval < 0 {
return fmt.Errorf("wspinginterval must not be negative")
}
if c.WsPongWait < 0 {
return fmt.Errorf("wspongwait must not be negative")
}
if c.WsPingInterval > 0 && c.WsPongWait >= c.WsPingInterval {
return fmt.Errorf("wspongwait (%v) must be less than "+
"wspinginterval (%v)", c.WsPongWait,
c.WsPingInterval)
}
return nil
}
// DefaultSqliteConfig returns the default configuration for a sqlite backend.
func DefaultSqliteConfig() *aperturedb.SqliteConfig {
return &aperturedb.SqliteConfig{
SkipMigrations: false,
DatabaseFileName: defaultSqliteDatabasePath,
}
}
// NewConfig initializes a new Config variable.
func NewConfig() *Config {
return &Config{
DatabaseBackend: "etcd",
Etcd: &EtcdConfig{},
Sqlite: DefaultSqliteConfig(),
Postgres: &aperturedb.PostgresConfig{},
Authenticator: &AuthConfig{},
Tor: &TorConfig{},
HashMail: &HashMailConfig{},
Prometheus: &PrometheusConfig{},
Admin: &AdminConfig{},
IdleTimeout: defaultIdleTimeout,
ReadTimeout: defaultReadTimeout,
WriteTimeout: defaultWriteTimeout,
WsPingInterval: defaultWsPingInterval,
WsPongWait: defaultWsPongWait,
InvoiceBatchSize: defaultInvoiceBatchSize,
Logging: build.DefaultLogConfig(),
Blocklist: []string{},
StrictVerify: defaultStrictVerify,
}
}