mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
2cbaf95eae
- Implemented TLS certificate model and service for managing certificates. - Added API endpoints for creating, importing, listing, and deleting TLS certificates. - Enhanced proxy route configuration to support HTTPS with certificate selection. - Updated frontend to include TLS certificate management UI with manual and file import options. - Added validation for HTTPS routes to ensure certificates are selected. - Implemented tests for TLS certificate creation and proxy route validation.
134 lines
3.9 KiB
Go
134 lines
3.9 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"gin-template/common"
|
|
"gin-template/model"
|
|
"math/big"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCreateTLSCertificateAndRenderHTTPSConfig(t *testing.T) {
|
|
setupServiceTestDB(t)
|
|
|
|
certPEM, keyPEM := generateCertificatePair(t, []string{"app.example.com"})
|
|
certificate, err := CreateTLSCertificate(TLSCertificateInput{
|
|
Name: "app-example",
|
|
CertPEM: certPEM,
|
|
KeyPEM: keyPEM,
|
|
Remark: "test cert",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("CreateTLSCertificate failed: %v", err)
|
|
}
|
|
if certificate.NotAfter.Before(certificate.NotBefore) {
|
|
t.Fatal("expected certificate validity period to be parsed")
|
|
}
|
|
|
|
route, err := CreateProxyRoute(ProxyRouteInput{
|
|
Domain: "app.example.com",
|
|
OriginURL: "https://origin.internal",
|
|
Enabled: true,
|
|
EnableHTTPS: true,
|
|
CertID: &certificate.ID,
|
|
RedirectHTTP: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("CreateProxyRoute failed: %v", err)
|
|
}
|
|
if !route.EnableHTTPS || route.CertID == nil {
|
|
t.Fatal("expected https fields to be persisted")
|
|
}
|
|
|
|
result, err := PublishConfigVersion("root")
|
|
if err != nil {
|
|
t.Fatalf("PublishConfigVersion failed: %v", err)
|
|
}
|
|
if !strings.Contains(result.Version.RenderedConfig, "listen 443 ssl;") {
|
|
t.Fatal("expected rendered config to include https server block")
|
|
}
|
|
if !strings.Contains(result.Version.RenderedConfig, "return 301 https://$host$request_uri;") {
|
|
t.Fatal("expected rendered config to include http redirect")
|
|
}
|
|
if !strings.Contains(result.Version.RenderedConfig, "__ATSF_CERT_DIR__/") {
|
|
t.Fatal("expected rendered config to keep certificate dir placeholder")
|
|
}
|
|
if !strings.Contains(result.Version.SupportFilesJSON, ".crt") || !strings.Contains(result.Version.SupportFilesJSON, ".key") {
|
|
t.Fatal("expected support files to contain certificate and key")
|
|
}
|
|
}
|
|
|
|
func TestCreateProxyRouteRejectsHTTPSWithoutCertificate(t *testing.T) {
|
|
setupServiceTestDB(t)
|
|
|
|
_, err := CreateProxyRoute(ProxyRouteInput{
|
|
Domain: "secure.example.com",
|
|
OriginURL: "https://origin.internal",
|
|
Enabled: true,
|
|
EnableHTTPS: true,
|
|
})
|
|
if err == nil || !strings.Contains(err.Error(), "必须选择证书") {
|
|
t.Fatalf("expected certificate validation error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCreateTLSCertificateRejectsInvalidPEM(t *testing.T) {
|
|
setupServiceTestDB(t)
|
|
|
|
_, err := CreateTLSCertificate(TLSCertificateInput{
|
|
Name: "broken-cert",
|
|
CertPEM: "invalid",
|
|
KeyPEM: "invalid",
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected invalid pem to fail")
|
|
}
|
|
}
|
|
|
|
func setupServiceTestDB(t *testing.T) {
|
|
t.Helper()
|
|
common.SQLitePath = filepath.Join(t.TempDir(), "service.db")
|
|
if err := model.InitDB(); err != nil {
|
|
t.Fatalf("failed to init db: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if err := model.CloseDB(); err != nil {
|
|
t.Fatalf("failed to close db: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func generateCertificatePair(t *testing.T, dnsNames []string) (string, string) {
|
|
t.Helper()
|
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatalf("GenerateKey failed: %v", err)
|
|
}
|
|
template := &x509.Certificate{
|
|
Subject: pkix.Name{
|
|
CommonName: dnsNames[0],
|
|
},
|
|
DNSNames: dnsNames,
|
|
NotBefore: time.Now().Add(-time.Hour),
|
|
NotAfter: time.Now().Add(24 * time.Hour),
|
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
IsCA: false,
|
|
SerialNumber: big.NewInt(time.Now().UnixNano()),
|
|
}
|
|
certDER, err := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey)
|
|
if err != nil {
|
|
t.Fatalf("CreateCertificate failed: %v", err)
|
|
}
|
|
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})
|
|
return string(certPEM), string(keyPEM)
|
|
}
|