Add autocert
This commit is contained in:
parent
f356858f02
commit
7ce2b2aa2b
13
apiary.toml
13
apiary.toml
@ -8,7 +8,8 @@ Type = "memory"
|
||||
[Store.Postgres]
|
||||
# Connection string for postgres
|
||||
# Must be set if using Store.Type = "postgres"
|
||||
DSN = "postgresql://film:film@10.69.10.130:5432/film?sslmode=disable"
|
||||
DSN = "postgresql://user:password@example.org:5432/apiary"
|
||||
|
||||
[Honeypot]
|
||||
# Path to SSH host key
|
||||
# If empty, a new one will be generated each time the service starts
|
||||
@ -36,12 +37,18 @@ ListenAddr = ":8080"
|
||||
|
||||
[Frontend.Autocert]
|
||||
# Enable using letsencrypt for automatic certificates
|
||||
# When enabled Frontend.ListenAddr will be ignored, and the server
|
||||
# will listen to 443
|
||||
# Default: false
|
||||
Enable = false
|
||||
# Email address for certificate owner
|
||||
Email = ""
|
||||
# Domains to use for certificates. Required when using autocert.
|
||||
# Default: ""
|
||||
Domains = "example.org"
|
||||
Domains = ["example.org"]
|
||||
# Dir where certificates are cached.
|
||||
# Default: "/tmp"
|
||||
CacheDir = "/var/apiary/certs"
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
# Default: true
|
||||
RedirectHTTP = true
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"github.uio.no/torjus/apiary/web"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -50,8 +51,10 @@ func ActionServe(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup logging
|
||||
loggers := setupLoggers(cfg)
|
||||
|
||||
// Setup store
|
||||
var s store.LoginAttemptStore
|
||||
switch cfg.Store.Type {
|
||||
case "MEMORY", "memory":
|
||||
@ -69,16 +72,32 @@ func ActionServe(c *cli.Context) error {
|
||||
return fmt.Errorf("Invalid store configured")
|
||||
}
|
||||
|
||||
// Setup honeypot
|
||||
hs, err := honeypot.NewHoneypotServer(cfg.Honeypot, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hs.Logger = loggers.honeypotLogger
|
||||
|
||||
// Setup webserver
|
||||
web := web.NewServer(cfg.Frontend, hs, s)
|
||||
web.AccessLogger = loggers.webAccessLogger
|
||||
web.ServerLogger = loggers.webServerLogger
|
||||
if cfg.Frontend.Autocert.Enable {
|
||||
certManager := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist(cfg.Frontend.Autocert.Domains...),
|
||||
Email: cfg.Frontend.Autocert.Email,
|
||||
}
|
||||
if cfg.Frontend.Autocert.CacheDir != "" {
|
||||
certManager.Cache = autocert.DirCache(cfg.Frontend.Autocert.CacheDir)
|
||||
}
|
||||
|
||||
tlsConfig := certManager.TLSConfig()
|
||||
web.TLSConfig = tlsConfig
|
||||
}
|
||||
|
||||
// Setup interrupt handling
|
||||
interruptChan := make(chan os.Signal, 1)
|
||||
signal.Notify(interruptChan, os.Interrupt)
|
||||
|
||||
@ -103,7 +122,7 @@ func ActionServe(c *cli.Context) error {
|
||||
// Start web server
|
||||
go func() {
|
||||
loggers.rootLogger.Info("Starting web server")
|
||||
if err := web.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
if err := web.StartServe(); err != nil && err != http.ErrServerClosed {
|
||||
loggers.rootLogger.Warnw("Web server returned error", "error", err)
|
||||
}
|
||||
}()
|
||||
|
@ -33,6 +33,15 @@ type FrontendConfig struct {
|
||||
ListenAddr string `toml:"ListenAddr"`
|
||||
LogLevel string `toml:"LogLevel"`
|
||||
AccessLogEnable bool `toml:"AccessLogEnable"`
|
||||
Autocert FrontendAutocertConfig `toml:"Autocert"`
|
||||
}
|
||||
|
||||
type FrontendAutocertConfig struct {
|
||||
Enable bool `toml:"Enable"`
|
||||
Email string `toml:"Email"`
|
||||
Domains []string `toml:"Domains"`
|
||||
CacheDir string `toml:"CacheDir"`
|
||||
RedirectHTTP bool `toml:"RedirectHTTP"`
|
||||
}
|
||||
|
||||
func FromReader(r io.Reader) (Config, error) {
|
||||
|
1
go.mod
1
go.mod
@ -16,5 +16,6 @@ require (
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
go.uber.org/zap v1.13.0
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 // indirect
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect
|
||||
)
|
||||
|
6
go.sum
6
go.sum
@ -400,6 +400,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 h1:4qWs8cYYH6PoEFy4dfhDFgoMGkwAcETd+MmPdCPMzUc=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -430,14 +432,16 @@ golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
||||
|
@ -55,7 +55,6 @@ func NewHoneypotServer(cfg config.HoneypotConfig, store store.LoginAttemptStore)
|
||||
}
|
||||
|
||||
hs.sshServer.AddHostKey(signer)
|
||||
|
||||
}
|
||||
|
||||
return &hs, nil
|
||||
|
@ -18,11 +18,14 @@ import (
|
||||
"github.uio.no/torjus/apiary/honeypot/store"
|
||||
"github.uio.no/torjus/apiary/models"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
http.Server
|
||||
|
||||
cfg config.FrontendConfig
|
||||
|
||||
store store.LoginAttemptStore
|
||||
|
||||
ServerLogger *zap.SugaredLogger
|
||||
@ -34,6 +37,7 @@ type Server struct {
|
||||
attemptListeners map[string]chan models.LoginAttempt
|
||||
|
||||
streamContext context.Context
|
||||
httpRedirectServer http.Server
|
||||
}
|
||||
|
||||
func NewServer(cfg config.FrontendConfig, hs *honeypot.HoneypotServer, store store.LoginAttemptStore) *Server {
|
||||
@ -41,8 +45,35 @@ func NewServer(cfg config.FrontendConfig, hs *honeypot.HoneypotServer, store sto
|
||||
ServerLogger: zap.NewNop().Sugar(),
|
||||
AccessLogger: zap.NewNop().Sugar(),
|
||||
store: store,
|
||||
cfg: cfg,
|
||||
}
|
||||
|
||||
if cfg.Autocert.Enable {
|
||||
certManager := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist(cfg.Autocert.Domains...),
|
||||
Email: cfg.Autocert.Email,
|
||||
}
|
||||
if cfg.Autocert.CacheDir != "" {
|
||||
certManager.Cache = autocert.DirCache(cfg.Autocert.CacheDir)
|
||||
}
|
||||
|
||||
tlsConfig := certManager.TLSConfig()
|
||||
s.TLSConfig = tlsConfig
|
||||
s.RegisterOnShutdown(func() {
|
||||
timeoutCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
s.httpRedirectServer.Shutdown(timeoutCtx)
|
||||
})
|
||||
s.Addr = ":443"
|
||||
|
||||
if cfg.Autocert.RedirectHTTP {
|
||||
s.httpRedirectServer.Addr = ":80"
|
||||
s.httpRedirectServer.Handler = certManager.HTTPHandler(nil)
|
||||
}
|
||||
} else {
|
||||
s.Addr = cfg.ListenAddr
|
||||
}
|
||||
|
||||
r := chi.NewRouter()
|
||||
|
||||
@ -79,6 +110,22 @@ func NewServer(cfg config.FrontendConfig, hs *honeypot.HoneypotServer, store sto
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) StartServe() error {
|
||||
if s.cfg.Autocert.Enable {
|
||||
if s.cfg.Autocert.RedirectHTTP {
|
||||
s.ServerLogger.Debug("Starting HTTP redirect server")
|
||||
go func() {
|
||||
if err := s.httpRedirectServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
s.ServerLogger.Warnw("HTTP redirect server returned error", "error", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return s.ListenAndServeTLS("", "")
|
||||
} else {
|
||||
return s.ListenAndServe()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) addAttemptListener() (string, chan models.LoginAttempt) {
|
||||
ch := make(chan models.LoginAttempt)
|
||||
s.attemptListenersLock.Lock()
|
||||
|
Loading…
Reference in New Issue
Block a user