Skip to content
This repository was archived by the owner on Dec 14, 2023. It is now read-only.

Commit e8c9f3d

Browse files
Feat/add permissions boundaries (#10)
1 parent 2b5a5b1 commit e8c9f3d

23 files changed

+156
-100
lines changed

.github/workflows/chart-release.yml

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
name: Release Charts
22

3-
#on:
4-
# push:
5-
# branches: [ release ]
63
on:
74
push:
85
paths:
96
- 'config/helm/irsa/Chart.yaml'
10-
branches:
11-
- main
7+
# branches:
8+
# - main
129

1310
jobs:
1411
release:

Makefile

-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ deploy: manifests kustomize
6060

6161
gen-helm: manifests kustomize
6262
$(KUSTOMIZE) build config/default > config/helm/irsa/templates/irsa-operator.yml
63-
#echo version: ${CHART_VERSION} >> config/helm/Chart.yaml
64-
#echo image: ${DOCKER_IMAGE}:${CHART_VERSION} >> config/helm/values.yaml
6563

6664
# UnDeploy controller from the configured Kubernetes cluster in ~/.kube/config
6765
undeploy:

api/v1alpha1/role_types.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ func (r Role) IsPendingDeletion() bool {
5656

5757
// RoleSpec defines the desired state of Role
5858
type RoleSpec struct {
59-
ServiceAccountName string `json:"serviceAccountName"`
60-
61-
PolicyARN string `json:"policyarn,omitempty"`
62-
RoleARN string `json:"rolearn,omitempty"`
59+
ServiceAccountName string `json:"serviceAccountName"`
60+
PolicyARN string `json:"policyarn,omitempty"`
61+
RoleARN string `json:"rolearn,omitempty"`
62+
PermissionsBoundariesPolicyArn string `json:"permissionsBoundariesPolicyARN,omitempty"`
6363
}
6464

6565
// Validate returns an error if the RoleSpec is not valid

aws/aws.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ func (m RealAwsManager) GetAttachedRolePoliciesARNs(roleName string) ([]string,
313313
return arns, nil
314314
}
315315

316-
func (m RealAwsManager) CreateRole(role api.Role) error {
316+
func (m RealAwsManager) CreateRole(role api.Role, permissionsBoundariesPolicyARN string) error {
317317
_ = m.log.WithName("aws").WithName("role")
318318

319319
roleDoc, err := NewAssumeRolePolicyDoc(role, m.oidcProviderArn)
@@ -323,11 +323,16 @@ func (m RealAwsManager) CreateRole(role api.Role) error {
323323
}
324324

325325
rn := role.AwsName(m.clusterName)
326-
if _, err := m.Client.CreateRole(&iam.CreateRoleInput{
326+
roleInput := &iam.CreateRoleInput{
327327
RoleName: &rn,
328328
AssumeRolePolicyDocument: &roleDoc,
329329
Description: &desc,
330-
}); err != nil {
330+
}
331+
if permissionsBoundariesPolicyARN != "" {
332+
roleInput.PermissionsBoundary = &permissionsBoundariesPolicyARN
333+
}
334+
335+
if _, err := m.Client.CreateRole(roleInput); err != nil {
331336
if reqErr, ok := err.(awserr.RequestFailure); ok {
332337
if reqErr.StatusCode() == http.StatusConflict {
333338
// the role already exists, we return without error

aws/aws_test.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
var validPolicy = api.NewPolicy("name", "testns", []api.StatementSpec{
1010
{Resource: "arn:aws:s3:::my_corporate_bucket/exampleobject.png", Action: []string{"an:action"}},
1111
})
12+
1213
var _ = Describe("policy", func() {
1314
It("given a valid policy", func() {
1415

@@ -39,6 +40,7 @@ var _ = Describe("policy", func() {
3940

4041
var _ = Describe("role", func() {
4142
role := api.NewRole("name", "testns")
43+
4244
Context("given a valid role", func() {
4345
It("doesn't exist yet", func() {
4446
exists, err := awsmngr.RoleExists(role.AwsName(clusterName))
@@ -47,14 +49,23 @@ var _ = Describe("role", func() {
4749
})
4850

4951
Context("creation", func() {
52+
It("can create it without error without permissionsBoundariesPolicyARN", func() {
53+
err := awsmngr.CreateRole(*role, "")
54+
Expect(err).NotTo(HaveOccurred())
55+
})
56+
})
57+
58+
Context("creation", func() {
59+
permissionsBoundariesPolicyARN := "arn:aws:iam::123456789012:policy/UsersManageOwnCredentials"
60+
5061
It("can create it without error", func() {
51-
err := awsmngr.CreateRole(*role)
62+
err := awsmngr.CreateRole(*role, permissionsBoundariesPolicyARN)
5263
Expect(err).NotTo(HaveOccurred())
5364
})
5465

5566
Context("idempotency", func() {
5667
It("creation is idempotent", func() {
57-
err := awsmngr.CreateRole(*role)
68+
err := awsmngr.CreateRole(*role, permissionsBoundariesPolicyARN)
5869
Expect(err).NotTo(HaveOccurred())
5970
})
6071

aws/types.go

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func (s Statement) ToSpec() api.StatementSpec {
2828

2929
type StatementEffect string
3030

31+
// todo : remove this
3132
const (
3233
StatementAllow StatementEffect = "Allow"
3334
StatementDeny StatementEffect = "Deny"

config/crd/bases/irsa.voodoo.io_roles.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ spec:
3636
spec:
3737
description: RoleSpec defines the desired state of Role
3838
properties:
39+
permissionsBoundariesPolicyARN:
40+
type: string
3941
policyarn:
4042
type: string
4143
rolearn:

config/default/manager_auth_proxy_patch.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ spec:
2626
- "--leader-elect"
2727
- "--cluster-name={{ .Values.clusterName }}"
2828
- "--oidc-provider-arn={{ .Values.oidcProviderARN }}"
29+
- "--permissions-boundaries-policy-arn={{ .Values.permissionsBoundariesPolicyARN }}"

config/helm/irsa/Chart.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
apiVersion: "v1"
22
name: irsa-operator
3-
version: 0.0.5
3+
version: 0.0.6-rc.1

config/helm/irsa/templates/irsa-operator.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ spec:
192192
spec:
193193
description: RoleSpec defines the desired state of Role
194194
properties:
195+
permissionsBoundariesPolicyARN:
196+
type: string
195197
policyarn:
196198
type: string
197199
rolearn:
@@ -228,7 +230,7 @@ apiVersion: v1
228230
kind: ServiceAccount
229231
metadata:
230232
annotations:
231-
eks.amazonaws.com/role-arn: '{{ .Values.rolearn }}'
233+
eks.amazonaws.com/role-arn: '{{ .Values.roleARN }}'
232234
name: irsa-operator-oidc-sa
233235
namespace: irsa-operator-system
234236
---
@@ -489,6 +491,7 @@ spec:
489491
- --leader-elect
490492
- --cluster-name={{ .Values.clusterName }}
491493
- --oidc-provider-arn={{ .Values.oidcProviderARN }}
494+
- --permissions-boundaries-policy-arn={{ .Values.permissionsBoundariesPolicyARN }}
492495
command:
493496
- /manager
494497
image: ghcr.io/voodooteam/irsa-operator:v{{ .Chart.Version }}

config/helm/irsa/values.yaml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
clusterName:
2-
rolearn:
2+
roleARN:
33
oidcProviderARN:
4+
permissionsBoundariesPolicyARN: ""

config/manager/manager.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ kind: ServiceAccount
1010
metadata:
1111
name: oidc-sa
1212
annotations:
13-
eks.amazonaws.com/role-arn: '{{ .Values.rolearn }}'
13+
eks.amazonaws.com/role-arn: '{{ .Values.roleARN }}'
1414
---
1515
apiVersion: apps/v1
1616
kind: Deployment

config/samples/irsa_v1alpha1_iamroleserviceaccount.yaml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
apiVersion: irsa.voodoo.io/v1alpha1
22
kind: IamRoleServiceAccount
33
metadata:
4-
name: iamroleserviceaccount-test-sample
4+
name: s3put
55
spec:
6-
serviceAccountName: s3put
76
policy:
87
statement:
98
- resource: "arn:aws:s3:::test-irsa-4gkut9fl"

controllers/aws.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type AwsPolicyManager interface {
2020

2121
type AwsRoleManager interface {
2222
RoleExists(roleName string) (bool, error)
23-
CreateRole(api.Role) error
23+
CreateRole(role api.Role, permissionsBoundariesPolicyARN string) error
2424
DeleteRole(roleName string) error
2525
AttachRolePolicy(roleName, policyARN string) error
2626
GetAttachedRolePoliciesARNs(roleName string) ([]string, error)

controllers/aws_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ type awsStack struct {
2929
}
3030

3131
type awsRole struct {
32-
name string
33-
arn string
34-
attachedPolicies []string
32+
name string
33+
arn string
34+
attachedPolicies []string
35+
permissionsBoundariesPolicyARN string
3536
}
3637

3738
type awsMethod string
@@ -143,7 +144,7 @@ func (s *awsFake) GetStatement(arn string) ([]api.StatementSpec, error) {
143144
return stack.(awsStack).policy.Statement, nil
144145
}
145146

146-
func (s *awsFake) CreateRole(r api.Role) error {
147+
func (s *awsFake) CreateRole(r api.Role, permissionsBoundariesPolicyARN string) error {
147148
n := r.ObjectMeta.Name
148149
if err := s.shouldFailAt(n, createRole); err != nil {
149150
return err
@@ -155,7 +156,7 @@ func (s *awsFake) CreateRole(r api.Role) error {
155156
}
156157

157158
stack := raw.(awsStack)
158-
stack.role = awsRole{name: roleName(r), arn: roleArn(r), attachedPolicies: []string{}}
159+
stack.role = awsRole{name: roleName(r), arn: roleArn(r), attachedPolicies: []string{}, permissionsBoundariesPolicyARN: permissionsBoundariesPolicyARN}
159160
s.stacks.Store(n, stack)
160161
return nil
161162
}

controllers/helper.go

+32-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package controllers
22

3-
import "fmt"
3+
import (
4+
"fmt"
5+
)
46

57
type completed bool
68

@@ -24,6 +26,33 @@ func removeString(slice []string, s string) (result []string) {
2426
return
2527
}
2628

27-
func (r *RoleReconciler) logExtErr(err error, msg string) {
28-
r.log.Info(fmt.Sprintf("%s : %s", msg, err))
29+
func newMsg(msg string) Event {
30+
return Event{
31+
msg: msg,
32+
}
33+
}
34+
35+
func newErr(msg string, err error) Event {
36+
return Event{
37+
msg: msg,
38+
err: err,
39+
}
40+
}
41+
42+
type Event struct {
43+
msg string
44+
err error
45+
}
46+
47+
func (e Event) String() string {
48+
if e.err != nil {
49+
return fmt.Sprintf("%s (%s)", e.msg, e.err)
50+
}
51+
return e.msg
52+
}
53+
54+
func scope(sc string) func(string) string {
55+
return func(s string) string {
56+
return sc + ": " + s
57+
}
2958
}

controllers/iamroleserviceaccount_controller.go

-14
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import (
1616
"sigs.k8s.io/controller-runtime/pkg/client"
1717
"sigs.k8s.io/controller-runtime/pkg/controller"
1818

19-
"time"
20-
2119
api "github.com/VoodooTeam/irsa-operator/api/v1alpha1"
2220
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2321
)
@@ -37,8 +35,6 @@ type IamRoleServiceAccountReconciler struct {
3735
log logr.Logger
3836
scheme *runtime.Scheme
3937
finalizerID string
40-
41-
TestingDelay *time.Duration
4238
}
4339

4440
// +kubebuilder:rbac:groups=irsa.voodoo.io,resources=iamroleserviceaccounts,verbs=get;list;watch;create;update;delete
@@ -50,10 +46,6 @@ type IamRoleServiceAccountReconciler struct {
5046
func (r *IamRoleServiceAccountReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
5147
_ = r.log.WithValues("iamroleserviceaccount", req.NamespacedName)
5248

53-
{ // a processing delay can be set to ensure the testing framework sees every transitionnal state
54-
r.waitIfTesting()
55-
}
56-
5749
var irsa *api.IamRoleServiceAccount
5850
{ // extract role from the request
5951
var ok completed
@@ -456,12 +448,6 @@ func (r *IamRoleServiceAccountReconciler) updateStatus(ctx context.Context, obj
456448
return r.Status().Update(ctx, obj)
457449
}
458450

459-
func (r *IamRoleServiceAccountReconciler) waitIfTesting() {
460-
if r.TestingDelay != nil {
461-
time.Sleep(*r.TestingDelay)
462-
}
463-
}
464-
465451
func (r *IamRoleServiceAccountReconciler) registerFinalizerIfNeeded(role *api.IamRoleServiceAccount) completed {
466452
if !containsString(role.ObjectMeta.Finalizers, r.finalizerID) {
467453
// the finalizer isn't registered yet

controllers/model_test.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,22 @@ func run(irsaName string, wg *sync.WaitGroup) {
4242
// will log in case of failure the final state and the events that lead there
4343
defer func(iE map[awsMethod]struct{}) {
4444
if recover() != nil {
45-
log.Println(">>> inital errors :")
45+
log.Println(">>>")
46+
log.Println("inital errors :")
4647
spew.Dump(iE)
47-
log.Println(">>> ended up with stack state :")
48+
log.Println("<<<")
49+
50+
log.Println("ended up with stack state :")
51+
log.Println(">>>")
4852
spew.Dump(stack)
53+
log.Println("<<<")
54+
55+
log.Println("k8s resources")
56+
log.Println(">>>")
57+
spew.Dump(getRole(irsaName, testns))
58+
spew.Dump(getPolicy(irsaName, testns))
59+
spew.Dump(getIrsa(irsaName, testns))
60+
log.Println("<<<")
4961

5062
GinkgoT().FailNow()
5163
}
@@ -130,7 +142,8 @@ func getInitialErrs() map[awsMethod]struct{} {
130142
deleteRole,
131143
roleExists,
132144
getRoleARN,
133-
getAttachedRolePoliciesARNs}
145+
getAttachedRolePoliciesARNs,
146+
}
134147

135148
errs := make(map[awsMethod]struct{})
136149
for _, m := range methods {

controllers/policy_controller.go

-12
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package controllers
33
import (
44
"context"
55
"fmt"
6-
"time"
76

87
"github.com/go-logr/logr"
98
"k8s.io/apimachinery/pkg/api/errors"
@@ -36,8 +35,6 @@ type PolicyReconciler struct {
3635

3736
finalizerID string
3837
clusterName string
39-
40-
TestingDelay *time.Duration
4138
}
4239

4340
// +kubebuilder:rbac:groups=irsa.voodoo.io,resources=policies,verbs=get;list;watch;create;update;patch;delete
@@ -48,10 +45,6 @@ type PolicyReconciler struct {
4845
func (r *PolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
4946
_ = r.log.WithValues("policy", req.NamespacedName)
5047

51-
{ // a processing delay can be set to ensure the testing framework sees every transitionnal state
52-
r.waitIfTesting()
53-
}
54-
5548
var policy *api.Policy
5649
{ // extract policy from the request
5750
var ok completed
@@ -205,11 +198,6 @@ func (r *PolicyReconciler) executeFinalizerIfPresent(policy *api.Policy) complet
205198

206199
return true
207200
}
208-
func (r *PolicyReconciler) waitIfTesting() {
209-
if r.TestingDelay != nil {
210-
time.Sleep(*r.TestingDelay)
211-
}
212-
}
213201

214202
// helper function to update a Policy status
215203
func (r *PolicyReconciler) updateStatus(ctx context.Context, Policy *api.Policy, status api.PolicyStatus) error {

0 commit comments

Comments
 (0)