Add test for empty label selector and fix ACL name
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
This commit is contained in:
parent
c67a4c62f1
commit
8f4ae31562
|
|
@ -75,7 +75,7 @@ type ImageRepositorySpec struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccessFrom struct {
|
type AccessFrom struct {
|
||||||
NamespaceSelectors []NamespaceSelector `json:"namespaceSelector,omitempty"`
|
NamespaceSelectors []NamespaceSelector `json:"namespaceSelectors,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NamespaceSelector struct {
|
type NamespaceSelector struct {
|
||||||
|
|
|
||||||
|
|
@ -296,7 +296,7 @@ spec:
|
||||||
accessFrom:
|
accessFrom:
|
||||||
description: AccessFrom defines an ACL for allowing cross-namespace references to the ImageRepository object based on the caller's namespace labels.
|
description: AccessFrom defines an ACL for allowing cross-namespace references to the ImageRepository object based on the caller's namespace labels.
|
||||||
properties:
|
properties:
|
||||||
namespaceSelector:
|
namespaceSelectors:
|
||||||
items:
|
items:
|
||||||
properties:
|
properties:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,7 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
image: ghcr.io/stefanprodan/podinfo
|
image: ghcr.io/stefanprodan/podinfo
|
||||||
interval: 1m0s
|
interval: 1m0s
|
||||||
|
accessFrom:
|
||||||
|
namespaceSelectors:
|
||||||
|
- matchLabels:
|
||||||
|
kubernetes.io/metadata.name: flux-system
|
||||||
|
|
|
||||||
|
|
@ -332,29 +332,17 @@ func (r *ImagePolicyReconciler) hasAccessToRepository(ctx context.Context, polic
|
||||||
}
|
}
|
||||||
policyLabels := policyNamespace.GetLabels()
|
policyLabels := policyNamespace.GetLabels()
|
||||||
|
|
||||||
// deny access if the policy namespace has no labels
|
|
||||||
if len(policyLabels) == 0 {
|
|
||||||
return false, fmt.Errorf("ImageRepository '%s/%s' can't be accessed due to missing lables on namespace '%s'",
|
|
||||||
repo.Namespace, repo.Name, policy.Namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the policy namespace labels match any ACL
|
// check if the policy namespace labels match any ACL
|
||||||
var allowed bool
|
|
||||||
for _, selector := range acl.NamespaceSelectors {
|
for _, selector := range acl.NamespaceSelectors {
|
||||||
sel, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{MatchLabels: selector.MatchLabels})
|
sel, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{MatchLabels: selector.MatchLabels})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if sel.Matches(labels.Set(policyLabels)) {
|
if sel.Matches(labels.Set(policyLabels)) {
|
||||||
allowed = true
|
return true, nil
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !allowed {
|
return false, fmt.Errorf("ImageRepository '%s/%s' can't be accessed due to labels mismatch on namespace '%s'",
|
||||||
return allowed, fmt.Errorf("ImageRepository '%s/%s' can't be accessed due to lables mismatch on namespace '%s'",
|
repo.Namespace, repo.Name, policy.Namespace)
|
||||||
repo.Namespace, repo.Name, policy.Namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
return allowed, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,85 @@ var _ = Describe("ImagePolicy controller", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
When("is in different namespace with no empty match labels", func() {
|
||||||
|
It("grants access", func() {
|
||||||
|
policyNamespace := &corev1.Namespace{}
|
||||||
|
policyNamespace.Name = "acl-" + randStringRunes(5)
|
||||||
|
|
||||||
|
Expect(k8sClient.Create(context.Background(), policyNamespace)).To(Succeed())
|
||||||
|
defer k8sClient.Delete(context.Background(), policyNamespace)
|
||||||
|
|
||||||
|
versions := []string{"1.0.0", "1.0.1"}
|
||||||
|
imgRepo := loadImages(registryServer, "acl-image-"+randStringRunes(5), versions)
|
||||||
|
|
||||||
|
repo := imagev1.ImageRepository{
|
||||||
|
Spec: imagev1.ImageRepositorySpec{
|
||||||
|
Interval: metav1.Duration{Duration: reconciliationInterval},
|
||||||
|
Image: imgRepo,
|
||||||
|
AccessFrom: &imagev1.AccessFrom{
|
||||||
|
NamespaceSelectors: []imagev1.NamespaceSelector{
|
||||||
|
{
|
||||||
|
MatchLabels: make(map[string]string),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
repoObjectName := types.NamespacedName{
|
||||||
|
Name: "acl-repo-" + randStringRunes(5),
|
||||||
|
Namespace: "default",
|
||||||
|
}
|
||||||
|
repo.Name = repoObjectName.Name
|
||||||
|
repo.Namespace = repoObjectName.Namespace
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
r := imageRepoReconciler
|
||||||
|
Expect(r.Create(ctx, &repo)).To(Succeed())
|
||||||
|
|
||||||
|
Eventually(func() bool {
|
||||||
|
err := r.Get(ctx, repoObjectName, &repo)
|
||||||
|
return err == nil && repo.Status.LastScanResult != nil
|
||||||
|
}, timeout, interval).Should(BeTrue())
|
||||||
|
Expect(repo.Status.CanonicalImageName).To(Equal(imgRepo))
|
||||||
|
Expect(repo.Status.LastScanResult.TagCount).To(Equal(len(versions)))
|
||||||
|
|
||||||
|
polObjectName := types.NamespacedName{
|
||||||
|
Name: "acl-pol-" + randStringRunes(5),
|
||||||
|
Namespace: policyNamespace.Name,
|
||||||
|
}
|
||||||
|
pol := imagev1.ImagePolicy{
|
||||||
|
Spec: imagev1.ImagePolicySpec{
|
||||||
|
ImageRepositoryRef: meta.NamespacedObjectReference{
|
||||||
|
Name: repoObjectName.Name,
|
||||||
|
Namespace: repoObjectName.Namespace,
|
||||||
|
},
|
||||||
|
Policy: imagev1.ImagePolicyChoice{
|
||||||
|
SemVer: &imagev1.SemVerPolicy{
|
||||||
|
Range: "1.0.x",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pol.Namespace = polObjectName.Namespace
|
||||||
|
pol.Name = polObjectName.Name
|
||||||
|
|
||||||
|
ctx, cancel = context.WithTimeout(context.Background(), contextTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
Expect(r.Create(ctx, &pol)).To(Succeed())
|
||||||
|
|
||||||
|
Eventually(func() bool {
|
||||||
|
err := r.Get(ctx, polObjectName, &pol)
|
||||||
|
return err == nil && pol.Status.LatestImage != ""
|
||||||
|
}, timeout, interval).Should(BeTrue())
|
||||||
|
Expect(pol.Status.LatestImage).To(Equal(imgRepo + ":1.0.1"))
|
||||||
|
|
||||||
|
Expect(r.Delete(ctx, &pol)).To(Succeed())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
When("is in different namespace with matching ACL", func() {
|
When("is in different namespace with matching ACL", func() {
|
||||||
It("grants access", func() {
|
It("grants access", func() {
|
||||||
policyNamespace := &corev1.Namespace{}
|
policyNamespace := &corev1.Namespace{}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ Resource Types:
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<code>namespaceSelector</code><br>
|
<code>namespaceSelectors</code><br>
|
||||||
<em>
|
<em>
|
||||||
<a href="#image.toolkit.fluxcd.io/v1beta1.NamespaceSelector">
|
<a href="#image.toolkit.fluxcd.io/v1beta1.NamespaceSelector">
|
||||||
[]NamespaceSelector
|
[]NamespaceSelector
|
||||||
|
|
|
||||||
|
|
@ -132,9 +132,9 @@ spec:
|
||||||
secretRef:
|
secretRef:
|
||||||
name: regcred
|
name: regcred
|
||||||
accessFrom:
|
accessFrom:
|
||||||
- namespaceSelector:
|
namespaceSelectors:
|
||||||
matchLabels:
|
- matchLabels:
|
||||||
kubernetes.io/metadata.name: flux-system
|
kubernetes.io/metadata.name: flux-system
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note** that the `kubernetes.io/metadata.name` is a readonly label added by Kubernetes >= 1.21
|
**Note** that the `kubernetes.io/metadata.name` is a readonly label added by Kubernetes >= 1.21
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue