From f3b29fc7043dfc47ab7a0899e097ba30d2932419 Mon Sep 17 00:00:00 2001 From: Gerd Oberlechner Date: Wed, 16 Feb 2022 22:23:40 +0100 Subject: [PATCH] RBAC manifest creation for Hosted Cluster admin personas (#1014) * RBAC manifest creation for Hosted Cluster admin personas introduce RBAC artifact generation for hypershift admin personas / services those artifacts are generated when the `--enable-admin-rbac-generation` flag is provided during `hypershift install` persona: hypershift-client used by a client leveraging hypershift as a service to create hosted clusters * hypershift-client ClusterRole full permissions on the hostedcluster and nodepool CRs * hypershift-client ServiceAccount * hypershift-client ClusterRoleBinding binds the hypershift-client ClusterRole to hypershift-client SA and Group Ref the groups itself must be provided by other means persona: hypershift-reader used by admins to investigate hosted clusters and the hypershift operator. * hypershift-reader ClusterRole same permission subjects as the hypershift-operator ClusterRole but restricted to get, list, watch. access to secrets is not granted * hypershift-reader ClusterRoleBinding binds the hypershift-reader ClusterRole to a Group called hypershift-readers the group itself must be provided by other means Refs: * https://issues.redhat.com/browse/HOSTEDCP-306 * https://issues.redhat.com/browse/APPSRE-4335 Signed-off-by: Gerd Oberlechner * renamed hypershift-reader -> hypershift-readers * list hypershift-readers clusterrole permissions explicitely instead of copy+modify the hypershift-operator cluster role, list the permissions required for the hypershift-readers cluster role explicitely Signed-off-by: Gerd Oberlechner --- Makefile | 1 + cmd/install/assets/hypershift_operator.go | 216 ++++++++++++++++++++++ cmd/install/install.go | 30 +++ hack/app-sre/saas_template.yaml | 188 +++++++++++++++++++ 4 files changed, 435 insertions(+) diff --git a/Makefile b/Makefile index fa428d00f4..45561d434d 100644 --- a/Makefile +++ b/Makefile @@ -148,6 +148,7 @@ app-sre-saas-template: hypershift --oidc-storage-provider-s3-secret-key=credentials \ --enable-ocp-cluster-monitoring=false \ --enable-ci-debug-output=false \ + --enable-admin-rbac-generation=true \ render --template --format yaml > $(DIR)/hack/app-sre/saas_template.yaml # Run tests diff --git a/cmd/install/assets/hypershift_operator.go b/cmd/install/assets/hypershift_operator.go index 4b69b2bf7f..2b949d6b66 100644 --- a/cmd/install/assets/hypershift_operator.go +++ b/cmd/install/assets/hypershift_operator.go @@ -725,3 +725,219 @@ func (r HypershiftRecordingRule) Build() *prometheusoperatorv1.PrometheusRule { rule.Spec = recordingRuleSpec() return rule } + +type HyperShiftClientClusterRole struct{} + +func (o HyperShiftClientClusterRole) Build() *rbacv1.ClusterRole { + role := &rbacv1.ClusterRole{ + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterRole", + APIVersion: rbacv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "hypershift-client", + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{"hypershift.openshift.io"}, + Resources: []string{"hostedclusters", "nodepools"}, + Verbs: []string{"*"}, + }, + }, + } + return role +} + +type HyperShiftClientServiceAccount struct { + Namespace *corev1.Namespace +} + +func (o HyperShiftClientServiceAccount) Build() *corev1.ServiceAccount { + sa := &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + APIVersion: corev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: o.Namespace.Name, + Name: "hypershift-client", + }, + } + return sa +} + +type HyperShiftClientClusterRoleBinding struct { + ClusterRole *rbacv1.ClusterRole + ServiceAccount *corev1.ServiceAccount + GroupName string +} + +func (o HyperShiftClientClusterRoleBinding) Build() *rbacv1.ClusterRoleBinding { + binding := &rbacv1.ClusterRoleBinding{ + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterRoleBinding", + APIVersion: rbacv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "hypershift-client", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: o.ClusterRole.Name, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: o.ServiceAccount.Name, + Namespace: o.ServiceAccount.Namespace, + }, + { + Kind: "Group", + APIGroup: "rbac.authorization.k8s.io", + Name: o.GroupName, + }, + }, + } + return binding +} + +type HyperShiftReaderClusterRole struct{} + +func (o HyperShiftReaderClusterRole) Build() *rbacv1.ClusterRole { + role := &rbacv1.ClusterRole{ + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterRole", + APIVersion: rbacv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "hypershift-readers", + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{"hypershift.openshift.io"}, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"config.openshift.io"}, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"apiextensions.k8s.io"}, + Resources: []string{"customresourcedefinitions"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"networking.k8s.io"}, + Resources: []string{"networkpolicies"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{ + "bootstrap.cluster.x-k8s.io", + "controlplane.cluster.x-k8s.io", + "infrastructure.cluster.x-k8s.io", + "machines.cluster.x-k8s.io", + "exp.infrastructure.cluster.x-k8s.io", + "addons.cluster.x-k8s.io", + "exp.cluster.x-k8s.io", + "cluster.x-k8s.io", + }, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"operator.openshift.io"}, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"route.openshift.io"}, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"security.openshift.io"}, + Resources: []string{"securitycontextconstraints"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{""}, + Resources: []string{ + "events", + "configmaps", + "pods", + "pods/log", + "nodes", + "namespaces", + "serviceaccounts", + "services", + }, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"apps"}, + Resources: []string{"deployments"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"etcd.database.coreos.com"}, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"machine.openshift.io"}, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"monitoring.coreos.com"}, + Resources: []string{"podmonitors"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{"capi-provider.agent-install.openshift.io"}, + Resources: []string{"*"}, + Verbs: []string{"get", "list", "watch"}, + }, + }, + } + return role +} + +type HyperShiftReaderClusterRoleBinding struct { + ClusterRole *rbacv1.ClusterRole + GroupName string +} + +func (o HyperShiftReaderClusterRoleBinding) Build() *rbacv1.ClusterRoleBinding { + binding := &rbacv1.ClusterRoleBinding{ + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterRoleBinding", + APIVersion: rbacv1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "hypershift-readers", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: o.ClusterRole.Name, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "Group", + APIGroup: "rbac.authorization.k8s.io", + Name: o.GroupName, + }, + }, + } + return binding +} diff --git a/cmd/install/install.go b/cmd/install/install.go index 945416fc24..050d6b161e 100644 --- a/cmd/install/install.go +++ b/cmd/install/install.go @@ -55,6 +55,7 @@ type Options struct { OIDCStorageProviderS3Credentials string OIDCStorageProviderS3CredentialsSecret string OIDCStorageProviderS3CredentialsSecretKey string + EnableAdminRBACGeneration bool } func (o *Options) Validate() error { @@ -120,6 +121,7 @@ func NewCommand() *cobra.Command { cmd.PersistentFlags().StringVar(&opts.OIDCStorageProviderS3Credentials, "oidc-storage-provider-s3-credentials", opts.OIDCStorageProviderS3Credentials, "Credentials to use for writing the OIDC documents into the S3 bucket. Required for AWS guest clusters") cmd.PersistentFlags().StringVar(&opts.OIDCStorageProviderS3CredentialsSecret, "oidc-storage-provider-s3-secret", "", "Name of an existing secret containing the OIDC S3 credentials.") cmd.PersistentFlags().StringVar(&opts.OIDCStorageProviderS3CredentialsSecretKey, "oidc-storage-provider-s3-secret-key", "credentials", "Name of the secret key containing the OIDC S3 credentials.") + cmd.PersistentFlags().BoolVar(&opts.EnableAdminRBACGeneration, "enable-admin-rbac-generation", false, "Generate RBAC manifests for hosted cluster admins") cmd.RunE = func(cmd *cobra.Command, args []string) error { opts.ApplyDefaults() @@ -306,5 +308,33 @@ func hyperShiftOperatorManifests(opts Options) ([]crclient.Object, error) { return true })...) + if opts.EnableAdminRBACGeneration { + // hypershift-client admin persona for hostedclusters and nodepools creation + clientClusterRole := assets.HyperShiftClientClusterRole{}.Build() + objects = append(objects, clientClusterRole) + + clientServiceAccount := assets.HyperShiftClientServiceAccount{ + Namespace: operatorNamespace, + }.Build() + objects = append(objects, clientServiceAccount) + + clientRoleBinding := assets.HyperShiftClientClusterRoleBinding{ + ClusterRole: clientClusterRole, + ServiceAccount: clientServiceAccount, + GroupName: "hypershift-client", + }.Build() + objects = append(objects, clientRoleBinding) + + // hypershift-reader admin persona for inspecting hosted controlplanes and the operator + readerClusterRole := assets.HyperShiftReaderClusterRole{}.Build() + objects = append(objects, readerClusterRole) + + readerRoleBinding := assets.HyperShiftReaderClusterRoleBinding{ + ClusterRole: readerClusterRole, + GroupName: "hypershift-readers", + }.Build() + objects = append(objects, readerRoleBinding) + } + return objects, nil } diff --git a/hack/app-sre/saas_template.yaml b/hack/app-sre/saas_template.yaml index 7e0a5503a6..5b6aa9cda3 100644 --- a/hack/app-sre/saas_template.yaml +++ b/hack/app-sre/saas_template.yaml @@ -26834,6 +26834,194 @@ objects: plural: "" conditions: [] storedVersions: [] +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + creationTimestamp: null + name: hypershift-client + rules: + - apiGroups: + - hypershift.openshift.io + resources: + - hostedclusters + - nodepools + verbs: + - '*' +- apiVersion: v1 + kind: ServiceAccount + metadata: + creationTimestamp: null + name: hypershift-client + namespace: ${NAMESPACE} +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + creationTimestamp: null + name: hypershift-client + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: hypershift-client + subjects: + - kind: ServiceAccount + name: hypershift-client + namespace: ${NAMESPACE} + - apiGroup: rbac.authorization.k8s.io + kind: Group + name: hypershift-client +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRole + metadata: + creationTimestamp: null + name: hypershift-readers + rules: + - apiGroups: + - hypershift.openshift.io + resources: + - '*' + verbs: + - get + - list + - watch + - apiGroups: + - config.openshift.io + resources: + - '*' + verbs: + - get + - list + - watch + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - networkpolicies + verbs: + - get + - list + - watch + - apiGroups: + - bootstrap.cluster.x-k8s.io + - controlplane.cluster.x-k8s.io + - infrastructure.cluster.x-k8s.io + - machines.cluster.x-k8s.io + - exp.infrastructure.cluster.x-k8s.io + - addons.cluster.x-k8s.io + - exp.cluster.x-k8s.io + - cluster.x-k8s.io + resources: + - '*' + verbs: + - get + - list + - watch + - apiGroups: + - operator.openshift.io + resources: + - '*' + verbs: + - get + - list + - watch + - apiGroups: + - route.openshift.io + resources: + - '*' + verbs: + - get + - list + - watch + - apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + verbs: + - get + - list + - watch + - apiGroups: + - rbac.authorization.k8s.io + resources: + - '*' + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - events + - configmaps + - pods + - pods/log + - nodes + - namespaces + - serviceaccounts + - services + verbs: + - get + - list + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - watch + - apiGroups: + - etcd.database.coreos.com + resources: + - '*' + verbs: + - get + - list + - watch + - apiGroups: + - machine.openshift.io + resources: + - '*' + verbs: + - get + - list + - watch + - apiGroups: + - monitoring.coreos.com + resources: + - podmonitors + verbs: + - get + - list + - watch + - apiGroups: + - capi-provider.agent-install.openshift.io + resources: + - '*' + verbs: + - get + - list + - watch +- apiVersion: rbac.authorization.k8s.io/v1 + kind: ClusterRoleBinding + metadata: + creationTimestamp: null + name: hypershift-readers + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: hypershift-readers + subjects: + - apiGroup: rbac.authorization.k8s.io + kind: Group + name: hypershift-readers parameters: - name: OPERATOR_IMG value: quay.io/hypershift/hypershift-operator