From 17bd3ea9edadcee41984325fc36d6ce75c5cb4a5 Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Tue, 17 Mar 2026 09:33:51 +0100 Subject: [PATCH] Fix: encode IP addresses as raw octets in Subject Alternative Names (#116) - Update `GenerateCertificate` to correctly identify IP addresses and add them to `IPAddresses` instead of `DNSNames`. - Update `GetServerTLSConfig` to verify both `DNSNames` and `IPAddresses` when checking certificate validity. - Add `TestCertificateManagerIPAddress` to `certmanager_test.go` to ensure correct encoding and prevent regressions. - Ensure compliance with RFC 5280 by using binary encoding for IP addresses in certificates. --- pkg/service/certmanager/certmanager.go | 21 +++++++++- pkg/service/certmanager/certmanager_test.go | 44 +++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/pkg/service/certmanager/certmanager.go b/pkg/service/certmanager/certmanager.go index 51cb29e..5c1314d 100644 --- a/pkg/service/certmanager/certmanager.go +++ b/pkg/service/certmanager/certmanager.go @@ -9,6 +9,7 @@ import ( "crypto/x509/pkix" "encoding/pem" "math/big" + "net" "os" "path/filepath" "time" @@ -80,6 +81,10 @@ func (cm *CertificateManager) GetServerTLSConfig(domains []string) (*tls.Config, domainMap[d] = true } + for _, ip := range cert.IPAddresses { + domainMap[ip.String()] = true + } + for _, d := range domains { if !domainMap[d] { generate = true @@ -237,6 +242,19 @@ func (cm *CertificateManager) GenerateCertificate(domains []string) ([]byte, []b return nil, nil, err } + var ( + dnsNames []string + ipAddresses []net.IP + ) + + for _, domain := range domains { + if ip := net.ParseIP(domain); ip != nil { + ipAddresses = append(ipAddresses, ip) + } else { + dnsNames = append(dnsNames, domain) + } + } + template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ @@ -247,7 +265,8 @@ func (cm *CertificateManager) GenerateCertificate(domains []string) ([]byte, []b NotAfter: notAfter, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - DNSNames: domains, + DNSNames: dnsNames, + IPAddresses: ipAddresses, } derBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, &priv.PublicKey, caKey) diff --git a/pkg/service/certmanager/certmanager_test.go b/pkg/service/certmanager/certmanager_test.go index 616f2ce..b2652fc 100644 --- a/pkg/service/certmanager/certmanager_test.go +++ b/pkg/service/certmanager/certmanager_test.go @@ -126,3 +126,47 @@ func TestCertificateManager(t *testing.T) { } } } + +func TestCertificateManagerIPAddress(t *testing.T) { + tempDir, err := os.MkdirTemp("", "crypto-ip-test") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + cm := NewCertificateManager(filepath.Join(tempDir, "certs")) + + // Test certificate generation with an IP address + domains := []string{"192.168.1.100", "localhost"} + certPEM, _, err := cm.GenerateCertificate(domains) + if err != nil { + t.Fatalf("Failed to generate certificate: %v", err) + } + + // Verify generated certificate + block, _ := pem.Decode(certPEM) + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatalf("Failed to parse certificate: %v", err) + } + + // Check IP addresses + foundIP := false + for _, ip := range cert.IPAddresses { + if ip.String() == "192.168.1.100" { + foundIP = true + break + } + } + + if !foundIP { + t.Errorf("Expected IP address 192.168.1.100 in IPAddresses, but it was not found") + } + + // Check if it was mistakenly added to DNSNames + for _, dns := range cert.DNSNames { + if dns == "192.168.1.100" { + t.Errorf("IP address 192.168.1.100 should NOT be in DNSNames") + } + } +}