Compare commits

...

4 Commits

Author SHA1 Message Date
Martin Hansen
15538621dc
Merge ea0a7b5698 into 6d0aeec367 2026-07-25 20:30:50 +02:00
renovate[bot]
6d0aeec367
chore(deps): update amazon/aws-cli docker tag to v2.36.7 (#1029)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-24 11:16:13 +02:00
renovate[bot]
ce8179442b
fix(deps): update kubernetes monorepo to v0.36.3 (#1031)
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-23 15:38:26 +02:00
Martin Hansen
ea0a7b5698
Scope barman Secret RBAC
Avoid granting namespace-wide Secret access when ObjectStores do not need credential Secrets.

Ref: #892
Signed-off-by: Martin Hansen <dontbeevilpls@gmail.com>
2026-05-07 15:49:04 +02:00
8 changed files with 99 additions and 41 deletions

14
go.mod
View File

@ -16,10 +16,10 @@ require (
github.com/spf13/viper v1.21.0 github.com/spf13/viper v1.21.0
google.golang.org/grpc v1.82.1 google.golang.org/grpc v1.82.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.36.2 k8s.io/api v0.36.3
k8s.io/apiextensions-apiserver v0.36.2 k8s.io/apiextensions-apiserver v0.36.3
k8s.io/apimachinery v0.36.2 k8s.io/apimachinery v0.36.3
k8s.io/client-go v0.36.2 k8s.io/client-go v0.36.3
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3
sigs.k8s.io/controller-runtime v0.24.1 sigs.k8s.io/controller-runtime v0.24.1
sigs.k8s.io/kustomize/api v0.21.1 sigs.k8s.io/kustomize/api v0.21.1
@ -125,11 +125,11 @@ require (
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/apiserver v0.36.2 // indirect k8s.io/apiserver v0.36.3 // indirect
k8s.io/component-base v0.36.2 // indirect k8s.io/component-base v0.36.3 // indirect
k8s.io/klog/v2 v2.140.0 // indirect k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25 // indirect k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25 // indirect
k8s.io/streaming v0.36.2 // indirect k8s.io/streaming v0.36.3 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect
sigs.k8s.io/gateway-api v1.6.0 // indirect sigs.k8s.io/gateway-api v1.6.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect

28
go.sum
View File

@ -308,24 +308,24 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.36.2 h1:TF6YDLIzKfccK7cq9YpTcGX8TJmEkHVRv78DM51fRYY= k8s.io/api v0.36.3 h1:NxB+05W2UGqXWFXcLO0RB5cnqnUPP5v5sVlaOH0Iz4w=
k8s.io/api v0.36.2/go.mod h1:F4LbMO4brjZYh7yFkXWhynSvtB7YauxV4c+HHkNRGNg= k8s.io/api v0.36.3/go.mod h1:JzLQKqRHC5+I8RVj/lS3lCg0mg6nWI9Fo/Sk3ElxHzg=
k8s.io/apiextensions-apiserver v0.36.2 h1:3O5gqOj/dt2XWWbpMe+TXWpE9yU6pjM/tXxtHHJT/K4= k8s.io/apiextensions-apiserver v0.36.3 h1:dPmOAPhwTtqb1bTxbFPsy18KHPhktQeO3WUPXunZIB0=
k8s.io/apiextensions-apiserver v0.36.2/go.mod h1:cL1tBWe8XSaP1H30iWKGo7hf6iAUUUJPEU70dskmAnA= k8s.io/apiextensions-apiserver v0.36.3/go.mod h1:KTXFqgXiuw2pRoL+Wpmttqc+up9Xt/GohadPWeLLOa4=
k8s.io/apimachinery v0.36.2 h1:0PE/W/WNy1UX61NLbXY5TMbJ6UwLL6E6lAPkYrKFxbQ= k8s.io/apimachinery v0.36.3 h1:PkzMRBRG8joFD8EhCuQAtNPvJlxb82FwplP26HIzvAM=
k8s.io/apimachinery v0.36.2/go.mod h1:fvf/HOLXq9RId0rnDIbN1OEBvHXdQbLMM8nu0LcBUf4= k8s.io/apimachinery v0.36.3/go.mod h1:cTSjBWgPe/6CQyBKzY/hDIRWCQQQeK0mfLbml0UYFHE=
k8s.io/apiserver v0.36.2 h1:6vMnkmHZPeBloNkHUhmZYq7Ylv8WIB8xjyEl+eSt26E= k8s.io/apiserver v0.36.3 h1:MGSg2SkdfuytiDEcRylT5mQFmmSsbx90XFUO67Y4bsQ=
k8s.io/apiserver v0.36.2/go.mod h1:9PoQ2ikCytrZyZg11mGhLEF5m8Rgsb5FJmYJ4Wvnl1k= k8s.io/apiserver v0.36.3/go.mod h1:fVH7zv9EUNUA7Fl7LtDKh8aB9W7u1VQPSGtWV5SjUxg=
k8s.io/client-go v0.36.2 h1:bfgxmFKc9CgqsgX4xKLAAdmTQlWee7Ob/HlDOrJ5TBI= k8s.io/client-go v0.36.3 h1:M4JdVzXxYcZk4fGpfDdYnxSwhLKWCFoQsHW6t+z8Hfg=
k8s.io/client-go v0.36.2/go.mod h1:1vgO4OAlfPnoLcb+Rze2GF5rAr14w8qjrYMoyXJzQj0= k8s.io/client-go v0.36.3/go.mod h1:gcPwr0c87vjjG6HB6pWEqOeuYVoXSsREjzux2j6GF30=
k8s.io/component-base v0.36.2 h1:Z0VH80O7Ng0HDZnZj3WRR3urEGa0kTwmO8CwEwjVK1w= k8s.io/component-base v0.36.3 h1:vc/UFvPCkW0irPz84LAodAL1j3f4xktPM6dDJIEheAY=
k8s.io/component-base v0.36.2/go.mod h1:mGfFOA7Gwpdm1VW2cwSQYbiDIlz8GD2WGwH88QSeCyA= k8s.io/component-base v0.36.3/go.mod h1:hZbNFG+gCMl9EbykDGEu73feKP9/Cq6JsV4pTo9GTO8=
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25 h1:mPMaPMpBij2V1Wv/fR+HW124vVGXXvOSS9ver/9yjWs= k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25 h1:mPMaPMpBij2V1Wv/fR+HW124vVGXXvOSS9ver/9yjWs=
k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25/go.mod h1:V/QaCUYDa+0QpcHhVVc5l99Uz56wEMEXBSj9oCDkNDY= k8s.io/kube-openapi v0.0.0-20260603220949-865597e52e25/go.mod h1:V/QaCUYDa+0QpcHhVVc5l99Uz56wEMEXBSj9oCDkNDY=
k8s.io/streaming v0.36.2 h1:NSKthPPg9UFSKsRauVJUVGH2Dvn8fhKmY4qrMkw/p98= k8s.io/streaming v0.36.3 h1:9rAaqBk0C0Pc7+/fqGekj07NV+/Xrew58p647A0JT8w=
k8s.io/streaming v0.36.2/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s= k8s.io/streaming v0.36.3/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s=
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 h1:jVkFFVfXdXP74B/zbO3hM3hpSFD0xvhQ5U686DPurkE= k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 h1:jVkFFVfXdXP74B/zbO3hM3hpSFD0xvhQ5U686DPurkE=
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3/go.mod h1:M2s5JB1lIYP3jzZdorPLHXIPJzt9vv2muW5a6L9DtNM= k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3/go.mod h1:M2s5JB1lIYP3jzZdorPLHXIPJzt9vv2muW5a6L9DtNM=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec=

View File

@ -60,7 +60,7 @@ func BuildRoleRules(barmanObjects []barmancloudv1.ObjectStore) []rbacv1.PolicyRu
} }
} }
return []rbacv1.PolicyRule{ rules := []rbacv1.PolicyRule{
{ {
APIGroups: []string{ APIGroups: []string{
barmancloudv1.GroupVersion.Group, barmancloudv1.GroupVersion.Group,
@ -87,7 +87,11 @@ func BuildRoleRules(barmanObjects []barmancloudv1.ObjectStore) []rbacv1.PolicyRu
}, },
ResourceNames: barmanObjectsSet.ToSortedList(), ResourceNames: barmanObjectsSet.ToSortedList(),
}, },
{ }
secrets := secretsSet.ToSortedList()
if len(secrets) > 0 {
rules = append(rules, rbacv1.PolicyRule{
APIGroups: []string{ APIGroups: []string{
"", "",
}, },
@ -99,9 +103,11 @@ func BuildRoleRules(barmanObjects []barmancloudv1.ObjectStore) []rbacv1.PolicyRu
"watch", "watch",
"list", "list",
}, },
ResourceNames: secretsSet.ToSortedList(), ResourceNames: secrets,
}, })
} }
return rules
} }
// ObjectStoreNamesFromRole extracts the ObjectStore names referenced // ObjectStoreNamesFromRole extracts the ObjectStore names referenced

View File

@ -78,13 +78,12 @@ var _ = Describe("BuildRoleRules", func() {
Expect(rules[2].ResourceNames).To(ConsistOf("secret-a", "secret-b")) Expect(rules[2].ResourceNames).To(ConsistOf("secret-a", "secret-b"))
}) })
It("should produce rules with empty ResourceNames for empty input", func() { It("should not produce a secrets rule for empty input", func() {
rules := BuildRoleRules(nil) rules := BuildRoleRules(nil)
Expect(rules).To(HaveLen(3)) Expect(rules).To(HaveLen(2))
Expect(rules[0].ResourceNames).To(BeEmpty()) Expect(rules[0].ResourceNames).To(BeEmpty())
Expect(rules[0].ResourceNames).NotTo(BeNil()) Expect(rules[0].ResourceNames).NotTo(BeNil())
Expect(rules[1].ResourceNames).To(BeEmpty()) Expect(rules[1].ResourceNames).To(BeEmpty())
Expect(rules[2].ResourceNames).To(BeEmpty())
}) })
It("should deduplicate secret names across ObjectStores", func() { It("should deduplicate secret names across ObjectStores", func() {
@ -95,6 +94,31 @@ var _ = Describe("BuildRoleRules", func() {
rules := BuildRoleRules(objects) rules := BuildRoleRules(objects)
Expect(rules[2].ResourceNames).To(Equal([]string{"shared-secret"})) Expect(rules[2].ResourceNames).To(Equal([]string{"shared-secret"}))
}) })
It("should not produce a secrets rule when ObjectStores use IAM role inheritance", func() {
objects := []barmancloudv1.ObjectStore{
{
ObjectMeta: metav1.ObjectMeta{
Name: "store-a",
Namespace: "default",
},
Spec: barmancloudv1.ObjectStoreSpec{
Configuration: barmanapi.BarmanObjectStoreConfiguration{
DestinationPath: "s3://bucket/path",
BarmanCredentials: barmanapi.BarmanCredentials{
AWS: &barmanapi.S3Credentials{
InheritFromIAMRole: true,
},
},
},
},
},
}
rules := BuildRoleRules(objects)
Expect(rules).To(HaveLen(2))
Expect(rules[0].ResourceNames).To(Equal([]string{"store-a"}))
Expect(rules[1].ResourceNames).To(Equal([]string{"store-a"}))
})
}) })
var _ = Describe("BuildRole", func() { var _ = Describe("BuildRole", func() {

View File

@ -28,6 +28,9 @@ import (
func CollectSecretNamesFromCredentials(barmanCredentials *barmanapi.BarmanCredentials) []string { func CollectSecretNamesFromCredentials(barmanCredentials *barmanapi.BarmanCredentials) []string {
var references []*machineryapi.SecretKeySelector var references []*machineryapi.SecretKeySelector
if barmanCredentials.AWS != nil { if barmanCredentials.AWS != nil {
// When using IAM role inheritance, barman-cloud uses the pod
// environment credential chain and does not read credential Secrets.
if !barmanCredentials.AWS.InheritFromIAMRole {
references = append( references = append(
references, references,
barmanCredentials.AWS.AccessKeyIDReference, barmanCredentials.AWS.AccessKeyIDReference,
@ -36,6 +39,7 @@ func CollectSecretNamesFromCredentials(barmanCredentials *barmanapi.BarmanCreden
barmanCredentials.AWS.SessionToken, barmanCredentials.AWS.SessionToken,
) )
} }
}
if barmanCredentials.Azure != nil { if barmanCredentials.Azure != nil {
// When using default Azure credentials or managed identity, no secrets are required // When using default Azure credentials or managed identity, no secrets are required
if !barmanCredentials.Azure.UseDefaultAzureCredentials && if !barmanCredentials.Azure.UseDefaultAzureCredentials &&

View File

@ -57,6 +57,29 @@ var _ = Describe("CollectSecretNamesFromCredentials", func() {
secrets := CollectSecretNamesFromCredentials(credentials) secrets := CollectSecretNamesFromCredentials(credentials)
Expect(secrets).To(BeEmpty()) Expect(secrets).To(BeEmpty())
}) })
It("should return empty list when using InheritFromIAMRole", func() {
credentials := &barmanapi.BarmanCredentials{
AWS: &barmanapi.S3Credentials{
InheritFromIAMRole: true,
AccessKeyIDReference: &machineryapi.SecretKeySelector{
LocalObjectReference: machineryapi.LocalObjectReference{
Name: "aws-secret",
},
Key: "access-key-id",
},
RegionReference: &machineryapi.SecretKeySelector{
LocalObjectReference: machineryapi.LocalObjectReference{
Name: "aws-region",
},
Key: "region",
},
},
}
secrets := CollectSecretNamesFromCredentials(credentials)
Expect(secrets).To(BeEmpty())
})
}) })
Context("when collecting secrets from Azure credentials", func() { Context("when collecting secrets from Azure credentials", func() {

View File

@ -31,8 +31,8 @@ import (
apierrs "k8s.io/apimachinery/pkg/api/errors" apierrs "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/client/interceptor" "sigs.k8s.io/controller-runtime/pkg/client/interceptor"
@ -261,7 +261,7 @@ var _ = Describe("ObjectStoreReconciler", func() {
Expect(result).To(Equal(reconcile.Result{})) Expect(result).To(Equal(reconcile.Result{}))
}) })
It("should produce empty ResourceNames when all ObjectStores are deleted", func() { It("should omit the secrets rule when all ObjectStores are deleted", func() {
store := newTestObjectStore("my-store", "default", "aws-creds") store := newTestObjectStore("my-store", "default", "aws-creds")
role := newLabeledRole("my-cluster", "default", []barmancloudv1.ObjectStore{*store}) role := newLabeledRole("my-cluster", "default", []barmancloudv1.ObjectStore{*store})
@ -291,10 +291,11 @@ var _ = Describe("ObjectStoreReconciler", func() {
Name: "my-cluster-barman-cloud", Name: "my-cluster-barman-cloud",
}, &updatedRole)).To(Succeed()) }, &updatedRole)).To(Succeed())
// All rules should have empty ResourceNames
Expect(updatedRole.Rules[0].ResourceNames).To(BeEmpty()) Expect(updatedRole.Rules[0].ResourceNames).To(BeEmpty())
Expect(updatedRole.Rules[1].ResourceNames).To(BeEmpty()) Expect(updatedRole.Rules[1].ResourceNames).To(BeEmpty())
Expect(updatedRole.Rules[2].ResourceNames).To(BeEmpty()) for _, rule := range updatedRole.Rules {
Expect(rule.Resources).NotTo(Equal([]string{"secrets"}))
}
}) })
It("should return an error when listing Roles fails", func() { It("should return an error when listing Roles fails", func() {

View File

@ -121,8 +121,8 @@ func newS3ClientDeployment(namespace string) *appsv1.Deployment {
{ {
Name: s3ClientName, Name: s3ClientName,
// renovate: datasource=docker depName=amazon/aws-cli versioning=docker // renovate: datasource=docker depName=amazon/aws-cli versioning=docker
// Version: 2.36.3 // Version: 2.36.7
Image: "docker.io/amazon/aws-cli@sha256:bdd02067a00c354684086071b475955c54caa7bd88b851aac99a51326fe19652", Image: "docker.io/amazon/aws-cli@sha256:5b76c069e37cfa091ec6398dc683c09e0c9ef8ae2e557b0a36d931df34011227",
Command: []string{"sleep", "infinity"}, Command: []string{"sleep", "infinity"},
Env: []corev1.EnvVar{ Env: []corev1.EnvVar{
{ {