mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 23:16:37 +08:00
修复证书问题
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package config_version
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
oftls "github.com/Rain-kl/Wavelet/internal/apps/openflare/tls"
|
||||
"github.com/Rain-kl/Wavelet/internal/config"
|
||||
"github.com/Rain-kl/Wavelet/internal/db"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBuildCertificateSupportFilesDecryptsSealedPrivateKey(t *testing.T) {
|
||||
cleanup := setupConfigVersionTestDB(t)
|
||||
defer cleanup()
|
||||
require.NoError(t, db.DB(context.Background()).AutoMigrate(&model.TLSCertificate{}))
|
||||
|
||||
oldSecret := config.Config.App.SessionSecret
|
||||
config.Config.App.SessionSecret = "test-session-secret-for-tls-seal"
|
||||
t.Cleanup(func() { config.Config.App.SessionSecret = oldSecret })
|
||||
|
||||
ctx := context.Background()
|
||||
certPEM, keyPEM := generateTestCertKeyPairForSnapshot(t)
|
||||
certificate, err := oftls.CreateCertificate(ctx, oftls.CertificateInput{
|
||||
Name: "publish-cert",
|
||||
CertPEM: certPEM,
|
||||
KeyPEM: keyPEM,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
files, err := buildCertificateSupportFiles(ctx, []snapshotRoute{
|
||||
{CertIDs: []uint{certificate.ID}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, files, 2)
|
||||
|
||||
var keyContent string
|
||||
for _, file := range files {
|
||||
if file.Path == certificateKeyFileName(certificate.ID) {
|
||||
keyContent = file.Content
|
||||
}
|
||||
assert.NotContains(t, file.Content, "enc:v1:")
|
||||
}
|
||||
assert.Contains(t, keyContent, "BEGIN")
|
||||
assert.Equal(t, normalizePEM(strings.TrimSpace(keyPEM)), keyContent)
|
||||
}
|
||||
|
||||
func generateTestCertKeyPairForSnapshot(t *testing.T) (certPEM string, keyPEM string) {
|
||||
t.Helper()
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err)
|
||||
template := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{CommonName: "test.example.com"},
|
||||
NotBefore: time.Now().Add(-time.Hour),
|
||||
NotAfter: time.Now().Add(24 * time.Hour),
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
}
|
||||
der, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||
require.NoError(t, err)
|
||||
certPEM = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}))
|
||||
keyPEM = string(pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}))
|
||||
return certPEM, keyPEM
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/routeidentity"
|
||||
oftls "github.com/Rain-kl/Wavelet/internal/apps/openflare/tls"
|
||||
"github.com/Rain-kl/Wavelet/internal/apps/openflare/waf"
|
||||
"github.com/Rain-kl/Wavelet/internal/model"
|
||||
"github.com/Rain-kl/Wavelet/internal/repository"
|
||||
@@ -538,11 +539,19 @@ func buildOpenRestyConfigSnapshot(ctx context.Context) openRestyConfigSnapshot {
|
||||
func buildCertificateSupportFiles(ctx context.Context, routes []snapshotRoute) ([]SupportFile, error) {
|
||||
certIDSet := make(map[uint]struct{})
|
||||
for _, route := range routes {
|
||||
if route.CertID != nil && *route.CertID != 0 {
|
||||
certIDSet[*route.CertID] = struct{}{}
|
||||
}
|
||||
for _, certID := range route.CertIDs {
|
||||
if certID != 0 {
|
||||
certIDSet[certID] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, certID := range route.DomainCertIDs {
|
||||
if certID != 0 {
|
||||
certIDSet[certID] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(certIDSet) == 0 {
|
||||
return nil, nil
|
||||
@@ -558,9 +567,16 @@ func buildCertificateSupportFiles(ctx context.Context, routes []snapshotRoute) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyPEM, err := oftls.OpenKeyPEM(certificate.KeyPEM)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("certificate %d private key: %w", certificate.ID, err)
|
||||
}
|
||||
if strings.TrimSpace(keyPEM) == "" {
|
||||
return nil, fmt.Errorf("certificate %d has no private key", certificate.ID)
|
||||
}
|
||||
files = append(files,
|
||||
SupportFile{Path: certificateCertFileName(certificate.ID), Content: normalizePEM(certificate.CertPEM)},
|
||||
SupportFile{Path: certificateKeyFileName(certificate.ID), Content: normalizePEM(certificate.KeyPEM)},
|
||||
SupportFile{Path: certificateKeyFileName(certificate.ID), Content: normalizePEM(keyPEM)},
|
||||
)
|
||||
}
|
||||
return dedupeSupportFiles(files), nil
|
||||
|
||||
@@ -39,6 +39,11 @@ func sealSensitive(plaintext string) (string, error) {
|
||||
return sensitiveValuePrefix + encrypted, nil
|
||||
}
|
||||
|
||||
// OpenKeyPEM decrypts a stored certificate private key for runtime distribution.
|
||||
func OpenKeyPEM(stored string) (string, error) {
|
||||
return openSensitive(stored)
|
||||
}
|
||||
|
||||
func openSensitive(stored string) (string, error) {
|
||||
stored = strings.TrimSpace(stored)
|
||||
if stored == "" {
|
||||
|
||||
Reference in New Issue
Block a user