Skip to content

Commit

Permalink
Support automountServiceAccountToken in Service Accounts (#324)
Browse files Browse the repository at this point in the history
* Support automountServiceAccountToken in Service Accounts

* Example: add all supported options to SA subject

* Add automountServiceAccountToken to rbacBindings.subjects CRD

* Trigger tests

Co-authored-by: Andrew Suderman <[email protected]>
  • Loading branch information
v1r7u and sudermanjr authored Aug 3, 2022
1 parent 7806832 commit dea3ca2
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 3 deletions.
2 changes: 2 additions & 0 deletions deploy/2_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ spec:
items:
type: object
properties:
automountServiceAccountToken:
type: boolean
imagePullSecrets:
type: array
items:
Expand Down
3 changes: 3 additions & 0 deletions examples/rbacdefinition-everything.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ rbacBindings:
- kind: ServiceAccount
name: example
namespace: default
imagePullSecrets:
- robot-secret
automountServiceAccountToken: false
clusterRoleBindings:
- clusterRole: view
roleBindings:
Expand Down
3 changes: 2 additions & 1 deletion pkg/apis/rbacmanager/v1beta1/rbacdefinition_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import (
// Subject is an expansion on the rbacv1.Subject to allow definition of ImagePullSecrets for a Service Account
type Subject struct {
rbacv1.Subject
ImagePullSecrets []string `json:"imagePullSecrets"`
ImagePullSecrets []string `json:"imagePullSecrets"`
AutomountServiceAccountToken *bool `json:"automountServiceAccountToken,omitempty"`
}

// RBACBinding is a specification for a RBACBinding resource
Expand Down
100 changes: 100 additions & 0 deletions pkg/reconciler/cases_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2018 FairwindsOps Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package reconciler

import (
rbacmanagerv1beta1 "github.com/fairwindsops/rbac-manager/pkg/apis/rbacmanager/v1beta1"
v1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var trueVal bool = true
var falseVal bool = false

// Parse Subject into ServiceAccount
var saTestCases = []struct {
name string
given []rbacmanagerv1beta1.Subject
expected []v1.ServiceAccount
}{
{
"AutomountServiceAccountToken is true",
[]rbacmanagerv1beta1.Subject{{
Subject: rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: "robot", Namespace: "default"},
AutomountServiceAccountToken: &trueVal,
}},
[]v1.ServiceAccount{{
ObjectMeta: metav1.ObjectMeta{Name: "robot", Namespace: "default"},
AutomountServiceAccountToken: &trueVal,
}},
},
{
"AutomountServiceAccountToken is false",
[]rbacmanagerv1beta1.Subject{{
Subject: rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: "robot", Namespace: "default"},
AutomountServiceAccountToken: &falseVal,
}},
[]v1.ServiceAccount{{
ObjectMeta: metav1.ObjectMeta{Name: "robot", Namespace: "default"},
AutomountServiceAccountToken: &falseVal,
}},
},
{
"AutomountServiceAccountToken is empty",
[]rbacmanagerv1beta1.Subject{{
Subject: rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: "robot", Namespace: "default"},
}},
[]v1.ServiceAccount{{
ObjectMeta: metav1.ObjectMeta{Name: "robot", Namespace: "default"},
AutomountServiceAccountToken: nil,
}},
},
{
"ImagePullSecrets is empty",
[]rbacmanagerv1beta1.Subject{{
Subject: rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: "robot", Namespace: "default"},
ImagePullSecrets: []string{},
}},
[]v1.ServiceAccount{{
ObjectMeta: metav1.ObjectMeta{Name: "robot", Namespace: "default"},
ImagePullSecrets: nil,
}},
},
{
"ImagePullSecrets is not empty",
[]rbacmanagerv1beta1.Subject{{
Subject: rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: "robot", Namespace: "default"},
ImagePullSecrets: []string{"secret-z", "secret-a"},
}},
[]v1.ServiceAccount{{
ObjectMeta: metav1.ObjectMeta{Name: "robot", Namespace: "default"},
ImagePullSecrets: []v1.LocalObjectReference{{Name: "secret-a"}, {Name: "secret-z"}},
}},
},
{
"Parsing multiple Service Accounts",
[]rbacmanagerv1beta1.Subject{
{Subject: rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: "robot-a", Namespace: "default"}},
{Subject: rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: "robot-a", Namespace: "non-default"}},
{Subject: rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Name: "robot-z", Namespace: "default"}},
},
[]v1.ServiceAccount{
{ObjectMeta: metav1.ObjectMeta{Name: "robot-z", Namespace: "default"}},
{ObjectMeta: metav1.ObjectMeta{Name: "robot-a", Namespace: "non-default"}},
{ObjectMeta: metav1.ObjectMeta{Name: "robot-a", Namespace: "default"}},
},
},
}
3 changes: 2 additions & 1 deletion pkg/reconciler/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ func (p *Parser) parseRBACBinding(rbacBinding rbacmanagerv1beta1.RBACBinding, na
OwnerReferences: p.ownerRefs,
Labels: kube.Labels,
},
ImagePullSecrets: pullsecrets,
ImagePullSecrets: pullsecrets,
AutomountServiceAccountToken: requestedSubject.AutomountServiceAccountToken,
})
}
}
Expand Down
22 changes: 21 additions & 1 deletion pkg/reconciler/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,24 @@ func TestManagerToRbacSubjects(t *testing.T) {
assert.ElementsMatch(t, expected, actual, "expected subjects to match")
}

func TestServiceAccountParsing(t *testing.T) {
for _, n := range saTestCases {
t.Run(n.name, func(t *testing.T) {
client := fake.NewSimpleClientset()
rbacDef := rbacmanagerv1beta1.RBACDefinition{}
rbacDef.Name = "rbac-config"

rbacDef.RBACBindings = []rbacmanagerv1beta1.RBACBinding{{
Name: "devs",
Subjects: n.given,
ClusterRoleBindings: []rbacmanagerv1beta1.ClusterRoleBinding{},
}}

newParseTest(t, client, rbacDef, []rbacv1.RoleBinding{}, []rbacv1.ClusterRoleBinding{}, n.expected)
})
}
}

func newParseTest(t *testing.T, client *fake.Clientset, rbacDef rbacmanagerv1beta1.RBACDefinition, expectedRb []rbacv1.RoleBinding, expectedCrb []rbacv1.ClusterRoleBinding, expectedSa []corev1.ServiceAccount) {
p := Parser{Clientset: client}

Expand Down Expand Up @@ -353,12 +371,14 @@ func expectParsedSA(t *testing.T, p Parser, expected []corev1.ServiceAccount) {
for _, actualSa := range p.parsedServiceAccounts {
if actualSa.Name == expectedSa.Name && expectedSa.Namespace == actualSa.Namespace {
matchFound = true
assert.ElementsMatch(t, actualSa.ImagePullSecrets, expectedSa.ImagePullSecrets)
assert.EqualValues(t, expectedSa.AutomountServiceAccountToken, actualSa.AutomountServiceAccountToken)
break
}
}

if !matchFound {
t.Fatalf("Matching service account not found for %v", expectedSa.Name)
t.Fatalf("Matching service account not found for %s in namespace %s", expectedSa.Name, expectedSa.Namespace)
}
}
}
Expand Down

0 comments on commit dea3ca2

Please sign in to comment.