mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-25 09:32:43 +00:00
feat(metrics): enable TLS/mTLS serving support (#1576)
* feat(config): add metrics TLS configuration Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(metrics): add naive TLS serving for metrics Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(metrics): import keypairreloader from a private project Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(metrics): properly surface errors with the metrics server Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(config): add CA certificate config value Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(metrics): enable mTLS support Signed-off-by: Xe Iaso <me@xeiaso.net> * doc(default-config): document how to set up TLS and mTLS Signed-off-by: Xe Iaso <me@xeiaso.net> * doc: document metrics TLS and mTLS Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: spelling Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
+88
-9
@@ -1,24 +1,34 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidMetricsConfig = errors.New("config: invalid metrics configuration")
|
||||
ErrNoMetricsBind = errors.New("config.Metrics: must define bind")
|
||||
ErrNoMetricsNetwork = errors.New("config.Metrics: must define network")
|
||||
ErrNoMetricsSocketMode = errors.New("config.Metrics: must define socket mode when using unix sockets")
|
||||
ErrInvalidMetricsSocketMode = errors.New("config.Metrics: invalid unix socket mode")
|
||||
ErrInvalidMetricsNetwork = errors.New("config.Metrics: invalid metrics network")
|
||||
ErrInvalidMetricsConfig = errors.New("config: invalid metrics configuration")
|
||||
ErrInvalidMetricsTLSConfig = errors.New("config: invalid metrics TLS configuration")
|
||||
ErrNoMetricsBind = errors.New("config.Metrics: must define bind")
|
||||
ErrNoMetricsNetwork = errors.New("config.Metrics: must define network")
|
||||
ErrNoMetricsSocketMode = errors.New("config.Metrics: must define socket mode when using unix sockets")
|
||||
ErrInvalidMetricsSocketMode = errors.New("config.Metrics: invalid unix socket mode")
|
||||
ErrInvalidMetricsNetwork = errors.New("config.Metrics: invalid metrics network")
|
||||
ErrNoMetricsTLSCertificate = errors.New("config.Metrics.TLS: must define certificate file")
|
||||
ErrNoMetricsTLSKey = errors.New("config.Metrics.TLS: must define key file")
|
||||
ErrInvalidMetricsTLSKeypair = errors.New("config.Metrics.TLS: keypair is invalid")
|
||||
ErrInvalidMetricsCACertificate = errors.New("config.Metrics.TLS: invalid CA certificate")
|
||||
ErrCantReadFile = errors.New("config: can't read required file")
|
||||
)
|
||||
|
||||
type Metrics struct {
|
||||
Bind string `json:"bind" yaml:"bind"`
|
||||
Network string `json:"network" yaml:"network"`
|
||||
SocketMode string `json:"socketMode" yaml:"socketMode"`
|
||||
Bind string `json:"bind" yaml:"bind"`
|
||||
Network string `json:"network" yaml:"network"`
|
||||
SocketMode string `json:"socketMode" yaml:"socketMode"`
|
||||
TLS *MetricsTLS `json:"tls" yaml:"tls"`
|
||||
}
|
||||
|
||||
func (m *Metrics) Valid() error {
|
||||
@@ -46,9 +56,78 @@ func (m *Metrics) Valid() error {
|
||||
errs = append(errs, ErrInvalidMetricsNetwork)
|
||||
}
|
||||
|
||||
if m.TLS != nil {
|
||||
if err := m.TLS.Valid(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return errors.Join(ErrInvalidMetricsConfig, errors.Join(errs...))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type MetricsTLS struct {
|
||||
Certificate string `json:"certificate" yaml:"certificate"`
|
||||
Key string `json:"key" yaml:"key"`
|
||||
CA string `json:"ca" yaml:"ca"`
|
||||
}
|
||||
|
||||
func (mt *MetricsTLS) Valid() error {
|
||||
var errs []error
|
||||
|
||||
if mt.Certificate == "" {
|
||||
errs = append(errs, ErrNoMetricsTLSCertificate)
|
||||
}
|
||||
|
||||
if err := canReadFile(mt.Certificate); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%w %s: %w", ErrCantReadFile, mt.Certificate, err))
|
||||
}
|
||||
|
||||
if mt.Key == "" {
|
||||
errs = append(errs, ErrNoMetricsTLSKey)
|
||||
}
|
||||
|
||||
if err := canReadFile(mt.Key); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%w %s: %w", ErrCantReadFile, mt.Key, err))
|
||||
}
|
||||
|
||||
if _, err := tls.LoadX509KeyPair(mt.Certificate, mt.Key); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%w: %w", ErrInvalidMetricsTLSKeypair, err))
|
||||
}
|
||||
|
||||
if mt.CA != "" {
|
||||
caCert, err := os.ReadFile(mt.CA)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("%w %s: %w", ErrCantReadFile, mt.CA, err))
|
||||
}
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
if !certPool.AppendCertsFromPEM(caCert) {
|
||||
errs = append(errs, fmt.Errorf("%w %s", ErrInvalidMetricsCACertificate, mt.CA))
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) != 0 {
|
||||
return errors.Join(ErrInvalidMetricsTLSConfig, errors.Join(errs...))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func canReadFile(fname string) error {
|
||||
fin, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fin.Close()
|
||||
|
||||
data := make([]byte, 64)
|
||||
if _, err := fin.Read(data); err != nil {
|
||||
return fmt.Errorf("can't read %s: %w", fname, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user