package tlsservice import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "errors" "math/big" "net/http" "os" "path/filepath" "strings" "testing" "time" ) func testPEMPair(t *testing.T, dnsNames []string) (certPEM, keyPEM string, otherKeyPEM string) { t.Helper() key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } other, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } serial, err := rand.Int(rand.Reader, big.NewInt(1<<62)) if err != nil { t.Fatal(err) } tmpl := &x509.Certificate{ SerialNumber: serial, Subject: pkix.Name{CommonName: dnsNames[0], Organization: []string{"opsx-vpn-test"}}, NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(24 * time.Hour), KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, DNSNames: dnsNames, } der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key) if err != nil { t.Fatal(err) } certPEMBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}) keyDER, err := x509.MarshalECPrivateKey(key) if err != nil { t.Fatal(err) } otherDER, err := x509.MarshalECPrivateKey(other) if err != nil { t.Fatal(err) } return string(certPEMBytes), string(pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})), string(pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: otherDER})) } func TestInstallUploadedCertificateSuccess(t *testing.T) { t.Setenv("VPN_TLS_DOMAIN", "") t.Setenv("VPN_TLS_SCHEME", "") t.Setenv("VPN_TLS_PORT", "") t.Setenv("VPN_TLS_MODE", "") dir := t.TempDir() certPEM, keyPEM, _ := testPEMPair(t, []string{"vpn.example.com"}) if err := ConfigureMode(dir, "nginx"); err != nil { t.Fatal(err) } if err := ConfigurePublicEntry(dir, "https", "vpn.example.com", 0); err != nil { t.Fatal(err) } if err := InstallUploadedCertificate(dir, "vpn.example.com", certPEM, keyPEM); err != nil { t.Fatal(err) } fullchain, privkey := UploadedCertificatePaths(dir, "vpn.example.com") if st, err := os.Stat(fullchain); err != nil || st.Mode().Perm() != 0o600 { t.Fatalf("fullchain perms: %v %v", st, err) } if st, err := os.Stat(privkey); err != nil || st.Mode().Perm() != 0o600 { t.Fatalf("privkey perms: %v %v", st, err) } if got := readCertSource(dir); got != CertSourceUpload { t.Fatalf("cert_source=%q", got) } t.Setenv("VPN_TLS_HTTP_ADDR", "127.0.0.1:0") t.Setenv("VPN_TLS_HTTPS_ADDR", "127.0.0.1:0") if err := Start(http.NotFoundHandler(), dir); err != nil { t.Fatal(err) } status := Status() if !status.Valid || status.CertSource != CertSourceUpload || status.Domain != "vpn.example.com" { t.Fatalf("status after upload: %+v", status) } if status.Issuer == "" || status.NotAfter.IsZero() { t.Fatalf("missing issuer/expiry: %+v", status) } if status.PublicBase != "https://vpn.example.com" { t.Fatalf("public_base=%q", status.PublicBase) } fc, pk, source := CertificateFiles("vpn.example.com") if source != CertSourceUpload || fc != fullchain || pk != privkey { t.Fatalf("CertificateFiles = %q %q %q", fc, pk, source) } } func TestInstallUploadedCertificateKeyMismatch(t *testing.T) { dir := t.TempDir() certPEM, _, otherKey := testPEMPair(t, []string{"vpn.example.com"}) err := InstallUploadedCertificate(dir, "vpn.example.com", certPEM, otherKey) if err == nil { t.Fatal("expected mismatch error") } if _, statErr := os.Stat(filepath.Join(dir, "tls", "certs", "vpn.example.com", "fullchain.pem")); !os.IsNotExist(statErr) { t.Fatal("mismatch must not write certificate files") } if readCertSource(dir) != "" { t.Fatal("mismatch must not mark cert_source") } } func TestInstallUploadedCertificateDomainMismatch(t *testing.T) { dir := t.TempDir() certPEM, keyPEM, _ := testPEMPair(t, []string{"other.example.com"}) if err := InstallUploadedCertificate(dir, "vpn.example.com", certPEM, keyPEM); err == nil { t.Fatal("expected hostname mismatch") } } func TestUploadRollbackPreservesPreviousCertificate(t *testing.T) { dir := t.TempDir() firstCert, firstKey, _ := testPEMPair(t, []string{"vpn.example.com"}) secondCert, secondKey, _ := testPEMPair(t, []string{"vpn.example.com"}) if err := InstallUploadedCertificate(dir, "vpn.example.com", firstCert, firstKey); err != nil { t.Fatal(err) } fullchain, privkey := UploadedCertificatePaths(dir, "vpn.example.com") if err := InstallUploadedCertificateWithApply(dir, "vpn.example.com", secondCert, secondKey, func() error { return errors.New("nginx reload failed") }); err == nil { t.Fatal("expected reload error") } gotCert, err := os.ReadFile(fullchain) if err != nil || strings.TrimSpace(string(gotCert)) != strings.TrimSpace(firstCert) { t.Fatalf("old certificate not restored: %v", err) } gotKey, err := os.ReadFile(privkey) if err != nil || strings.TrimSpace(string(gotKey)) != strings.TrimSpace(firstKey) { t.Fatalf("old key not restored: %v", err) } if readCertSource(dir) != CertSourceUpload { t.Fatal("certificate source not restored") } }