Skip to content

Commit

Permalink
fix(tls): Pass certificate and private key files to listener method (#…
Browse files Browse the repository at this point in the history
…531)

Fixes #530
  • Loading branch information
TwiN committed Jul 20, 2023
1 parent 2f6b8f2 commit fd17dcd
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 26 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1278,14 +1278,14 @@ Confused? Read [Securing Gatus with OIDC using Auth0](https://twin.sh/articles/5

### TLS Encryption
Gatus supports basic encryption with TLS. To enable this, certificate files in PEM format have to be provided.
The example below shows an example configuration which makes gatus respond on port 4443 to HTTPS requests.

The example below shows an example configuration which makes gatus respond on port 4443 to HTTPS requests:
```yaml
web:
port: 4443
tls:
certificate-file: "server.crt"
private-key-file: "server.key"
certificate-file: "certificate.crt"
private-key-file: "private.key"
```

### Metrics
Expand Down
20 changes: 7 additions & 13 deletions config/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ type TLSConfig struct {

// PrivateKeyFile is the private key file for TLS in PEM format.
PrivateKeyFile string `yaml:"private-key-file,omitempty"`

tlsConfig *tls.Config
}

// GetDefaultConfig returns a Config struct with the default values
Expand All @@ -57,33 +55,29 @@ func (web *Config) ValidateAndSetDefaults() error {
}
// Try to load the TLS certificates
if web.TLS != nil {
if err := web.TLS.loadConfig(); err != nil {
if err := web.TLS.isValid(); err != nil {
return fmt.Errorf("invalid tls config: %w", err)
}
}
return nil
}

func (web *Config) HasTLS() bool {
return web.TLS != nil && len(web.TLS.CertificateFile) > 0 && len(web.TLS.PrivateKeyFile) > 0
}

// SocketAddress returns the combination of the Address and the Port
func (web *Config) SocketAddress() string {
return fmt.Sprintf("%s:%d", web.Address, web.Port)
}

func (t *TLSConfig) loadConfig() error {
func (t *TLSConfig) isValid() error {
if len(t.CertificateFile) > 0 && len(t.PrivateKeyFile) > 0 {
certificate, err := tls.LoadX509KeyPair(t.CertificateFile, t.PrivateKeyFile)
_, err := tls.LoadX509KeyPair(t.CertificateFile, t.PrivateKeyFile)
if err != nil {
return err
}
t.tlsConfig = &tls.Config{Certificates: []tls.Certificate{certificate}}
return nil
}
return errors.New("certificate-file and private-key-file must be specified")
}

func (web *Config) TLSConfig() *tls.Config {
if web.TLS != nil {
return web.TLS.tlsConfig
}
return nil
}
43 changes: 37 additions & 6 deletions config/web/web_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ func TestConfig_ValidateAndSetDefaults(t *testing.T) {
cfg: &Config{Port: 100000000},
expectedErr: true,
},
{
name: "with-good-tls-config",
cfg: &Config{Port: 443, TLS: &TLSConfig{CertificateFile: "../../testdata/cert.pem", PrivateKeyFile: "../../testdata/cert.key"}},
expectedAddress: "0.0.0.0",
expectedPort: 443,
expectedErr: false,
},
{
name: "with-bad-tls-config",
cfg: &Config{Port: 443, TLS: &TLSConfig{CertificateFile: "../../testdata/badcert.pem", PrivateKeyFile: "../../testdata/cert.key"}},
expectedAddress: "0.0.0.0",
expectedPort: 443,
expectedErr: true,
},
{
name: "with-partial-tls-config",
cfg: &Config{Port: 443, TLS: &TLSConfig{CertificateFile: "", PrivateKeyFile: "../../testdata/cert.key"}},
expectedAddress: "0.0.0.0",
expectedPort: 443,
expectedErr: true,
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
Expand Down Expand Up @@ -67,7 +88,7 @@ func TestConfig_SocketAddress(t *testing.T) {
}
}

func TestConfig_TLSConfig(t *testing.T) {
func TestConfig_isValid(t *testing.T) {
scenarios := []struct {
name string
cfg *Config
Expand All @@ -79,27 +100,37 @@ func TestConfig_TLSConfig(t *testing.T) {
expectedErr: false,
},
{
name: "missing-crt-file",
name: "missing-certificate-file",
cfg: &Config{TLS: &TLSConfig{CertificateFile: "doesnotexist", PrivateKeyFile: "../../testdata/cert.key"}},
expectedErr: true,
},
{
name: "bad-crt-file",
name: "bad-certificate-file",
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/badcert.pem", PrivateKeyFile: "../../testdata/cert.key"}},
expectedErr: true,
},
{
name: "no-certificate-file",
cfg: &Config{TLS: &TLSConfig{CertificateFile: "", PrivateKeyFile: "../../testdata/cert.key"}},
expectedErr: true,
},
{
name: "missing-private-key-file",
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/cert.pem", PrivateKeyFile: "doesnotexist"}},
expectedErr: true,
},
{
name: "no-private-key-file",
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/cert.pem", PrivateKeyFile: ""}},
expectedErr: true,
},
{
name: "bad-private-key-file",
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/cert.pem", PrivateKeyFile: "../../testdata/badcert.key"}},
expectedErr: true,
},
{
name: "bad-cert-and-private-key-file",
name: "bad-certificate-and-private-key-file",
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/badcert.pem", PrivateKeyFile: "../../testdata/badcert.key"}},
expectedErr: true,
},
Expand All @@ -112,8 +143,8 @@ func TestConfig_TLSConfig(t *testing.T) {
return
}
if !scenario.expectedErr {
if scenario.cfg.TLS.tlsConfig == nil {
t.Error("TLS configuration was not correctly loaded although no error was returned")
if scenario.cfg.TLS.isValid() != nil {
t.Error("cfg.TLS.isValid() returned an error even though no error was expected")
}
}
})
Expand Down
14 changes: 10 additions & 4 deletions controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,22 @@ func Handle(cfg *config.Config) {
server.ReadTimeout = 15 * time.Second
server.WriteTimeout = 15 * time.Second
server.IdleTimeout = 15 * time.Second
server.TLSConfig = cfg.Web.TLSConfig()
if os.Getenv("ROUTER_TEST") == "true" {
return
}
log.Println("[controller][Handle] Listening on " + cfg.Web.SocketAddress())
if server.TLSConfig != nil {
log.Println("[controller][Handle]", app.ListenTLS(cfg.Web.SocketAddress(), "", ""))
if cfg.Web.HasTLS() {
err := app.ListenTLS(cfg.Web.SocketAddress(), cfg.Web.TLS.CertificateFile, cfg.Web.TLS.PrivateKeyFile)
if err != nil {
log.Fatal("[controller][Handle]", err)
}
} else {
log.Println("[controller][Handle]", app.Listen(cfg.Web.SocketAddress()))
err := app.Listen(cfg.Web.SocketAddress())
if err != nil {
log.Fatal("[controller][Handle]", err)
}
}
log.Println("[controller][Handle] Server has shut down successfully")
}

// Shutdown stops the server
Expand Down

0 comments on commit fd17dcd

Please sign in to comment.