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]
|
[Store.Postgres]
|
||||||
# Connection string for postgres
|
# Connection string for postgres
|
||||||
# Must be set if using Store.Type = "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]
|
[Honeypot]
|
||||||
# Path to SSH host key
|
# Path to SSH host key
|
||||||
# If empty, a new one will be generated each time the service starts
|
# If empty, a new one will be generated each time the service starts
|
||||||
@ -36,12 +37,18 @@ ListenAddr = ":8080"
|
|||||||
|
|
||||||
[Frontend.Autocert]
|
[Frontend.Autocert]
|
||||||
# Enable using letsencrypt for automatic certificates
|
# Enable using letsencrypt for automatic certificates
|
||||||
|
# When enabled Frontend.ListenAddr will be ignored, and the server
|
||||||
|
# will listen to 443
|
||||||
# Default: false
|
# Default: false
|
||||||
Enable = false
|
Enable = false
|
||||||
|
# Email address for certificate owner
|
||||||
|
Email = ""
|
||||||
# Domains to use for certificates. Required when using autocert.
|
# Domains to use for certificates. Required when using autocert.
|
||||||
# Default: ""
|
# Default: ""
|
||||||
Domains = "example.org"
|
Domains = ["example.org"]
|
||||||
# Dir where certificates are cached.
|
# Dir where certificates are cached.
|
||||||
# Default: "/tmp"
|
# Default: "/tmp"
|
||||||
CacheDir = "/var/apiary/certs"
|
CacheDir = "/var/apiary/certs"
|
||||||
|
# Redirect HTTP to HTTPS
|
||||||
|
# Default: true
|
||||||
|
RedirectHTTP = true
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.uio.no/torjus/apiary/web"
|
"github.uio.no/torjus/apiary/web"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"go.uber.org/zap/zapcore"
|
"go.uber.org/zap/zapcore"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -50,8 +51,10 @@ func ActionServe(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup logging
|
||||||
loggers := setupLoggers(cfg)
|
loggers := setupLoggers(cfg)
|
||||||
|
|
||||||
|
// Setup store
|
||||||
var s store.LoginAttemptStore
|
var s store.LoginAttemptStore
|
||||||
switch cfg.Store.Type {
|
switch cfg.Store.Type {
|
||||||
case "MEMORY", "memory":
|
case "MEMORY", "memory":
|
||||||
@ -69,16 +72,32 @@ func ActionServe(c *cli.Context) error {
|
|||||||
return fmt.Errorf("Invalid store configured")
|
return fmt.Errorf("Invalid store configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup honeypot
|
||||||
hs, err := honeypot.NewHoneypotServer(cfg.Honeypot, s)
|
hs, err := honeypot.NewHoneypotServer(cfg.Honeypot, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hs.Logger = loggers.honeypotLogger
|
hs.Logger = loggers.honeypotLogger
|
||||||
|
|
||||||
|
// Setup webserver
|
||||||
web := web.NewServer(cfg.Frontend, hs, s)
|
web := web.NewServer(cfg.Frontend, hs, s)
|
||||||
web.AccessLogger = loggers.webAccessLogger
|
web.AccessLogger = loggers.webAccessLogger
|
||||||
web.ServerLogger = loggers.webServerLogger
|
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)
|
interruptChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(interruptChan, os.Interrupt)
|
signal.Notify(interruptChan, os.Interrupt)
|
||||||
|
|
||||||
@ -103,7 +122,7 @@ func ActionServe(c *cli.Context) error {
|
|||||||
// Start web server
|
// Start web server
|
||||||
go func() {
|
go func() {
|
||||||
loggers.rootLogger.Info("Starting web server")
|
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)
|
loggers.rootLogger.Warnw("Web server returned error", "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -30,9 +30,18 @@ type HoneypotConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FrontendConfig struct {
|
type FrontendConfig struct {
|
||||||
ListenAddr string `toml:"ListenAddr"`
|
ListenAddr string `toml:"ListenAddr"`
|
||||||
LogLevel string `toml:"LogLevel"`
|
LogLevel string `toml:"LogLevel"`
|
||||||
AccessLogEnable bool `toml:"AccessLogEnable"`
|
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) {
|
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
|
github.com/urfave/cli/v2 v2.3.0
|
||||||
go.uber.org/zap v1.13.0
|
go.uber.org/zap v1.13.0
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
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
|
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-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-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-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-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
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=
|
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-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-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-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 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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.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.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.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-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-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
|
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)
|
hs.sshServer.AddHostKey(signer)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &hs, nil
|
return &hs, nil
|
||||||
|
@ -18,11 +18,14 @@ import (
|
|||||||
"github.uio.no/torjus/apiary/honeypot/store"
|
"github.uio.no/torjus/apiary/honeypot/store"
|
||||||
"github.uio.no/torjus/apiary/models"
|
"github.uio.no/torjus/apiary/models"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
http.Server
|
http.Server
|
||||||
|
|
||||||
|
cfg config.FrontendConfig
|
||||||
|
|
||||||
store store.LoginAttemptStore
|
store store.LoginAttemptStore
|
||||||
|
|
||||||
ServerLogger *zap.SugaredLogger
|
ServerLogger *zap.SugaredLogger
|
||||||
@ -33,7 +36,8 @@ type Server struct {
|
|||||||
attemptListenersLock sync.RWMutex
|
attemptListenersLock sync.RWMutex
|
||||||
attemptListeners map[string]chan models.LoginAttempt
|
attemptListeners map[string]chan models.LoginAttempt
|
||||||
|
|
||||||
streamContext context.Context
|
streamContext context.Context
|
||||||
|
httpRedirectServer http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(cfg config.FrontendConfig, hs *honeypot.HoneypotServer, store store.LoginAttemptStore) *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(),
|
ServerLogger: zap.NewNop().Sugar(),
|
||||||
AccessLogger: zap.NewNop().Sugar(),
|
AccessLogger: zap.NewNop().Sugar(),
|
||||||
store: store,
|
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
|
||||||
}
|
}
|
||||||
s.Addr = cfg.ListenAddr
|
|
||||||
|
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
|
||||||
@ -79,6 +110,22 @@ func NewServer(cfg config.FrontendConfig, hs *honeypot.HoneypotServer, store sto
|
|||||||
return s
|
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) {
|
func (s *Server) addAttemptListener() (string, chan models.LoginAttempt) {
|
||||||
ch := make(chan models.LoginAttempt)
|
ch := make(chan models.LoginAttempt)
|
||||||
s.attemptListenersLock.Lock()
|
s.attemptListenersLock.Lock()
|
||||||
|
Loading…
Reference in New Issue
Block a user