diff --git a/data/config.db b/data/config.db index 4538828..f4453c4 100644 --- a/data/config.db +++ b/data/config.db Binary files differ diff --git a/internal/api_handlers.go b/internal/api_handlers.go index 263ef90..1c730cb 100644 --- a/internal/api_handlers.go +++ b/internal/api_handlers.go @@ -510,6 +510,11 @@ http.Error(w, fmt.Sprintf("failed to update SDS Secret in cache: %v", err), http.StatusInternalServerError) return } + } else { + if err := api.Manager.AddSDSSecretWithCert(r.Context(), cert); err != nil { + http.Error(w, fmt.Sprintf("failed to add SDS Secret in cache: %v", err), http.StatusInternalServerError) + return + } } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(cert) diff --git a/internal/pkg/snapshot/resource_crud.go b/internal/pkg/snapshot/resource_crud.go index fa879ca..088a54c 100644 --- a/internal/pkg/snapshot/resource_crud.go +++ b/internal/pkg/snapshot/resource_crud.go @@ -4,6 +4,7 @@ "context" "fmt" "sort" + "strings" "time" corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" @@ -456,99 +457,6 @@ return resources } -// UpdateSDSSecretByDomain updates an existing Secret resource in the cache with a refreshed -// certificate and private key. -func (sm *SnapshotManager) UpdateSDSSecretByDomain(ctx context.Context, domain string, newCert *storage.CertStorage) error { - log := internallog.LogFromContext(ctx) - - // 1. Determine the Secret name. We assume a convention: "tls-secret-" - secretName := fmt.Sprintf("tls-secret-%s", domain) - secretType := resourcev3.SecretType - - // 2. Get the current Secret from the cache - resource, err := sm.GetResourceFromCache(secretName, secretType) - if err != nil { - return fmt.Errorf("failed to get Secret '%s' from cache: %w", secretName, err) - } - - secret, ok := resource.(*secretv3.Secret) - if !ok { - return fmt.Errorf("resource '%s' is not a Secret type", secretName) - } - - // 3. Validate and update the Secret data - if secret.GetType() == nil || secret.GetTlsCertificate() == nil { - return fmt.Errorf("secret '%s' is not a TlsCertificate secret or is malformed", secretName) - } - - // Update the certificate chain and private key fields - tlsCert := secret.GetTlsCertificate() - tlsCert.CertificateChain = &corev3.DataSource{ - Specifier: &corev3.DataSource_InlineBytes{ - InlineBytes: newCert.CertPEM, - }, - } - tlsCert.PrivateKey = &corev3.DataSource{ - Specifier: &corev3.DataSource_InlineBytes{ - InlineBytes: newCert.KeyPEM, - }, - } - - log.Debugf("Updated TlsCertificate data for secret '%s' (Domain: %s)", secretName, domain) - - // 4. Get current snapshot to extract all resources for the new snapshot - snap, err := sm.Cache.GetSnapshot(sm.NodeID) - if err != nil { - return fmt.Errorf("failed to get snapshot for modification: %w", err) - } - - // Get all current resources - resources := sm.getAllResourcesFromSnapshot(snap) - - // Replace the old secret with the modified one in the resource list - secretList, ok := resources[secretType] - if !ok { - return fmt.Errorf("secret resource type not present in snapshot with name %s", secretName) - } - - foundAndReplaced := false - for i, res := range secretList { - if namer, ok := res.(interface{ GetName() string }); ok && namer.GetName() == secretName { - // The `secret` variable already holds the modified secret - secretList[i] = secret - foundAndReplaced = true - break - } - } - - if !foundAndReplaced { - // Should not happen if GetResourceFromCache succeeded. - return fmt.Errorf("failed to locate Secret '%s' in current resource list for replacement", secretName) - } - - // 5. Create and set the new snapshot - version := fmt.Sprintf("secret-update-%s-%d", secretName, time.Now().UnixNano()) - newSnap, err := cachev3.NewSnapshot(version, resources) - if err != nil { - return fmt.Errorf("failed to create new snapshot: %w", err) - } - - if err := sm.Cache.SetSnapshot(ctx, sm.NodeID, newSnap); err != nil { - return fmt.Errorf("failed to set new snapshot: %w", err) - } - - // 6. Flush the updated secret to the persistent database. - // We pass DeleteNone as strategy because we only updated one resource; we don't want to affect others. - // NOTE: We assume sm.DB.SaveSecret is called elsewhere, or we call it here directly if needed for persistence. - // For now, we only flush the *snapshot* metadata if sm.FlushCacheToDB does that. - // If `FlushCacheToDB` serializes the entire snapshot back to the DB, it implicitly saves the updated secret. - sm.FlushCacheToDB(ctx, storage.DeleteLogical) - - log.Infof("Successfully updated Secret '%s' in cache with refreshed certificate for domain '%s'.", secretName, domain) - - return nil -} - // UpdateSDSSecretByName updates an existing Secret resource in the cache with a refreshed // certificate and private key, using the Secret's exact name. This is useful when the // Secret name convention is not based directly on the domain. @@ -646,6 +554,56 @@ return nil } +// AddSDSSecretWithCert creates a new Secret resource for SDS with the provided certificate +// and adds it to the snapshot. The secret name is derived from the domain: +// e.g., "abc.com" -> "abc_com". +func (sm *SnapshotManager) AddSDSSecretWithCert(ctx context.Context, internalcert *internalcertapi.Certificate) error { + log := internallog.LogFromContext(ctx) + + if internalcert == nil { + return fmt.Errorf("certificate data is nil") + } + + // 1. Determine the Secret Name (e.g., "abc.com" -> "abc_com") + // NOTE: This logic assumes the 'Domain' field of the certificate is what should be used for naming. + secretName := strings.ReplaceAll(internalcert.Domain, ".", "_") + + // 2. Create the Secret resource (envoy/config/secret/v3.Secret) + tlsCert := &secretv3.TlsCertificate{ + // The certificate content (CertPEM) is stored as an InlineString. + CertificateChain: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineString{ + InlineString: string(internalcert.CertPEM), + }, + }, + // The private key content (KeyPEM) is stored as an InlineString. + PrivateKey: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineString{ + InlineString: string(internalcert.KeyPEM), + }, + }, + } + + newSecret := &secretv3.Secret{ + Name: secretName, + Type: &secretv3.Secret_TlsCertificate{ + TlsCertificate: tlsCert, + }, + } + + // 3. Add the resource to the snapshot using the generic function + if err := sm.AddResourceToSnapshot(newSecret, resourcev3.SecretType); err != nil { + return fmt.Errorf("failed to add SDS Secret '%s' to snapshot: %w", secretName, err) + } + + // 4. Flush the updated snapshot metadata to persistent storage. + sm.FlushCacheToDB(ctx, storage.DeleteLogical) + + log.Infof("Successfully added new SDS Secret '%s' for domain '%s' to cache.", secretName, internalcert.Domain) + + return nil +} + // mapToSlice converts a map of named resources to a slice of resources. func mapToSlice(m map[string]types.Resource) []types.Resource { out := make([]types.Resource, 0, len(m)) diff --git a/static/index.html b/static/index.html index 6167ce8..222baf6 100644 --- a/static/index.html +++ b/static/index.html @@ -6,8 +6,7 @@ Envoy Configuration Dashboard - + @@ -16,8 +15,7 @@

Envoy Configuration Dashboard ⚙️

- @@ -40,14 +38,7 @@ Listener
- +

Existing Clusters (Click a row for full JSON/YAML details)

@@ -67,7 +58,14 @@
-

Existing Secrets (Click a row for full JSON/YAML details)

+
+

Existing Secrets (Click a row for full JSON/YAML details)

+ + Issue New Certificate + + +
@@ -84,7 +82,7 @@
- +

Existing Listeners (Click a domain/filter for details)

@@ -113,16 +111,14 @@
-
- +
@@ -131,8 +127,7 @@