Skip to content

Commit

Permalink
Manage RoleBinding for rbac enabled cluster
Browse files Browse the repository at this point in the history
  • Loading branch information
tamalsaha committed Oct 10, 2017
1 parent 3b2a281 commit 054813d
Show file tree
Hide file tree
Showing 19 changed files with 440 additions and 51 deletions.
5 changes: 5 additions & 0 deletions chart/stash/templates/cluster-role.yaml
Expand Up @@ -54,4 +54,9 @@ rules:
resources:
- pods
verbs: ["list", delete"]
- apiGroups:
- rbac.authorization.k8s.io
resources:
- rolebindings
verbs: ["get", "create", "delete", "patch"]
{{ end }}
1 change: 1 addition & 0 deletions chart/stash/templates/deployment.yaml
Expand Up @@ -24,6 +24,7 @@ spec:
- args:
- run
- --v=3
- --rbac={{ .Values.rbac.create }}
image: {{ .Values.operator.image }}:{{ .Values.operator.tag }}
imagePullPolicy: {{ .Values.operator.pullPolicy }}
name: operator
Expand Down
46 changes: 6 additions & 40 deletions docs/rbac.md
@@ -1,57 +1,23 @@
# Configuring RBAC

To use Stash in a cluster with RBAC enabled, [install Stash](/docs/install.md) with RBAC options.
To use Stash in a cluster with RBAC enabled, [install Stash](/docs/install.md) with RBAC options. This creates a ClusterRole named `stash-sidecar`.

Sidecar container added to workloads makes various calls to Kubernetes api. ServiceAccounts used with workloads should have the following roles:
Sidecar container added to workloads makes various calls to Kubernetes api. ServiceAccounts used with Deployment, ReplicaSet, DaemonSet and ReplicationController workloads are automatically bound to `stash-sidecar` ClusterRole by Stash operator. Users should manually add the following RoleBinding to service accounts used with StatefulSet workloads to authorize these api calls.

```yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: stash-sidecar
rules:
- apiGroups:
- stash.appscode.com
resources: ["*"]
verbs: ["*"]
- apiGroups:
- apps
resources:
- deployments
verbs: ["get"]
- apiGroups:
- extensions
resources:
- daemonsets
- replicasets
verbs: ["get"]
- apiGroups: [""]
resources:
- replicationcontrollers
- secrets
verbs: ["get"]
- apiGroups: [""]
resources:
- events
verbs: ["create"]
```

Create `stash-sidecar` ClusterRole, if it is not already present.

Now, create a RoleBinding for service account used to a workload.
```yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: workload-sa
name: <statefulset-name>-stash-sidecar
namespace: <statefulset-namespace>
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: stash-sidecar
subjects:
- kind: ServiceAccount
name: workload-sa
namespace: default
name: <statefulset-sa>
namespace: <statefulset-namespace>
```

You can find full working examples [here](/docs/examples/workloads).
Expand Down
1 change: 1 addition & 0 deletions docs/reference/stash_run.md
Expand Up @@ -18,6 +18,7 @@ stash run [flags]
-h, --help help for run
--kubeconfig string Path to kubeconfig file with authorization information (the master location is set by the master flag).
--master string The address of the Kubernetes API server (overrides any value in kubeconfig)
--rbac Enable RBAC for operator
--resync-period duration If non-zero, will re-list this often. Otherwise, re-list will be delayed aslong as possible (until the upstream source closes the watch or times out. (default 5m0s)
--scratch-dir emptyDir Directory used to store temporary files. Use an emptyDir in Kubernetes. (default "/tmp")
```
Expand Down
7 changes: 4 additions & 3 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions hack/deploy/with-rbac.yaml
Expand Up @@ -48,6 +48,11 @@ rules:
resources:
- pods
verbs: ["list", delete"]
- apiGroups:
- rbac.authorization.k8s.io
resources:
- rolebindings
verbs: ["get", "create", "delete", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
Expand Down Expand Up @@ -93,6 +98,7 @@ spec:
args:
- run
- --v=3
- --rbac=true
image: appscode/stash:0.5.0
ports:
- containerPort: 56790
Expand Down Expand Up @@ -151,18 +157,19 @@ rules:
resources: ["*"]
verbs: ["*"]
- apiGroups:
- extensions
- apps
resources:
- deployments
verbs: ["get"]
- apiGroups:
- extensions
resources:
- daemonsets
- replicasets
verbs: ["get"]
- apiGroups: [""]
resources:
- replicationcontrollers
verbs: ["*"]
- apiGroups: [""]
resources:
- secrets
verbs: ["get"]
- apiGroups: [""]
Expand Down
1 change: 1 addition & 0 deletions pkg/cmds/run.go
Expand Up @@ -87,6 +87,7 @@ func NewCmdRun(version string) *cobra.Command {
cmd.Flags().StringVar(&masterURL, "master", masterURL, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
cmd.Flags().StringVar(&kubeconfigPath, "kubeconfig", kubeconfigPath, "Path to kubeconfig file with authorization information (the master location is set by the master flag).")
cmd.Flags().StringVar(&address, "address", address, "Address to listen on for web interface and telemetry.")
cmd.Flags().BoolVar(&opts.EnableRBAC, "rbac", opts.EnableRBAC, "Enable RBAC for operator")
cmd.Flags().StringVar(&scratchDir, "scratch-dir", scratchDir, "Directory used to store temporary files. Use an `emptyDir` in Kubernetes.")
cmd.Flags().DurationVar(&opts.ResyncPeriod, "resync-period", opts.ResyncPeriod, "If non-zero, will re-list this often. Otherwise, re-list will be delayed aslong as possible (until the upstream source closes the watch or times out.")

Expand Down
1 change: 1 addition & 0 deletions pkg/controller/config.go
Expand Up @@ -5,6 +5,7 @@ import (
)

type Options struct {
EnableRBAC bool
SidecarImageTag string
ResyncPeriod time.Duration
MaxNumRequeues int
Expand Down
14 changes: 14 additions & 0 deletions pkg/controller/daemonsets.go
Expand Up @@ -155,6 +155,13 @@ func (c *StashController) EnsureDaemonSetSidecar(resource *extensions.DaemonSet,
return err
}

if c.options.EnableRBAC {
err := c.ensureRoleBinding(kutil.GetObjectReference(resource, extensions.SchemeGroupVersion), resource.Spec.Template.Spec.ServiceAccountName)
if err != nil {
return err
}
}

resource, err = ext_util.PatchDaemonSet(c.k8sClient, resource, func(obj *extensions.DaemonSet) *extensions.DaemonSet {
obj.Spec.Template.Spec.Containers = core_util.UpsertContainer(obj.Spec.Template.Spec.Containers, util.CreateSidecarContainer(new, c.options.SidecarImageTag, "DaemonSet/"+obj.Name))
obj.Spec.Template.Spec.Volumes = util.UpsertScratchVolume(obj.Spec.Template.Spec.Volumes)
Expand Down Expand Up @@ -191,6 +198,13 @@ func (c *StashController) EnsureDaemonSetSidecar(resource *extensions.DaemonSet,
}

func (c *StashController) EnsureDaemonSetSidecarDeleted(resource *extensions.DaemonSet, restic *api.Restic) (err error) {
if c.options.EnableRBAC {
err := c.ensureRoleBindingDeleted(resource.ObjectMeta)
if err != nil {
return err
}
}

resource, err = ext_util.PatchDaemonSet(c.k8sClient, resource, func(obj *extensions.DaemonSet) *extensions.DaemonSet {
obj.Spec.Template.Spec.Containers = core_util.EnsureContainerDeleted(obj.Spec.Template.Spec.Containers, util.StashContainer)
obj.Spec.Template.Spec.Volumes = util.EnsureVolumeDeleted(obj.Spec.Template.Spec.Volumes, util.ScratchDirVolumeName)
Expand Down
14 changes: 14 additions & 0 deletions pkg/controller/deployment.go
Expand Up @@ -155,6 +155,13 @@ func (c *StashController) EnsureDeploymentSidecar(resource *apps.Deployment, old
return err
}

if c.options.EnableRBAC {
err := c.ensureRoleBinding(kutil.GetObjectReference(resource, apps.SchemeGroupVersion), resource.Spec.Template.Spec.ServiceAccountName)
if err != nil {
return err
}
}

resource, err = apps_util.PatchDeployment(c.k8sClient, resource, func(obj *apps.Deployment) *apps.Deployment {
obj.Spec.Template.Spec.Containers = core_util.UpsertContainer(obj.Spec.Template.Spec.Containers, util.CreateSidecarContainer(new, c.options.SidecarImageTag, "Deployment/"+obj.Name))
obj.Spec.Template.Spec.Volumes = util.UpsertScratchVolume(obj.Spec.Template.Spec.Volumes)
Expand Down Expand Up @@ -191,6 +198,13 @@ func (c *StashController) EnsureDeploymentSidecar(resource *apps.Deployment, old
}

func (c *StashController) EnsureDeploymentSidecarDeleted(resource *apps.Deployment, restic *api.Restic) (err error) {
if c.options.EnableRBAC {
err := c.ensureRoleBindingDeleted(resource.ObjectMeta)
if err != nil {
return err
}
}

resource, err = apps_util.PatchDeployment(c.k8sClient, resource, func(obj *apps.Deployment) *apps.Deployment {
obj.Spec.Template.Spec.Containers = core_util.EnsureContainerDeleted(obj.Spec.Template.Spec.Containers, util.StashContainer)
obj.Spec.Template.Spec.Volumes = util.EnsureVolumeDeleted(obj.Spec.Template.Spec.Volumes, util.ScratchDirVolumeName)
Expand Down
74 changes: 74 additions & 0 deletions pkg/controller/rbac.go
@@ -0,0 +1,74 @@
package controller

import (
"github.com/appscode/go/log"
"github.com/appscode/go/types"
rbac_util "github.com/appscode/kutil/rbac/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
apiv1 "k8s.io/client-go/pkg/api/v1"
rbac "k8s.io/client-go/pkg/apis/rbac/v1beta1"
)

const (
sidecarClusterRole = "stash-sidecar"
)

func (c *StashController) getRoleBindingName(name string) string {
return name + "-" + sidecarClusterRole
}

func (c *StashController) ensureOwnerReference(rb metav1.ObjectMeta, resource *apiv1.ObjectReference) metav1.ObjectMeta {
fi := -1
for i, ref := range rb.OwnerReferences {
if ref.Kind == ref.Kind && ref.Name == ref.Name {
fi = i
break
}
}
if fi == -1 {
rb.OwnerReferences = append(rb.OwnerReferences, metav1.OwnerReference{})
fi = len(rb.OwnerReferences) - 1
}
rb.OwnerReferences[fi].APIVersion = resource.APIVersion
rb.OwnerReferences[fi].Kind = resource.Kind
rb.OwnerReferences[fi].Name = resource.Name
rb.OwnerReferences[fi].UID = resource.UID
rb.OwnerReferences[fi].BlockOwnerDeletion = types.TrueP()
return rb
}

func (c *StashController) ensureRoleBinding(resource *apiv1.ObjectReference, sa string) error {
meta := metav1.ObjectMeta{
Namespace: resource.Namespace,
Name: c.getRoleBindingName(resource.Name),
}
_, err := rbac_util.CreateOrPatchRoleBinding(c.k8sClient, meta, func(in *rbac.RoleBinding) *rbac.RoleBinding {
in.ObjectMeta = c.ensureOwnerReference(in.ObjectMeta, resource)

if in.Annotations == nil {
in.Annotations = map[string]string{}
}

in.RoleRef = rbac.RoleRef{
APIGroup: rbac.GroupName,
Kind: "ClusterRole",
Name: sidecarClusterRole,
}
in.Subjects = []rbac.Subject{
{
Kind: "ServiceAccount",
Name: sa,
Namespace: resource.Namespace,
},
}
return in
})
return err
}

func (c *StashController) ensureRoleBindingDeleted(resource metav1.ObjectMeta) error {
log.Infof("Deleting RoleBinding %s/%s", resource.Namespace, c.getRoleBindingName(resource.Name))
return c.k8sClient.RbacV1beta1().
RoleBindings(resource.Namespace).
Delete(c.getRoleBindingName(resource.Name), &metav1.DeleteOptions{})
}
14 changes: 14 additions & 0 deletions pkg/controller/rcs.go
Expand Up @@ -153,6 +153,13 @@ func (c *StashController) EnsureReplicationControllerSidecar(resource *apiv1.Rep
return err
}

if c.options.EnableRBAC {
err := c.ensureRoleBinding(kutil.GetObjectReference(resource, apiv1.SchemeGroupVersion), resource.Spec.Template.Spec.ServiceAccountName)
if err != nil {
return err
}
}

resource, err = core_util.PatchRC(c.k8sClient, resource, func(obj *apiv1.ReplicationController) *apiv1.ReplicationController {
obj.Spec.Template.Spec.Containers = core_util.UpsertContainer(obj.Spec.Template.Spec.Containers, util.CreateSidecarContainer(new, c.options.SidecarImageTag, "rc/"+obj.Name))
obj.Spec.Template.Spec.Volumes = util.UpsertScratchVolume(obj.Spec.Template.Spec.Volumes)
Expand Down Expand Up @@ -189,6 +196,13 @@ func (c *StashController) EnsureReplicationControllerSidecar(resource *apiv1.Rep
}

func (c *StashController) EnsureReplicationControllerSidecarDeleted(resource *apiv1.ReplicationController, restic *api.Restic) (err error) {
if c.options.EnableRBAC {
err := c.ensureRoleBindingDeleted(resource.ObjectMeta)
if err != nil {
return err
}
}

resource, err = core_util.PatchRC(c.k8sClient, resource, func(obj *apiv1.ReplicationController) *apiv1.ReplicationController {
obj.Spec.Template.Spec.Containers = core_util.EnsureContainerDeleted(obj.Spec.Template.Spec.Containers, util.StashContainer)
obj.Spec.Template.Spec.Volumes = util.EnsureVolumeDeleted(obj.Spec.Template.Spec.Volumes, util.ScratchDirVolumeName)
Expand Down
14 changes: 14 additions & 0 deletions pkg/controller/replicasets.go
Expand Up @@ -160,6 +160,13 @@ func (c *StashController) EnsureReplicaSetSidecar(resource *extensions.ReplicaSe
return err
}

if c.options.EnableRBAC {
err := c.ensureRoleBinding(kutil.GetObjectReference(resource, extensions.SchemeGroupVersion), resource.Spec.Template.Spec.ServiceAccountName)
if err != nil {
return err
}
}

resource, err = ext_util.PatchReplicaSet(c.k8sClient, resource, func(obj *extensions.ReplicaSet) *extensions.ReplicaSet {
obj.Spec.Template.Spec.Containers = core_util.UpsertContainer(obj.Spec.Template.Spec.Containers, util.CreateSidecarContainer(new, c.options.SidecarImageTag, "ReplicaSet/"+obj.Name))
obj.Spec.Template.Spec.Volumes = util.UpsertScratchVolume(obj.Spec.Template.Spec.Volumes)
Expand Down Expand Up @@ -196,6 +203,13 @@ func (c *StashController) EnsureReplicaSetSidecar(resource *extensions.ReplicaSe
}

func (c *StashController) EnsureReplicaSetSidecarDeleted(resource *extensions.ReplicaSet, restic *api.Restic) (err error) {
if c.options.EnableRBAC {
err := c.ensureRoleBindingDeleted(resource.ObjectMeta)
if err != nil {
return err
}
}

resource, err = ext_util.PatchReplicaSet(c.k8sClient, resource, func(obj *extensions.ReplicaSet) *extensions.ReplicaSet {
obj.Spec.Template.Spec.Containers = core_util.EnsureContainerDeleted(obj.Spec.Template.Spec.Containers, util.StashContainer)
obj.Spec.Template.Spec.Volumes = util.EnsureVolumeDeleted(obj.Spec.Template.Spec.Volumes, util.ScratchDirVolumeName)
Expand Down
14 changes: 14 additions & 0 deletions pkg/controller/statefulsets.go
Expand Up @@ -155,6 +155,13 @@ func (c *StashController) EnsureStatefulSetSidecar(resource *apps.StatefulSet, o
return
}

if c.options.EnableRBAC {
err := c.ensureRoleBinding(kutil.GetObjectReference(resource, apps.SchemeGroupVersion), resource.Spec.Template.Spec.ServiceAccountName)
if err != nil {
return err
}
}

resource, err = apps_util.PatchStatefulSet(c.k8sClient, resource, func(obj *apps.StatefulSet) *apps.StatefulSet {
obj.Spec.Template.Spec.Containers = core_util.UpsertContainer(obj.Spec.Template.Spec.Containers, util.CreateSidecarContainer(new, c.options.SidecarImageTag, "StatefulSet/"+obj.Name))
obj.Spec.Template.Spec.Volumes = util.UpsertScratchVolume(obj.Spec.Template.Spec.Volumes)
Expand Down Expand Up @@ -191,6 +198,13 @@ func (c *StashController) EnsureStatefulSetSidecar(resource *apps.StatefulSet, o
}

func (c *StashController) EnsureStatefulSetSidecarDeleted(resource *apps.StatefulSet, restic *api.Restic) (err error) {
if c.options.EnableRBAC {
err := c.ensureRoleBindingDeleted(resource.ObjectMeta)
if err != nil {
return err
}
}

resource, err = apps_util.PatchStatefulSet(c.k8sClient, resource, func(obj *apps.StatefulSet) *apps.StatefulSet {
obj.Spec.Template.Spec.Containers = core_util.EnsureContainerDeleted(obj.Spec.Template.Spec.Containers, util.StashContainer)
obj.Spec.Template.Spec.Volumes = util.EnsureVolumeDeleted(obj.Spec.Template.Spec.Volumes, util.ScratchDirVolumeName)
Expand Down

0 comments on commit 054813d

Please sign in to comment.