Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Control associations across namespaces with ServiceAccount and RBAC #2482

Merged
merged 15 commits into from
Feb 3, 2020
21 changes: 19 additions & 2 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import (
"github.com/elastic/cloud-on-k8s/pkg/dev/portforward"
licensing "github.com/elastic/cloud-on-k8s/pkg/license"
"github.com/elastic/cloud-on-k8s/pkg/utils/net"
"github.com/elastic/cloud-on-k8s/pkg/utils/rbac"
"k8s.io/apimachinery/pkg/util/wait"
)

Expand All @@ -63,6 +64,8 @@ const (
CertValidityFlag = "cert-validity"
CertRotateBeforeFlag = "cert-rotate-before"

RbacControlledReferencesFlag = "rbac-controlled-references"
barkbay marked this conversation as resolved.
Show resolved Hide resolved

OperatorNamespaceFlag = "operator-namespace"

ManageWebhookCertsFlag = "manage-webhook-certs"
Expand Down Expand Up @@ -141,6 +144,11 @@ func init() {
"",
"k8s namespace the operator runs in",
)
Cmd.Flags().Bool(
RbacControlledReferencesFlag,
false, // Set to false for backward compatibility
"enables role based access control for references on resources accross namespaces ",
)
Cmd.Flags().String(
WebhookSecretFlag,
"",
Expand Down Expand Up @@ -294,6 +302,15 @@ func execute() {
setupWebhook(mgr, params.CertRotation, clientset)
}

rbacControlledReferences := viper.GetBool(RbacControlledReferencesFlag)

var accessReviewer rbac.AccessReviewer
if rbacControlledReferences {
accessReviewer = rbac.NewSubjectAccessReviewer(clientset)
} else {
accessReviewer = rbac.NewPermissiveAccessReviewer()
}

if operator.HasRole(operator.NamespaceOperator, roles) {
if err = apmserver.Add(mgr, params); err != nil {
log.Error(err, "unable to create controller", "controller", "ApmServer")
Expand All @@ -307,11 +324,11 @@ func execute() {
log.Error(err, "unable to create controller", "controller", "Kibana")
os.Exit(1)
}
if err = asesassn.Add(mgr, params); err != nil {
if err = asesassn.Add(mgr, accessReviewer, params); err != nil {
log.Error(err, "unable to create controller", "controller", "ApmServerElasticsearchAssociation")
os.Exit(1)
}
if err = kbassn.Add(mgr, params); err != nil {
if err = kbassn.Add(mgr, accessReviewer, params); err != nil {
log.Error(err, "unable to create controller", "controller", "KibanaAssociation")
os.Exit(1)
}
Expand Down
12 changes: 12 additions & 0 deletions config/crds/all-crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is the name of the ServiceAccount to
use to check access to objects in different namespaces.
type: string
version:
description: Version of the APM Server.
type: string
Expand Down Expand Up @@ -1073,6 +1077,10 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is the name of the ServiceAccount to
use to check access to objects in different namespaces.
type: string
updateStrategy:
description: UpdateStrategy specifies how updates to the cluster should
be performed.
Expand Down Expand Up @@ -1502,6 +1510,10 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is the name of the ServiceAccount to
use to check access to objects in different namespaces.
type: string
version:
description: Version of Kibana.
type: string
Expand Down
4 changes: 4 additions & 0 deletions config/crds/bases/apm.k8s.elastic.co_apmservers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6050,6 +6050,10 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is the name of the ServiceAccount to
use to check access to objects in different namespaces.
type: string
version:
description: Version of the APM Server.
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6767,6 +6767,10 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is the name of the ServiceAccount to
use to check access to objects in different namespaces.
type: string
updateStrategy:
description: UpdateStrategy specifies how updates to the cluster should
be performed.
Expand Down
4 changes: 4 additions & 0 deletions config/crds/bases/kibana.k8s.elastic.co_kibanas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6049,6 +6049,10 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is the name of the ServiceAccount to
use to check access to objects in different namespaces.
type: string
version:
description: Version of Kibana.
type: string
Expand Down
6 changes: 6 additions & 0 deletions config/operator/all-in-one/cluster_role.template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ kind: ClusterRole
metadata:
name: elastic-operator
rules:
- apiGroups:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the cluster role of the all-in-one manifest is updated since this feature only makes sense when the operator is watching more than one namespace.

- "authorization.k8s.io"
resources:
- subjectaccessreviews
verbs:
- create
- apiGroups:
- ""
resources:
Expand Down
120 changes: 120 additions & 0 deletions config/samples/associations-rbac/apm_es_kibana_rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# This file contains an example of Roles, RoleBindings and ServiceAccount which allow the associations to be established
# between resources living in different namespaces if the access control between resources across namespaces is enabled.
# This example is only valid if ECK is started with the related option.
# See https://www.elastic.co/guide/en/cloud-on-k8s/master/k8s-operator-config.html.
---
apiVersion: v1
kind: Namespace
metadata:
name: kibana-ns
---
apiVersion: v1
kind: Namespace
metadata:
name: elasticsearch-ns
---
apiVersion: v1
kind: Namespace
metadata:
name: apmserver-ns
---
# Create a Role at the cluster level to access some Elasticsearch clusters.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: elasticsearch-association
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we should pre-create this role as part of the operator manifest?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My gut feeling is that we should create roles only if there are needed but add it as an example in our documentation if the user wants to enable access control. That being said I'm happy to pre-create it if you think it's worth it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 let's not include it for now and maybe do it later depending on users feedback.

rules:
- apiGroups:
- elasticsearch.k8s.elastic.co
resources:
- elasticsearches
# It is also possible to do some fine grain filtering with some per cluster roles
# resourceNames:
# - elasticsearch-sample
# - an-other-elasticsearch-cluster
verbs:
- get # association is allowed if a resource can "get" the remote one
---
# This is the service account used by Kibana
apiVersion: v1
kind: ServiceAccount
metadata:
name: kibana-user
namespace: kibana-ns
---
# This RoleBinding give the permission to Kibana to access the Elasticsearch cluster
barkbay marked this conversation as resolved.
Show resolved Hide resolved
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: allow-kibana-from-remote-namespace
namespace: elasticsearch-ns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: elasticsearch-association
subjects:
- kind: ServiceAccount
name: kibana-user
namespace: kibana-ns
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: apmserver-user
namespace: apmserver-ns
---
# This RoleBinding give the permission to Kibana to access the Elasticsearch cluster
barkbay marked this conversation as resolved.
Show resolved Hide resolved
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: allow-apmserver-from-remote-namespace
namespace: elasticsearch-ns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: elasticsearch-association
subjects:
- kind: ServiceAccount
name: apmserver-user
namespace: apmserver-ns
---
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: elasticsearch-sample
namespace: elasticsearch-ns
spec:
version: 7.5.2
nodeSets:
- name: default
count: 1
config:
node.store.allow_mmap: false
---
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
name: kibana-sample
namespace: kibana-ns
spec:
version: 7.5.2
count: 1
elasticsearchRef:
name: "elasticsearch-sample"
namespace: "elasticsearch-ns"
# Service account used by Kibana to get access to the Elasticsearch cluster
serviceAccountName: kibana-user
---
apiVersion: apm.k8s.elastic.co/v1
kind: ApmServer
metadata:
name: apm-apm-sample
namespace: apmserver-ns
spec:
version: 7.5.2
count: 1
elasticsearchRef:
name: "elasticsearch-sample"
namespace: "elasticsearch-ns"
# Service account used the APM Server to get access to the Elasticsearch cluster
barkbay marked this conversation as resolved.
Show resolved Hide resolved
serviceAccountName: apmserver-user
4 changes: 4 additions & 0 deletions pkg/apis/apm/v1/apmserver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ type ApmServerSpec struct {
// SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for APM Server.
// See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-apm-server.html#k8s-apm-secure-settings
SecureSettings []commonv1.SecretSource `json:"secureSettings,omitempty"`

// ServiceAccountName is the name of the ServiceAccount to use to check access to objects in different namespaces.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
barkbay marked this conversation as resolved.
Show resolved Hide resolved
}

// ApmServerHealth expresses the status of the Apm Server instances.
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/apm/v1beta1/apmserver_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ type ApmServerSpec struct {
// SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for APM Server.
// See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-apm-server.html#k8s-apm-secure-settings
SecureSettings []commonv1beta1.SecretSource `json:"secureSettings,omitempty"`

// ServiceAccountName is the name of the ServiceAccount to use to check access to objects in different namespaces.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// ApmServerHealth expresses the status of the Apm Server instances.
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/elasticsearch/v1/elasticsearch_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ type ElasticsearchSpec struct {
// See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-es-secure-settings.html
// +kubebuilder:validation:Optional
SecureSettings []commonv1.SecretSource `json:"secureSettings,omitempty"`

// ServiceAccountName is the name of the ServiceAccount to use to check access to objects in different namespaces.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// NodeCount returns the total number of nodes of the Elasticsearch cluster
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/elasticsearch/v1beta1/elasticsearch_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ type ElasticsearchSpec struct {
// See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-es-secure-settings.html
// +kubebuilder:validation:Optional
SecureSettings []commonv1beta1.SecretSource `json:"secureSettings,omitempty"`

// ServiceAccountName is the name of the ServiceAccount to use to check access to objects in different namespaces.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// NodeCount returns the total number of nodes of the Elasticsearch cluster
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/kibana/v1/kibana_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ type KibanaSpec struct {
// SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for Kibana.
// See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-kibana.html#k8s-kibana-secure-settings
SecureSettings []commonv1.SecretSource `json:"secureSettings,omitempty"`

// ServiceAccountName is the name of the ServiceAccount to use to check access to objects in different namespaces.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// KibanaHealth expresses the status of the Kibana instances.
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/kibana/v1beta1/kibana_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ type KibanaSpec struct {
// SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for Kibana.
// See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-kibana.html#k8s-kibana-secure-settings
SecureSettings []commonv1beta1.SecretSource `json:"secureSettings,omitempty"`

// ServiceAccountName is the name of the ServiceAccount to use to check access to objects in different namespaces.
// +optional
ServiceAccountName string `json:"serviceAccountName,omitempty"`
}

// KibanaHealth expresses the status of the Kibana instances.
Expand Down
Loading