feat(config): add CA certificate config value

Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
Xe Iaso
2026-04-22 19:32:11 -04:00
parent b66630df74
commit dfeb02b4ae
6 changed files with 101 additions and 11 deletions
+26 -11
View File
@@ -2,6 +2,7 @@ package config
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"os"
@@ -9,17 +10,18 @@ import (
)
var (
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")
ErrCantReadFile = errors.New("config: can't read required file")
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 {
@@ -70,6 +72,7 @@ func (m *Metrics) Valid() error {
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 {
@@ -95,6 +98,18 @@ func (mt *MetricsTLS) Valid() error {
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...))
}
+38
View File
@@ -119,6 +119,44 @@ func TestMetricsValid(t *testing.T) {
},
err: ErrInvalidMetricsTLSKeypair,
},
{
name: "mTLS with CA",
input: &Metrics{
Bind: ":9090",
Network: "tcp",
TLS: &MetricsTLS{
Certificate: "./testdata/tls/selfsigned.crt",
Key: "./testdata/tls/selfsigned.key",
CA: "./testdata/tls/minica.pem",
},
},
},
{
name: "mTLS with nonexistent CA",
input: &Metrics{
Bind: ":9090",
Network: "tcp",
TLS: &MetricsTLS{
Certificate: "./testdata/tls/selfsigned.crt",
Key: "./testdata/tls/selfsigned.key",
CA: "./testdata/tls/nonexistent.crt",
},
},
err: ErrCantReadFile,
},
{
name: "mTLS with invalid CA",
input: &Metrics{
Bind: ":9090",
Network: "tcp",
TLS: &MetricsTLS{
Certificate: "./testdata/tls/selfsigned.crt",
Key: "./testdata/tls/selfsigned.key",
CA: "./testdata/tls/invalid.crt",
},
},
err: ErrInvalidMetricsCACertificate,
},
} {
t.Run(tt.name, func(t *testing.T) {
if err := tt.input.Valid(); !errors.Is(err, tt.err) {
+12
View File
@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIB1zCCAVygAwIBAgIIYO0SAFtXlVgwCgYIKoZIzj0EAwMwIDEeMBwGA1UEAxMV
bWluaWNhIHJvb3QgY2EgNDE2MmMwMB4XDTI2MDQyMjIzMjUwMVoXDTI4MDUyMjIz
MjUwMVowEjEQMA4GA1UEAxMHMS4xLjEuMTB2MBAGByqGSM49AgEGBSuBBAAiA2IA
BLsuA2LKGbEBuSA4LTm1KaKc7/QCkUOsipXR4+D5/3sWBZiAH7iWUgHwpx5YZf2q
kZn6oRda+ks0JLTQ6VhteQedmb7l86bMeDMR8p4Lg2b38l/xEr7S25UfUDKudXrO
AqNxMG8wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF
BQcDAjAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFE/7VDxF2+cUs9bu0pJM3xoC
L1TSMA8GA1UdEQQIMAaHBAEBAQEwCgYIKoZIzj0EAwMDaQAwZgIxAPLXds9MMH4K
F5FxTf9i0PKPsLQARsABVTgwB94hMR70rqW8Pwbjl7ZGNaYlaeRHUwIxAPMQ8zoF
nim+YS1xLqQek/LXuJto8jxcfkQQBsboVzcTa5uaNRhNd5YwrpomGl3lKA==
-----END CERTIFICATE-----
+6
View File
@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBN8QsHxxHGJpStu8K7
D/FmaBBNo6c514KGFSIfqGFuREF5aOL3gN/W11yk2OIibdWhZANiAAS7LgNiyhmx
AbkgOC05tSminO/0ApFDrIqV0ePg+f97FgWYgB+4llIB8KceWGX9qpGZ+qEXWvpL
NCS00OlYbXkHnZm+5fOmzHgzEfKeC4Nm9/Jf8RK+0tuVH1AyrnV6zgI=
-----END PRIVATE KEY-----
+6
View File
@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDr9QQo7ZaTgUL6d73G
2BG7+YRTFJHAZa0FogRglfc+jYttL1J4/xTig3RmHoqSgrehZANiAASDhijM9Xe0
G9Vam6AJMeKC6aWDNSLwrxNVmPxemsY/yJ1urBgnxRd9GFH6YW1ki/B8rS+Xl1UX
NnhBrukLaXvgAQQq782/5IUYGsvK5jw8+dSscYVMCQJwGfmQuaNeczQ=
-----END PRIVATE KEY-----
+13
View File
@@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIIB+zCCAYKgAwIBAgIIQWLAtv4ijQ0wCgYIKoZIzj0EAwMwIDEeMBwGA1UEAxMV
bWluaWNhIHJvb3QgY2EgNDE2MmMwMCAXDTI2MDQyMjIzMjUwMVoYDzIxMjYwNDIy
MjMyNTAxWjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSA0MTYyYzAwdjAQBgcq
hkjOPQIBBgUrgQQAIgNiAASDhijM9Xe0G9Vam6AJMeKC6aWDNSLwrxNVmPxemsY/
yJ1urBgnxRd9GFH6YW1ki/B8rS+Xl1UXNnhBrukLaXvgAQQq782/5IUYGsvK5jw8
+dSscYVMCQJwGfmQuaNeczSjgYYwgYMwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1Ud
DgQWBBRP+1Q8RdvnFLPW7tKSTN8aAi9U0jAfBgNVHSMEGDAWgBRP+1Q8RdvnFLPW
7tKSTN8aAi9U0jAKBggqhkjOPQQDAwNnADBkAjBfY7vb7cuLTjg7uoe+kl07FMYT
BGMSnWdhN3yXqMUS3A6XZxD/LntXT6V7yFOlAJYCMH7w8/ATYaTqbk2jBRyQt9/x
ajN+kZ6ZK+fKttqE8CD62mbHg09xoNxRq+K2I3PVyQ==
-----END CERTIFICATE-----