保存证书同步云端权限复测通过基线
Some checks are pending
Security Checks / security (push) Waiting to run

This commit is contained in:
smail 2026-09-20 04:49:03 +08:00
parent 747f79a47a
commit 951fdc00c4
8 changed files with 258 additions and 78 deletions

View File

@ -104,15 +104,15 @@ type EmailConfig struct {
}
type SSLConfig struct {
Management string `json:"management,omitempty"`
Mode string `json:"mode"`
DefaultDirectory string `json:"defaultDirectory"`
CustomDirectory string `json:"customDirectory,omitempty"`
Challenge string `json:"challenge,omitempty"`
CertificateRef string `json:"certificateRef,omitempty"`
PrivateKeyRef string `json:"privateKeyRef,omitempty"`
PreviousCertificateRef string `json:"previousCertificateRef,omitempty"`
PreviousPrivateKeyRef string `json:"previousPrivateKeyRef,omitempty"`
Management string `json:"management,omitempty"`
Mode string `json:"mode"`
DefaultDirectory string `json:"defaultDirectory"`
CustomDirectory string `json:"customDirectory,omitempty"`
Challenge string `json:"challenge,omitempty"`
CertificateRef string `json:"certificateRef,omitempty"`
PrivateKeyRef string `json:"privateKeyRef,omitempty"`
PreviousCertificateRef string `json:"previousCertificateRef,omitempty"`
PreviousPrivateKeyRef string `json:"previousPrivateKeyRef,omitempty"`
DNSProvider string `json:"dnsProvider,omitempty"`
DNSZone string `json:"dnsZone,omitempty"`
DNSAccessKey StoredSecret `json:"dnsAccessKey,omitempty"`

View File

@ -15,7 +15,9 @@ func TestDNSCredentialsEncryptedPreservedAndRedacted(t *testing.T) {
ROOT_PATH = root
t.Cleanup(func() { ROOT_PATH = previousRoot })
vault, err := credential.NewVault(root)
if err != nil { t.Fatal(err) }
if err != nil {
t.Fatal(err)
}
before := validRuntimeConfig()
candidate := cloneRuntimeConfig(before)
candidate.SSL.TLS.DNSProvider = DNSProviderAliDNS
@ -23,29 +25,51 @@ func TestDNSCredentialsEncryptedPreservedAndRedacted(t *testing.T) {
"tls.dnsAccessKey": {Operation: SecretReplace, Value: &SecretInput{Plaintext: "dns-access-fixture"}},
"tls.dnsSecretKey": {Operation: SecretReplace, Value: &SecretInput{Plaintext: "dns-secret-fixture"}},
}
if err := applySecretChanges(&candidate, SectionSSL, changes, vault); err != nil { t.Fatal(err) }
if err := validateDNSProvider(candidate.SSL.TLS); err != nil { t.Fatal(err) }
if err := ValidateRuntimeSecrets(vault, candidate); err != nil { t.Fatal(err) }
if err := applySecretChanges(&candidate, SectionSSL, changes, vault); err != nil {
t.Fatal(err)
}
if err := validateDNSProvider(candidate.SSL.TLS); err != nil {
t.Fatal(err)
}
if err := ValidateRuntimeSecrets(vault, candidate); err != nil {
t.Fatal(err)
}
access, secret, err := DNSProviderCredentials(candidate.SSL.TLS)
if err != nil || access != "dns-access-fixture" || secret != "dns-secret-fixture" { t.Fatal("DNS credentials did not round trip through vault") }
if err != nil || access != "dns-access-fixture" || secret != "dns-secret-fixture" {
t.Fatal("DNS credentials did not round trip through vault")
}
data, _ := json.Marshal(candidate)
for _, value := range []string{access, secret} { if strings.Contains(string(data), value) { t.Fatal("plaintext persisted") } }
for _, value := range []string{access, secret} {
if strings.Contains(string(data), value) {
t.Fatal("plaintext persisted")
}
}
audit, _ := json.Marshal(AuditChanges(before, candidate))
public, _ := json.Marshal(RedactPublicValue(candidate.SSL))
for _, data := range [][]byte{audit, public} {
for _, value := range []string{access, secret, candidate.SSL.TLS.DNSAccessKey.Ciphertext, candidate.SSL.TLS.DNSSecretKey.Ciphertext} {
if strings.Contains(string(data), value) { t.Fatal("DNS secret leaked into audit or public view") }
if strings.Contains(string(data), value) {
t.Fatal("DNS secret leaked into audit or public view")
}
}
}
section := candidate.SSL
section.TLS.DNSAccessKey, section.TLS.DNSSecretKey = StoredSecret{}, StoredSecret{}
payload, _ := json.Marshal(section)
kept, err := applySectionPayload(candidate, SectionSSL, payload, nil, vault)
if err != nil || kept.SSL.TLS.DNSAccessKey != candidate.SSL.TLS.DNSAccessKey || kept.SSL.TLS.DNSSecretKey != candidate.SSL.TLS.DNSSecretKey { t.Fatal("public edit lost stored DNS credentials") }
if err != nil || kept.SSL.TLS.DNSAccessKey != candidate.SSL.TLS.DNSAccessKey || kept.SSL.TLS.DNSSecretKey != candidate.SSL.TLS.DNSSecretKey {
t.Fatal("public edit lost stored DNS credentials")
}
payload, _ = json.Marshal(candidate.SSL)
if _, err := applySectionPayload(candidate, SectionSSL, payload, nil, vault); !errors.Is(err, ErrInvalidSecretChange) { t.Fatal("direct stored-secret injection accepted") }
if _, err := applySectionPayload(candidate, SectionSSL, payload, nil, vault); !errors.Is(err, ErrInvalidSecretChange) {
t.Fatal("direct stored-secret injection accepted")
}
clear := map[string]SecretChange{"tls.dnsSecretKey": {Operation: SecretClear, Confirmed: true}}
if err := applySecretChanges(&candidate, SectionSSL, clear, vault); !errors.Is(err, ErrSecretClearDenied) { t.Fatal("enabled DNS provider allowed clearing its secret") }
if err := applySecretChanges(&candidate, SectionSSL, clear, vault); !errors.Is(err, ErrSecretClearDenied) {
t.Fatal("enabled DNS provider allowed clearing its secret")
}
candidate.SSL.TLS.DNSProvider = ""
if err := applySecretChanges(&candidate, SectionSSL, clear, vault); err != nil { t.Fatal(err) }
if err := applySecretChanges(&candidate, SectionSSL, clear, vault); err != nil {
t.Fatal(err)
}
}

View File

@ -2,9 +2,11 @@ package config
import (
"bytes"
"errors"
"io"
"os"
"path/filepath"
"runtime"
"git.pcux.cn/xuans/Smail/utils/atomicfile"
)
@ -12,6 +14,13 @@ import (
// ExportProjectTLS 为本机 TLS 入口导出一个原子更新的证书私钥合并文件。
// 仅接受项目生成的平铺证书;不允许高权限调用者按配置读取任意服务器路径。
func ExportProjectTLS(root, destination string) error {
if runtime.GOOS != "linux" {
return errors.New("本机 Nginx 证书同步仅支持 Linux")
}
if !filepath.IsAbs(root) || !filepath.IsAbs(destination) {
return ErrUnsafeSSLFile
}
root, destination = filepath.Clean(root), filepath.Clean(destination)
runtime, err := NewConfigLoader(root, ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}}).Load()
if err != nil {
return ErrInvalidSSL
@ -26,43 +35,79 @@ func ExportProjectTLS(root, destination string) error {
}
sourceDirectory := filepath.Join(root, "ssl")
resolvedSource, err := filepath.EvalSymlinks(sourceDirectory)
if err != nil || resolvedSource != sourceDirectory { return ErrUnsafeSSLFile }
if err != nil || resolvedSource != sourceDirectory {
return ErrUnsafeSSLFile
}
storage, err := os.OpenRoot(sourceDirectory)
if err != nil { return ErrUnsafeSSLFile }
if err != nil {
return ErrUnsafeSSLFile
}
defer storage.Close()
read := func(name string) ([]byte, error) {
info, err := storage.Lstat(name)
if err != nil || !info.Mode().IsRegular() || info.Size() > maxManagedSSLFileSize || info.Mode().Perm() & 0077 != 0 { return nil, ErrUnsafeSSLFile }
if err != nil || !info.Mode().IsRegular() || info.Size() > maxManagedSSLFileSize || info.Mode().Perm()&0077 != 0 {
return nil, ErrUnsafeSSLFile
}
file, err := storage.Open(name)
if err != nil { return nil, ErrUnsafeSSLFile }
if err != nil {
return nil, ErrUnsafeSSLFile
}
defer file.Close()
opened, err := file.Stat()
if err != nil || !os.SameFile(info, opened) { return nil, ErrUnsafeSSLFile }
if err != nil || !os.SameFile(info, opened) || !opened.Mode().IsRegular() || opened.Mode().Perm()&0077 != 0 {
return nil, ErrUnsafeSSLFile
}
data, err := io.ReadAll(io.LimitReader(file, maxManagedSSLFileSize+1))
if err != nil || len(data) > maxManagedSSLFileSize { return nil, ErrUnsafeSSLFile }
if err != nil || len(data) > maxManagedSSLFileSize {
return nil, ErrUnsafeSSLFile
}
after, err := file.Stat()
if err != nil || !os.SameFile(opened, after) || after.Size() != int64(len(data)) || !opened.ModTime().Equal(after.ModTime()) || after.Mode().Perm()&0077 != 0 {
return nil, ErrUnsafeSSLFile
}
return data, nil
}
certificate, err := read(filepath.Base(publicPath))
if err != nil { return err }
if err != nil {
return err
}
privateKey, err := read(filepath.Base(privatePath))
if err != nil { return err }
if err := validateSSLMaterial(privateKey, certificate, nil, validationCertificateDomains(&runtime)); err != nil { return err }
if err != nil {
return err
}
if err := validateSSLMaterial(privateKey, certificate, nil, validationCertificateDomains(&runtime)); err != nil {
return err
}
combined := append(append(append([]byte{}, certificate...), '\n'), privateKey...)
// 输出目录由部署管理员创建,拒绝符号链接和其他用户可写目录。
directory := filepath.Dir(destination)
resolved, err := filepath.EvalSymlinks(directory)
if err != nil || resolved != directory { return ErrUnsafeSSLFile }
if err != nil || resolved != directory {
return ErrUnsafeSSLFile
}
info, err := os.Stat(directory)
if err != nil || !info.IsDir() || info.Mode().Perm() & 0022 != 0 { return ErrUnsafeSSLFile }
if err != nil || !info.IsDir() || info.Mode().Perm()&0022 != 0 {
return ErrUnsafeSSLFile
}
output, err := os.OpenRoot(directory)
if err != nil { return ErrUnsafeSSLFile }
if err != nil {
return ErrUnsafeSSLFile
}
defer output.Close()
name := filepath.Base(destination)
if info, err := output.Lstat(name); err == nil {
if !info.Mode().IsRegular() || info.Mode().Perm() & 0077 != 0 { return ErrUnsafeSSLFile }
if !info.Mode().IsRegular() || info.Mode().Perm()&0077 != 0 {
return ErrUnsafeSSLFile
}
previous, err := output.ReadFile(name)
if err != nil { return ErrUnsafeSSLFile }
if bytes.Equal(previous, combined) { return nil }
} else if !os.IsNotExist(err) { return ErrUnsafeSSLFile }
if err != nil {
return ErrUnsafeSSLFile
}
if bytes.Equal(previous, combined) {
return nil
}
} else if !os.IsNotExist(err) {
return ErrUnsafeSSLFile
}
return atomicfile.WriteRoot(output, name, combined, 0600)
}

View File

@ -5,41 +5,107 @@ import (
"crypto/tls"
"os"
"path/filepath"
"runtime"
"testing"
"time"
)
func TestExportProjectTLSKeepsOldBundleOnInvalidInput(t *testing.T) {
if runtime.GOOS != "linux" {
if err := ExportProjectTLS(t.TempDir(), filepath.Join(t.TempDir(), "tls.pem")); err == nil {
t.Fatal("unsupported platform allowed exporting a private key")
}
t.Skip("Linux Nginx export requires POSIX permission checks")
}
root := t.TempDir()
outputDirectory := t.TempDir()
if err := os.Chmod(outputDirectory, 0700); err != nil { t.Fatal(err) }
if err := os.Chmod(outputDirectory, 0700); err != nil {
t.Fatal(err)
}
destination := filepath.Join(outputDirectory, "tls.pem")
runtime := validRuntimeConfig()
runtime.Root.Domains = DomainConfig{Primary: "example.com", Web: "web.example.com", Accepted: []string{"example.com"}}
key, certificate := testManagedCertificate(t, "example.com", time.Now().Add(-time.Hour), time.Now().Add(time.Hour), 2048, "*.example.com")
ref, err := stageFlatSSLAtRoot(root, ManagedSSLModeDefault, key, certificate, nil, nil)
if err != nil { t.Fatal(err) }
if err != nil {
t.Fatal(err)
}
defer releaseManagedSSL(root, SSLConfig{CertificateRef: ref.CertificateRef})
runtime.SSL.TLS = SSLConfig{Management: SSLManagementProject, Challenge: SSLChallengeDNS, Mode: ManagedSSLModeDefault, DefaultDirectory: "ssl", CertificateRef: ref.CertificateRef, PrivateKeyRef: ref.PrivateKeyRef}
writeRuntimeFixture(t, root, runtime)
if err := ExportProjectTLS(root, destination); err != nil { t.Fatal(err) }
if err := ExportProjectTLS(root, destination); err != nil {
t.Fatal(err)
}
bundle, err := os.ReadFile(destination)
if err != nil { t.Fatal(err) }
if _, err := tls.X509KeyPair(bundle, bundle); err != nil { t.Fatal(err) }
if err := ExportProjectTLS(root, destination); err != nil { t.Fatal(err) }
if err := os.WriteFile(ref.PrivateKeyPath, []byte("invalid"), 0600); err != nil { t.Fatal(err) }
if err := ExportProjectTLS(root, destination); err == nil { t.Fatal("invalid key was exported") }
if err != nil {
t.Fatal(err)
}
if _, err := tls.X509KeyPair(bundle, bundle); err != nil {
t.Fatal(err)
}
before, err := os.Stat(destination)
if err != nil || before.Mode().Perm() != 0600 {
t.Fatal("export must be private")
}
if err := ExportProjectTLS(root, destination); err != nil {
t.Fatal(err)
}
after, _ := os.Stat(destination)
if !os.SameFile(before, after) {
t.Fatal("unchanged certificate must not be rewritten")
}
for _, target := range []string{ref.PrivateKeyPath, ref.CertificatePath, destination} {
if err := os.Chmod(target, 0644); err != nil {
t.Fatal(err)
}
if err := ExportProjectTLS(root, destination); err == nil {
t.Fatal("unsafe file permissions accepted")
}
if err := os.Chmod(target, 0600); err != nil {
t.Fatal(err)
}
}
if err := os.Chmod(outputDirectory, 0777); err != nil {
t.Fatal(err)
}
if err := ExportProjectTLS(root, destination); err == nil {
t.Fatal("writable output directory accepted")
}
if err := os.Chmod(outputDirectory, 0700); err != nil {
t.Fatal(err)
}
link := filepath.Join(t.TempDir(), "linked")
if err := os.Symlink(outputDirectory, link); err != nil {
t.Fatal(err)
}
if err := ExportProjectTLS(root, filepath.Join(link, "tls.pem")); err == nil {
t.Fatal("linked output directory accepted")
}
if err := os.WriteFile(ref.PrivateKeyPath, []byte("invalid"), 0600); err != nil {
t.Fatal(err)
}
if err := ExportProjectTLS(root, destination); err == nil {
t.Fatal("invalid key was exported")
}
preserved, _ := os.ReadFile(destination)
if !bytes.Equal(bundle, preserved) { t.Fatal("failed export replaced valid bundle") }
if !bytes.Equal(bundle, preserved) {
t.Fatal("failed export replaced valid bundle")
}
runtime.SSL.TLS.CertificateRef = "ssl/unmanaged.crt"
runtime.SSL.TLS.PrivateKeyRef = "ssl/unmanaged.key"
writeRuntimeFixture(t, root, runtime)
if err := ExportProjectTLS(root, destination); err == nil { t.Fatal("unmanaged input accepted") }
if err := ExportProjectTLS(root, destination); err == nil {
t.Fatal("unmanaged input accepted")
}
for _, mode := range []string{SSLManagementCustom, SSLManagementWebServer} {
runtime.SSL.TLS.Management = mode
writeRuntimeFixture(t, root, runtime)
if err := ExportProjectTLS(root, destination); err != nil { t.Fatal(err) }
if err := ExportProjectTLS(root, destination); err != nil {
t.Fatal(err)
}
preserved, _ := os.ReadFile(destination)
if !bytes.Equal(bundle, preserved) { t.Fatal("external mode changed exported bundle") }
if !bytes.Equal(bundle, preserved) {
t.Fatal("external mode changed exported bundle")
}
}
}

View File

@ -42,7 +42,7 @@ func TestPublicConfigSectionsDoNotExposeSecretsAndRemainStrictlyDecodable(t *tes
SSL: config.SSLSectionConfig{
Meta: config.SectionMeta{Schema: config.CurrentSectionSchema, Generation: 7},
TLS: config.SSLConfig{
DNSProvider: config.DNSProviderAliDNS,
DNSProvider: config.DNSProviderAliDNS,
DNSAccessKey: config.StoredSecret{Ciphertext: "enc:v1:dns-access", Version: credential.CurrentVersion, UpdatedAt: time.Now().UTC()},
DNSSecretKey: config.StoredSecret{Ciphertext: "enc:v1:dns-secret", Version: credential.CurrentVersion, UpdatedAt: time.Now().UTC()},
},

View File

@ -94,7 +94,9 @@ func (provider *aliDNSChallenge) Present(domain, token, keyAuth string) error {
if err != nil {
return err
}
var response struct{ RecordID string `json:"RecordId"` }
var response struct {
RecordID string `json:"RecordId"`
}
err = provider.call("AddDomainRecord", url.Values{
"DomainName": {provider.zone}, "RR": {record.Name}, "Type": {"TXT"},
"Value": {record.Value}, "TTL": {"600"},
@ -174,7 +176,9 @@ func (provider *aliDNSChallenge) call(action string, values url.Values, target a
if err != nil || len(data) > 1<<20 {
return errors.New("DNS 服务商响应无效")
}
var status struct{ Code string `json:"Code"` }
var status struct {
Code string `json:"Code"`
}
if json.Unmarshal(data, &status) != nil || response.StatusCode != http.StatusOK || status.Code != "" {
// 服务商错误可能包含凭据、签名或请求原文,不能直接交给日志。
return errors.New("DNS 服务商拒绝请求,请检查凭据、区域和记录管理权限")

View File

@ -35,10 +35,16 @@ func TestAutomaticDNSCleansOnlyOwnedUnchangedRecords(t *testing.T) {
_ = json.NewEncoder(w).Encode(map[string]string{"RecordId": id})
case "DescribeDomainRecordInfo":
record := records[id]
if tamper { record.Value = "changed-by-user" }
if tamper {
record.Value = "changed-by-user"
}
_ = json.NewEncoder(w).Encode(map[string]string{"RecordId": id, "DomainName": "example.com", "RR": record.Name, "Type": "TXT", "Value": record.Value})
case "DeleteDomainRecord":
if failDelete { w.WriteHeader(503); _, _ = w.Write([]byte(`{"Code":"Busy"}`)); return }
if failDelete {
w.WriteHeader(503)
_, _ = w.Write([]byte(`{"Code":"Busy"}`))
return
}
deletes++
delete(records, id)
_ = json.NewEncoder(w).Encode(map[string]string{"RecordId": id})
@ -55,23 +61,50 @@ func TestAutomaticDNSCleansOnlyOwnedUnchangedRecords(t *testing.T) {
workers.Add(1)
go func(domain string) {
defer workers.Done()
if err := provider.Present(domain, "token", "key-"+domain); err != nil { t.Error(err) }
if err := provider.Present(domain, "token", "key-"+domain); err != nil {
t.Error(err)
}
}(domain)
}
}
workers.Wait()
if adds != 2 || len(records) != 3 { t.Fatalf("same-name TXT records lost or duplicated: adds=%d records=%d", adds, len(records)) }
if err := provider.CleanUp("other.example.com", "token", "unknown"); err != nil || deletes != 0 { t.Fatal("unknown challenge must not touch DNS") }
mu.Lock(); tamper = true; mu.Unlock()
if err := provider.CleanUp("example.com", "token", "key-example.com"); err == nil || deletes != 0 { t.Fatal("modified record must be retained") }
mu.Lock(); tamper = false; failDelete = true; mu.Unlock()
if err := provider.CleanUp("example.com", "token", "key-example.com"); err == nil || len(provider.records) != 2 { t.Fatal("failed deletion must retain ownership for retry") }
mu.Lock(); failDelete = false; mu.Unlock()
if err := provider.CleanUp("example.com", "token", "key-example.com"); err != nil { t.Fatal(err) }
if len(records) != 2 { t.Fatal("cleaning root challenge removed wildcard or unrelated TXT") }
if err := provider.CleanUp("*.example.com", "token", "key-*.example.com"); err != nil { t.Fatal(err) }
if len(records) != 1 || records["existing"].Value != "unrelated-record" { t.Fatal("existing TXT must survive all challenge cleanup") }
if err := provider.CleanUp("*.example.com", "token", "key-*.example.com"); err != nil || deletes != 2 { t.Fatal("cleanup must be idempotent") }
if adds != 2 || len(records) != 3 {
t.Fatalf("same-name TXT records lost or duplicated: adds=%d records=%d", adds, len(records))
}
if err := provider.CleanUp("other.example.com", "token", "unknown"); err != nil || deletes != 0 {
t.Fatal("unknown challenge must not touch DNS")
}
mu.Lock()
tamper = true
mu.Unlock()
if err := provider.CleanUp("example.com", "token", "key-example.com"); err == nil || deletes != 0 {
t.Fatal("modified record must be retained")
}
mu.Lock()
tamper = false
failDelete = true
mu.Unlock()
if err := provider.CleanUp("example.com", "token", "key-example.com"); err == nil || len(provider.records) != 2 {
t.Fatal("failed deletion must retain ownership for retry")
}
mu.Lock()
failDelete = false
mu.Unlock()
if err := provider.CleanUp("example.com", "token", "key-example.com"); err != nil {
t.Fatal(err)
}
if len(records) != 2 {
t.Fatal("cleaning root challenge removed wildcard or unrelated TXT")
}
if err := provider.CleanUp("*.example.com", "token", "key-*.example.com"); err != nil {
t.Fatal(err)
}
if len(records) != 1 || records["existing"].Value != "unrelated-record" {
t.Fatal("existing TXT must survive all challenge cleanup")
}
if err := provider.CleanUp("*.example.com", "token", "key-*.example.com"); err != nil || deletes != 2 {
t.Fatal("cleanup must be idempotent")
}
}
func TestAutomaticDNSRejectsOutsideZoneAndRedactsErrors(t *testing.T) {
@ -84,13 +117,21 @@ func TestAutomaticDNSRejectsOutsideZoneAndRedactsErrors(t *testing.T) {
}))
defer server.Close()
provider := &aliDNSChallenge{zone: "example.com", accessKey: "test-access", secretKey: "test-secret", endpoint: server.URL, client: server.Client(), records: map[string]aliDNSRecord{}}
if err := provider.Present("unrelated.com", "token", "key"); err == nil || calls != 0 { t.Fatal("out-of-zone write attempted") }
if err := provider.Present("unrelated.com", "token", "key"); err == nil || calls != 0 {
t.Fatal("out-of-zone write attempted")
}
err := provider.Present("example.com", "token", "key")
if err == nil || calls != 1 { t.Fatal("provider refusal not reported") }
if err == nil || calls != 1 {
t.Fatal("provider refusal not reported")
}
for _, secret := range []string{"test-access", "test-secret", "signature"} {
if strings.Contains(err.Error(), secret) { t.Fatal("error leaked provider response") }
if strings.Contains(err.Error(), secret) {
t.Fatal("error leaked provider response")
}
}
for _, management := range []string{config.SSLManagementCustom, config.SSLManagementWebServer} {
if _, err := automaticDNSProvider(&config.Config{SSL: config.SSLConfig{Management: management, Challenge: config.SSLChallengeDNS}}); err == nil { t.Fatal("external certificate mode allowed automatic DNS") }
if _, err := automaticDNSProvider(&config.Config{SSL: config.SSLConfig{Management: management, Challenge: config.SSLChallengeDNS}}); err == nil {
t.Fatal("external certificate mode allowed automatic DNS")
}
}
}

View File

@ -118,10 +118,10 @@ func SetSSL(sslType, priKey, crtKey string) error {
Management: config.SSLManagementProject,
Mode: config.ManagedSSLModeDefault,
DefaultDirectory: runtimepath.SSLReference(config.ROOT_PATH, "data/ssl"),
DNSProvider: previousSSL.DNSProvider,
DNSZone: previousSSL.DNSZone,
DNSAccessKey: previousSSL.DNSAccessKey,
DNSSecretKey: previousSSL.DNSSecretKey,
DNSProvider: previousSSL.DNSProvider,
DNSZone: previousSSL.DNSZone,
DNSAccessKey: previousSSL.DNSAccessKey,
DNSSecretKey: previousSSL.DNSSecretKey,
}
if cfg.SSLType == config.SSLTypeAutoDNS {
cfg.SSL.Challenge = config.SSLChallengeDNS
@ -192,10 +192,10 @@ func applyManagedSSLReference(cfg *config.Config, reference config.ManagedSSLRef
DefaultDirectory: "ssl",
CertificateRef: reference.CertificateRef,
PrivateKeyRef: reference.PrivateKeyRef,
DNSProvider: previousSSL.DNSProvider,
DNSZone: previousSSL.DNSZone,
DNSAccessKey: previousSSL.DNSAccessKey,
DNSSecretKey: previousSSL.DNSSecretKey,
DNSProvider: previousSSL.DNSProvider,
DNSZone: previousSSL.DNSZone,
DNSAccessKey: previousSSL.DNSAccessKey,
DNSSecretKey: previousSSL.DNSSecretKey,
}
if reference.Mode == config.ManagedSSLModeUpload {
cfg.SSL.CustomDirectory = "ssl"