Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS support #11

Merged
merged 2 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/system-api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func runCli(cCtx *cli.Context) (err error) {
log.Error("Error creating server", "err", err)
return err
}
go server.Start()
go server.Start() //nolint:errcheck

// Wait for signal, then graceful shutdown
exit := make(chan os.Signal, 1)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ toolchain go1.23.1

require (
github.com/ethereum/go-ethereum v1.14.9
github.com/flashbots/go-utils v0.8.2
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/httplog/v2 v2.1.1
github.com/pelletier/go-toml/v2 v2.2.3
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etly
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/ethereum/go-ethereum v1.14.9 h1:J7iwXDrtUyE9FUjUYbd4c9tyzwMh6dTJsKzo9i6SrwA=
github.com/ethereum/go-ethereum v1.14.9/go.mod h1:QeW+MtTpRdBEm2pUFoonByee8zfHv7kGp0wK0odvU1I=
github.com/flashbots/go-utils v0.8.2 h1:8JUKd9Cv1CTcp63V03ya+47nflTwBEqZq357iwW4fxQ=
github.com/flashbots/go-utils v0.8.2/go.mod h1:Lo/nrlC+q8ANgT3e6MKALIJCU+V9qTSgNtoLk/q1uIw=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/httplog/v2 v2.1.1 h1:ojojiu4PIaoeJ/qAO4GWUxJqvYUTobeo7zmuHQJAxRk=
Expand Down
7 changes: 7 additions & 0 deletions systemapi-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ basic_auth_secret_salt = "D;%yL9TS:5PalS/d" # use a random string for the s
# http_read_timeout_ms = 2500
# http_write_timeout_ms = 2500

# TLS configuration
tls_enabled = true
tls_create_if_missing = true
tls_cert_hosts = ["localhost", ""]
tls_cert_path = "cert.pem"
tls_key_path = "key.pem"

[actions]
echo_test = "echo test"
# reboot = "reboot"
Expand Down
22 changes: 14 additions & 8 deletions systemapi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,24 @@ import (
var DefaultLogMaxEntries = common.GetEnvInt("MAX_EVENTS", 1000)

type systemAPIConfigGeneral struct {
ListenAddr string `toml:"listen_addr"`
PipeFile string `toml:"pipe_file"`
LogJSON bool `toml:"log_json"`
LogDebug bool `toml:"log_debug"`
ListenAddr string `toml:"listen_addr"` // Address (host and port) for server to listen on
PipeFile string `toml:"pipe_file"` // Path for the named pipe file
LogJSON bool `toml:"log_json"` // Enables JSON logging
LogDebug bool `toml:"log_debug"` // Enables debug logging
EnablePprof bool `toml:"pprof"` // Enables pprof endpoints
LogMaxEntries int `toml:"log_max_entries"` // Maximum number of log entries

BasicAuthSecretPath string `toml:"basic_auth_secret_path"`
BasicAuthSecretSalt string `toml:"basic_auth_secret_salt"`
BasicAuthSecretPath string `toml:"basic_auth_secret_path"` // Path to the file containing the basic auth secret hash
BasicAuthSecretSalt string `toml:"basic_auth_secret_salt"` // Path to the file containing the basic auth secret salt

HTTPReadTimeoutMillis int `toml:"http_read_timeout_ms"`
HTTPWriteTimeoutMillis int `toml:"http_write_timeout_ms"`
HTTPReadTimeoutMillis int `toml:"http_read_timeout_ms"` // A zero or negative value means there will be no timeout.
HTTPWriteTimeoutMillis int `toml:"http_write_timeout_ms"` // A zero or negative value means there will be no timeout.

TLSEnabled bool `toml:"tls_enabled"` // Enable TLS
TLSCreateIfMissing bool `toml:"tls_create_if_missing"` // Create TLS cert and key files if they do not exist
TLSCertHosts []string `toml:"tls_cert_hosts"` // Hosts for the TLS cert
TLSCertPath string `toml:"tls_cert_path"` // Path to the TLS cert file
TLSKeyPath string `toml:"tls_key_path"` // Path to the TLS key file
}

type SystemAPIConfig struct {
Expand Down
7 changes: 7 additions & 0 deletions systemapi/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ func TestLoadConfig(t *testing.T) {
require.NotNil(t, cfg)
require.NotEmpty(t, cfg.Actions)
require.Equal(t, "echo test", cfg.Actions["echo_test"])

// check TLS config
require.True(t, cfg.General.TLSEnabled)
require.True(t, cfg.General.TLSCreateIfMissing)
require.NotEmpty(t, cfg.General.TLSCertHosts)
require.NotEmpty(t, cfg.General.TLSCertPath)
require.NotEmpty(t, cfg.General.TLSKeyPath)
}

func TestEmptyConfig(t *testing.T) {
Expand Down
22 changes: 20 additions & 2 deletions systemapi/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ func NewServer(log *httplog.Logger, cfg *SystemAPIConfig) (server *Server, err e
go server.readPipeInBackground()
}

// Load or create TLS certificate
if cfg.General.TLSEnabled {
err = server.createTLSCertIfNotExists()
if err != nil {
server.log.Error("Failed to create TLS certificate", "err", err)
return nil, err
}
}

// Create the HTTP server
server.srv = &http.Server{
Addr: cfg.General.ListenAddr,
Expand Down Expand Up @@ -180,11 +189,20 @@ func (s *Server) readPipeInBackground() {
}
}

func (s *Server) Start() {
func (s *Server) Start() (err error) {
s.log.Info("Starting HTTP server", "listenAddress", s.cfg.General.ListenAddr)
if err := s.srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {

if s.cfg.General.TLSEnabled {
s.log.Info("TLS enabled", "cert", s.cfg.General.TLSCertPath, "key", s.cfg.General.TLSKeyPath)
err = s.srv.ListenAndServeTLS(s.cfg.General.TLSCertPath, s.cfg.General.TLSKeyPath)
} else {
err = s.srv.ListenAndServe()
}
if err != nil && !errors.Is(err, http.ErrServerClosed) {
s.log.Error("HTTP server failed", "err", err)
return err
}
return nil
}

func (s *Server) Shutdown(ctx context.Context) error {
Expand Down
58 changes: 58 additions & 0 deletions systemapi/tls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package systemapi

import (
"errors"
"os"
"time"

"github.com/flashbots/go-utils/tls"
)

// createTLSCertIfNotExists created a cert and key file if it doesn't exist yet
func (s *Server) createTLSCertIfNotExists() error {
log := s.log.With("cert", s.cfg.General.TLSCertPath, "key", s.cfg.General.TLSKeyPath)
_, err1 := os.Stat(s.cfg.General.TLSCertPath)
if err1 != nil && !os.IsNotExist(err1) {
return err1
}

_, err2 := os.Stat(s.cfg.General.TLSKeyPath)
if err2 != nil && !os.IsNotExist(err2) {
return err2
}

certFileExists := err1 == nil
keyFileExists := err2 == nil
if certFileExists && keyFileExists {
// Files exist, use them
log.Info("TLS cert and key found, using them")
return nil
} else if certFileExists || keyFileExists {
// Only one of the files exist, should not happen
return errors.New("both TLS cert and key files are required, but only one exists")
}

// Files do not exist, should create them
if !s.cfg.General.TLSCreateIfMissing {
return errors.New("TLS cert and key files do not exist, but config is set to not create them")
}

// Create them
cert, key, err := tls.GenerateTLS(time.Hour*24*365, s.cfg.General.TLSCertHosts)
if err != nil {
return err
}

err = os.WriteFile(s.cfg.General.TLSCertPath, cert, 0o600)
if err != nil {
return err
}

err = os.WriteFile(s.cfg.General.TLSKeyPath, key, 0o600)
if err != nil {
return err
}

log.With("hosts", s.cfg.General.TLSCertHosts).Info("TLS cert and key files created")
return nil
}
Loading