补齐初始化外部证书预检查并增加回归用例
This commit is contained in:
parent
b48ba0beca
commit
3c1e3cefc7
@ -115,6 +115,10 @@ var legacyPluginExecutableMap = map[string]string{
|
||||
var legacyMySQLDSNPattern = regexp.MustCompile(`^([^:@/[:space:]]+):([^\r\n]*)@tcp\(([^():/[:space:]]+)(?::([0-9]{1,5}))?\)/([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:\?[^#[:space:]]*)?$`)
|
||||
|
||||
func PlanLegacyMigration(root string, _ RuntimeValidationOptions) (LegacyMigrationPlan, error) {
|
||||
return planLegacyMigration(root, false)
|
||||
}
|
||||
|
||||
func planLegacyMigration(root string, allowExternalSSL bool) (LegacyMigrationPlan, error) {
|
||||
directory, err := secureManagerConfigDirectory(root)
|
||||
if err != nil {
|
||||
return LegacyMigrationPlan{}, err
|
||||
@ -144,7 +148,7 @@ func PlanLegacyMigration(root string, _ RuntimeValidationOptions) (LegacyMigrati
|
||||
if err := json.Unmarshal(data, &legacy); err != nil {
|
||||
return LegacyMigrationPlan{}, err
|
||||
}
|
||||
plan.ManualFields = legacyManualFields(root, legacy)
|
||||
plan.ManualFields = legacyManualFields(root, legacy, allowExternalSSL)
|
||||
if len(plan.ManualFields) > 0 {
|
||||
return plan, ErrMigrationUnsafeValue
|
||||
}
|
||||
@ -155,7 +159,7 @@ func MigrateLegacyConfig(root string, options RuntimeValidationOptions) (LegacyM
|
||||
}
|
||||
|
||||
func migrateLegacyConfig(root string, options RuntimeValidationOptions, completeSetup bool) (LegacyMigrationResult, error) {
|
||||
plan, err := PlanLegacyMigration(root, options)
|
||||
plan, err := planLegacyMigration(root, completeSetup)
|
||||
if err != nil {
|
||||
return LegacyMigrationResult{}, err
|
||||
}
|
||||
@ -383,20 +387,25 @@ func equivalentStoredSecret(expected, existing StoredSecret, vault *credential.V
|
||||
return expectedErr == nil && existingErr == nil && expectedPlaintext == existingPlaintext
|
||||
}
|
||||
|
||||
func legacyManualFields(root string, legacy legacyMigrationConfig) []string {
|
||||
func legacyManualFields(root string, legacy legacyMigrationConfig, allowExternalSSL ...bool) []string {
|
||||
permitExternal := len(allowExternalSSL) > 0 && allowExternalSSL[0]
|
||||
validReference := func(value string) bool {
|
||||
_, ok := legacyControlledReference(value)
|
||||
return ok || (permitExternal && filepath.IsAbs(strings.TrimSpace(value)))
|
||||
}
|
||||
fields := make([]string, 0)
|
||||
_, _, databaseManualFields := legacyDatabasePlan(root, legacy)
|
||||
fields = append(fields, databaseManualFields...)
|
||||
if _, ok := legacyControlledReference(legacy.SSLPublicKeyPath); legacy.SSLPublicKeyPath != "" && !ok {
|
||||
if legacy.SSLPublicKeyPath != "" && !validReference(legacy.SSLPublicKeyPath) {
|
||||
fields = append(fields, "SSLPublicKeyPath")
|
||||
}
|
||||
if _, ok := legacyControlledReference(legacy.SSLPrivateKeyPath); legacy.SSLPrivateKeyPath != "" && !ok {
|
||||
if legacy.SSLPrivateKeyPath != "" && !validReference(legacy.SSLPrivateKeyPath) {
|
||||
fields = append(fields, "SSLPrivateKeyPath")
|
||||
}
|
||||
if _, ok := legacyControlledReference(legacy.SSL.CertificateRef); legacy.SSL.CertificateRef != "" && !ok {
|
||||
if legacy.SSL.CertificateRef != "" && !validReference(legacy.SSL.CertificateRef) {
|
||||
fields = append(fields, "ssl.certificateRef")
|
||||
}
|
||||
if _, ok := legacyControlledReference(legacy.SSL.PrivateKeyRef); legacy.SSL.PrivateKeyRef != "" && !ok {
|
||||
if legacy.SSL.PrivateKeyRef != "" && !validReference(legacy.SSL.PrivateKeyRef) {
|
||||
fields = append(fields, "ssl.privateKeyRef")
|
||||
}
|
||||
if legacy.SMTPUsername == "" && legacy.SMTPPassword != "" {
|
||||
|
||||
@ -738,6 +738,60 @@ func TestMigrateLegacyConfigDerivesSetupStateFromFullPreflight(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupMigrationAcceptsExternalSSLOnlyDuringFinalization(t *testing.T) {
|
||||
for _, completing := range []bool{false, true} {
|
||||
root := t.TempDir()
|
||||
external := t.TempDir()
|
||||
certificate := filepath.ToSlash(filepath.Join(external, "public.crt"))
|
||||
privateKey := filepath.ToSlash(filepath.Join(external, "private.key"))
|
||||
legacy := map[string]any{
|
||||
"domain": "example.com", "webDomain": "web.example.com",
|
||||
"dbDSN": "./data/smail.db", "dbType": "sqlite",
|
||||
"isInit": false, "setupState": "bootstrap",
|
||||
"ssl": map[string]any{
|
||||
"management": SSLManagementWebServer,
|
||||
"mode": ManagedSSLModeUpload, "defaultDirectory": "ssl",
|
||||
"customDirectory": "ssl", "certificateRef": certificate,
|
||||
"privateKeyRef": privateKey,
|
||||
},
|
||||
}
|
||||
writeLegacyMigrationFixture(t, root, legacy)
|
||||
options := RuntimeValidationOptions{HasSuperAdmin: true, Production: true}
|
||||
if _, err := PlanLegacyMigration(root, options); !errors.Is(err, ErrMigrationUnsafeValue) {
|
||||
t.Fatalf("ordinary plan must reject external references: %v", err)
|
||||
}
|
||||
_, err := migrateLegacyConfig(root, options, completing)
|
||||
if !completing {
|
||||
if !errors.Is(err, ErrMigrationUnsafeValue) {
|
||||
t.Fatalf("ordinary migration must reject external references: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runtime, err := NewConfigLoader(root, ConfigLoaderOptions{Validation: options}).Load()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if runtime.SetupState != SetupStateReady || runtime.SSL.TLS.CertificateRef != certificate || runtime.SSL.TLS.PrivateKeyRef != privateKey {
|
||||
t.Fatalf("setup did not preserve external SSL references: %+v", runtime.SSL.TLS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupMigrationStillRejectsUnsafeRelativeSSL(t *testing.T) {
|
||||
legacy := legacyMigrationConfig{DbType: "sqlite", DbDSN: "./data/smail.db"}
|
||||
legacy.SSL.CertificateRef = "../outside/public.crt"
|
||||
legacy.SSL.PrivateKeyRef = "../outside/private.key"
|
||||
if fields := legacyManualFields(t.TempDir(), legacy, true); len(fields) == 0 {
|
||||
t.Fatal("setup preflight accepted unsafe relative SSL paths")
|
||||
}
|
||||
if _, fields := legacySSLConfig(legacy, true); len(fields) == 0 {
|
||||
t.Fatal("setup conversion accepted unsafe relative SSL paths")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateLegacyConfigAcceptsStructuredSSLObject(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
legacy := map[string]any{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user