Skip to content

Commit

Permalink
TLS support (#11)
Browse files Browse the repository at this point in the history
## 📝 Summary

Support for TLS encrypted requests. Can create cert+key files, or use
existing ones.

New configuration options:

```toml
[general]

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

## ⛱ Motivation and Context

Encryption of requests

---

## ✅ I have run these commands

* [x] `make lint`
* [x] `make test`
* [x] `go mod tidy`
  • Loading branch information
metachris authored Nov 27, 2024
1 parent 3f9fa05 commit a8296e4
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 11 deletions.
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
}

0 comments on commit a8296e4

Please sign in to comment.