mirror of
https://github.com/TwiN/gatus.git
synced 2024-12-14 11:58:04 +00:00
fix(tls): Pass certificate and private key files to listener method (#531)
Fixes #530
This commit is contained in:
parent
2f6b8f23f7
commit
fd17dcd204
4 changed files with 57 additions and 26 deletions
|
@ -1278,14 +1278,14 @@ Confused? Read [Securing Gatus with OIDC using Auth0](https://twin.sh/articles/5
|
||||||
|
|
||||||
### TLS Encryption
|
### TLS Encryption
|
||||||
Gatus supports basic encryption with TLS. To enable this, certificate files in PEM format have to be provided.
|
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
|
```yaml
|
||||||
web:
|
web:
|
||||||
port: 4443
|
port: 4443
|
||||||
tls:
|
tls:
|
||||||
certificate-file: "server.crt"
|
certificate-file: "certificate.crt"
|
||||||
private-key-file: "server.key"
|
private-key-file: "private.key"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Metrics
|
### Metrics
|
||||||
|
|
|
@ -34,8 +34,6 @@ type TLSConfig struct {
|
||||||
|
|
||||||
// PrivateKeyFile is the private key file for TLS in PEM format.
|
// PrivateKeyFile is the private key file for TLS in PEM format.
|
||||||
PrivateKeyFile string `yaml:"private-key-file,omitempty"`
|
PrivateKeyFile string `yaml:"private-key-file,omitempty"`
|
||||||
|
|
||||||
tlsConfig *tls.Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaultConfig returns a Config struct with the default values
|
// GetDefaultConfig returns a Config struct with the default values
|
||||||
|
@ -57,33 +55,29 @@ func (web *Config) ValidateAndSetDefaults() error {
|
||||||
}
|
}
|
||||||
// Try to load the TLS certificates
|
// Try to load the TLS certificates
|
||||||
if web.TLS != nil {
|
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 fmt.Errorf("invalid tls config: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
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
|
// SocketAddress returns the combination of the Address and the Port
|
||||||
func (web *Config) SocketAddress() string {
|
func (web *Config) SocketAddress() string {
|
||||||
return fmt.Sprintf("%s:%d", web.Address, web.Port)
|
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 {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.tlsConfig = &tls.Config{Certificates: []tls.Certificate{certificate}}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New("certificate-file and private-key-file must be specified")
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -37,6 +37,27 @@ func TestConfig_ValidateAndSetDefaults(t *testing.T) {
|
||||||
cfg: &Config{Port: 100000000},
|
cfg: &Config{Port: 100000000},
|
||||||
expectedErr: true,
|
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 {
|
for _, scenario := range scenarios {
|
||||||
t.Run(scenario.name, func(t *testing.T) {
|
t.Run(scenario.name, func(t *testing.T) {
|
||||||
|
@ -67,7 +88,7 @@ func TestConfig_SocketAddress(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_TLSConfig(t *testing.T) {
|
func TestConfig_isValid(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
name string
|
name string
|
||||||
cfg *Config
|
cfg *Config
|
||||||
|
@ -79,27 +100,37 @@ func TestConfig_TLSConfig(t *testing.T) {
|
||||||
expectedErr: false,
|
expectedErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing-crt-file",
|
name: "missing-certificate-file",
|
||||||
cfg: &Config{TLS: &TLSConfig{CertificateFile: "doesnotexist", PrivateKeyFile: "../../testdata/cert.key"}},
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "doesnotexist", PrivateKeyFile: "../../testdata/cert.key"}},
|
||||||
expectedErr: true,
|
expectedErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bad-crt-file",
|
name: "bad-certificate-file",
|
||||||
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/badcert.pem", PrivateKeyFile: "../../testdata/cert.key"}},
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/badcert.pem", PrivateKeyFile: "../../testdata/cert.key"}},
|
||||||
expectedErr: true,
|
expectedErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "no-certificate-file",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "", PrivateKeyFile: "../../testdata/cert.key"}},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "missing-private-key-file",
|
name: "missing-private-key-file",
|
||||||
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/cert.pem", PrivateKeyFile: "doesnotexist"}},
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/cert.pem", PrivateKeyFile: "doesnotexist"}},
|
||||||
expectedErr: true,
|
expectedErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "no-private-key-file",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/cert.pem", PrivateKeyFile: ""}},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "bad-private-key-file",
|
name: "bad-private-key-file",
|
||||||
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/cert.pem", PrivateKeyFile: "../../testdata/badcert.key"}},
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/cert.pem", PrivateKeyFile: "../../testdata/badcert.key"}},
|
||||||
expectedErr: true,
|
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"}},
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../../testdata/badcert.pem", PrivateKeyFile: "../../testdata/badcert.key"}},
|
||||||
expectedErr: true,
|
expectedErr: true,
|
||||||
},
|
},
|
||||||
|
@ -112,8 +143,8 @@ func TestConfig_TLSConfig(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !scenario.expectedErr {
|
if !scenario.expectedErr {
|
||||||
if scenario.cfg.TLS.tlsConfig == nil {
|
if scenario.cfg.TLS.isValid() != nil {
|
||||||
t.Error("TLS configuration was not correctly loaded although no error was returned")
|
t.Error("cfg.TLS.isValid() returned an error even though no error was expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,16 +22,22 @@ func Handle(cfg *config.Config) {
|
||||||
server.ReadTimeout = 15 * time.Second
|
server.ReadTimeout = 15 * time.Second
|
||||||
server.WriteTimeout = 15 * time.Second
|
server.WriteTimeout = 15 * time.Second
|
||||||
server.IdleTimeout = 15 * time.Second
|
server.IdleTimeout = 15 * time.Second
|
||||||
server.TLSConfig = cfg.Web.TLSConfig()
|
|
||||||
if os.Getenv("ROUTER_TEST") == "true" {
|
if os.Getenv("ROUTER_TEST") == "true" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println("[controller][Handle] Listening on " + cfg.Web.SocketAddress())
|
log.Println("[controller][Handle] Listening on " + cfg.Web.SocketAddress())
|
||||||
if server.TLSConfig != nil {
|
if cfg.Web.HasTLS() {
|
||||||
log.Println("[controller][Handle]", app.ListenTLS(cfg.Web.SocketAddress(), "", ""))
|
err := app.ListenTLS(cfg.Web.SocketAddress(), cfg.Web.TLS.CertificateFile, cfg.Web.TLS.PrivateKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("[controller][Handle]", err)
|
||||||
|
}
|
||||||
} else {
|
} 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
|
// Shutdown stops the server
|
||||||
|
|
Loading…
Reference in a new issue