diff --git a/cli/policies.go b/cli/policies.go index 25de3a99d..14de0d7ce 100644 --- a/cli/policies.go +++ b/cli/policies.go @@ -3,12 +3,13 @@ package main import ( "fmt" "os" + "slices" "github.com/edgelesssys/nunki/internal/kubeapi" "github.com/edgelesssys/nunki/internal/manifest" ) -func policiesFromKubeResources(yamlPaths []string) (map[string]manifest.Policy, error) { +func policiesFromKubeResources(yamlPaths []string) (map[string]deployment, error) { var kubeObjs []any for _, path := range yamlPaths { data, err := os.ReadFile(path) @@ -22,24 +23,29 @@ func policiesFromKubeResources(yamlPaths []string) (map[string]manifest.Policy, kubeObjs = append(kubeObjs, objs...) } - policies := make(map[string]manifest.Policy) + deployments := make(map[string]deployment) for _, objAny := range kubeObjs { - var name, annotation string + var name, annotation, namespace string switch obj := objAny.(type) { case kubeapi.Pod: name = obj.Name + namespace = obj.Namespace annotation = obj.Annotations[kataPolicyAnnotationKey] case kubeapi.Deployment: name = obj.Name + namespace = obj.Namespace annotation = obj.Spec.Template.Annotations[kataPolicyAnnotationKey] case kubeapi.ReplicaSet: name = obj.Name + namespace = obj.Namespace annotation = obj.Spec.Template.Annotations[kataPolicyAnnotationKey] case kubeapi.StatefulSet: name = obj.Name + namespace = obj.Namespace annotation = obj.Spec.Template.Annotations[kataPolicyAnnotationKey] case kubeapi.DaemonSet: name = obj.Name + namespace = obj.Namespace annotation = obj.Spec.Template.Annotations[kataPolicyAnnotationKey] } if annotation == "" { @@ -48,46 +54,67 @@ func policiesFromKubeResources(yamlPaths []string) (map[string]manifest.Policy, if name == "" { return nil, fmt.Errorf("name is required but empty") } + if namespace == "" { + namespace = "default" + } policy, err := manifest.NewPolicyFromAnnotation([]byte(annotation)) if err != nil { return nil, fmt.Errorf("failed to parse policy %s: %w", name, err) } - policies[name] = policy + deployments[name] = deployment{ + name: name, + namespace: namespace, + policy: policy, + } } - return policies, nil + return deployments, nil } -func manifestPolicyMapFromPolicies(policies map[string]manifest.Policy) (map[manifest.HexString]string, error) { - policyHashes := make(map[manifest.HexString]string) - for name, policy := range policies { - if existingName, ok := policyHashes[policy.Hash()]; ok { - if existingName != name { - return nil, fmt.Errorf("policy hash collision: %s and %s have the same hash %s", - existingName, name, policy.Hash()) +func manifestPolicyMapFromPolicies(policies map[string]deployment) (map[manifest.HexString][]string, error) { + policyHashes := make(map[manifest.HexString][]string) + for name, depl := range policies { + if existingNames, ok := policyHashes[depl.policy.Hash()]; ok { + if slices.Equal(existingNames, depl.DNSNames()) { + return nil, fmt.Errorf("policy hash collision: %s and %s have the same hash %v", + existingNames, name, depl.policy.Hash()) } continue } - policyHashes[policy.Hash()] = name + policyHashes[depl.policy.Hash()] = depl.DNSNames() } return policyHashes, nil } -func checkPoliciesMatchManifest(policies map[string]manifest.Policy, policyHashes map[manifest.HexString]string) error { +func checkPoliciesMatchManifest(policies map[string]deployment, policyHashes map[manifest.HexString][]string) error { if len(policies) != len(policyHashes) { return fmt.Errorf("policy count mismatch: %d policies in deployment, but %d in manifest", len(policies), len(policyHashes)) } - for name, policy := range policies { - existingName, ok := policyHashes[policy.Hash()] + for name, deployment := range policies { + existingNames, ok := policyHashes[deployment.policy.Hash()] if !ok { return fmt.Errorf("policy %s not found in manifest", name) } - if existingName != name { - return fmt.Errorf("policy %s with hash %s exists in manifest, but with different name %s", - name, policy.Hash(), existingName, + + if !slices.Equal(existingNames, deployment.DNSNames()) { + return fmt.Errorf("policy %s with hash %s exists in manifest, but with different names %v", + name, deployment.policy.Hash(), existingNames, ) } } return nil } + +type deployment struct { + name string + namespace string + policy manifest.Policy +} + +func (d deployment) DNSNames() []string { + return []string{ + fmt.Sprintf("%s.%s", d.name, d.namespace), + fmt.Sprintf("*.%s", d.namespace), + } +} diff --git a/cli/set.go b/cli/set.go index 9f3da970f..fd22d3e32 100644 --- a/cli/set.go +++ b/cli/set.go @@ -143,10 +143,10 @@ func parseSetFlags(cmd *cobra.Command) (*setFlags, error) { return flags, nil } -func policyMapToBytesList(m map[string]manifest.Policy) [][]byte { +func policyMapToBytesList(m map[string]deployment) [][]byte { var policies [][]byte - for _, policy := range m { - policies = append(policies, policy) + for _, depl := range m { + policies = append(policies, depl.policy) } return policies } diff --git a/coordinator/mesh.go b/coordinator/mesh.go index 23d88fbc8..779a23d0b 100644 --- a/coordinator/mesh.go +++ b/coordinator/mesh.go @@ -70,7 +70,7 @@ func (m *meshAuthority) SNPValidateOpts(report *sevsnp.Report) (*validate.Option func (m *meshAuthority) ValidateCallback(ctx context.Context, report *sevsnp.Report, nonce []byte, peerPubKeyBytes []byte) error { hostData := manifest.NewHexString(report.HostData) - commonName, ok := m.manifest.Policies[hostData] + dnsNames, ok := m.manifest.Policies[hostData] if !ok { return fmt.Errorf("report data %s not found in manifest", hostData) } @@ -81,7 +81,7 @@ func (m *meshAuthority) ValidateCallback(ctx context.Context, report *sevsnp.Rep } var extensions []pkix.Extension // TODO - cert, err := m.ca.NewAttestedMeshCert(commonName, extensions, peerPubKey) + cert, err := m.ca.NewAttestedMeshCert(dnsNames, extensions, peerPubKey) if err != nil { return fmt.Errorf("failed to issue new attested mesh cert: %w", err) } diff --git a/data/manifest.json b/data/manifest.json index 38ac580e0..0337a2685 100644 --- a/data/manifest.json +++ b/data/manifest.json @@ -1,7 +1,13 @@ { "Policies": { - "2b3422e2e44c933f5a2bea3d25fc36502951cfac3bd07ea2033936b4b72b5c65": "workload", - "3638d61e7c8701e19e819751eb61e2e353f25f68374443d03428c8acc39ed3e9": "coordinator-kbs" + "2b3422e2e44c933f5a2bea3d25fc36502951cfac3bd07ea2033936b4b72b5c65": [ + "workload.edg-coco", + "*.edg-coco" + ], + "3638d61e7c8701e19e819751eb61e2e353f25f68374443d03428c8acc39ed3e9": [ + "coordinator-kbs.edg-coco", + "*.edg-coco" + ] }, "ReferenceValues": { "SNP": { diff --git a/internal/ca/ca.go b/internal/ca/ca.go index 3bf8d3ebc..a5c32ad7c 100644 --- a/internal/ca/ca.go +++ b/internal/ca/ca.go @@ -91,7 +91,7 @@ func New(namespace string) (*CA, error) { }, nil } -func (c *CA) NewAttestedMeshCert(commonName string, extensions []pkix.Extension, subjectPublicKey any) ([]byte, error) { +func (c *CA) NewAttestedMeshCert(dnsNames []string, extensions []pkix.Extension, subjectPublicKey any) ([]byte, error) { serialNumber, err := crypto.GenerateCertificateSerialNumber() if err != nil { return nil, err @@ -100,7 +100,7 @@ func (c *CA) NewAttestedMeshCert(commonName string, extensions []pkix.Extension, now := time.Now() certTemplate := &x509.Certificate{ SerialNumber: serialNumber, - Subject: pkix.Name{CommonName: commonName}, + Subject: pkix.Name{CommonName: dnsNames[0]}, Issuer: pkix.Name{CommonName: "system:coordinator-kbs:intermediate"}, NotBefore: now.Add(-2 * time.Hour), NotAfter: now.Add(354 * 24 * time.Hour), @@ -108,7 +108,7 @@ func (c *CA) NewAttestedMeshCert(commonName string, extensions []pkix.Extension, KeyUsage: x509.KeyUsageDigitalSignature, BasicConstraintsValid: true, ExtraExtensions: extensions, - DNSNames: []string{fmt.Sprintf("*.%s", c.namespace)}, + DNSNames: dnsNames, } certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, c.intermCert, subjectPublicKey, c.intermPrivKey) diff --git a/internal/manifest/manifest.go b/internal/manifest/manifest.go index 07525d2bf..c751e724f 100644 --- a/internal/manifest/manifest.go +++ b/internal/manifest/manifest.go @@ -11,7 +11,7 @@ import ( type Manifest struct { // policyHash/HOSTDATA -> commonName - Policies map[HexString]string + Policies map[HexString][]string ReferenceValues ReferenceValues }