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
Merged
30 changes: 30 additions & 0 deletions NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,36 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--------------------------------------------------------------------------------
Module : github.com/gobuffalo/flect
Version : v0.1.5
Time : 2019-06-13 21:51:46 +0000 UTC

Contents of probable licence file $GOMODCACHE/github.com/gobuffalo/flect@v0.1.5/LICENSE:

The MIT License (MIT)

Copyright (c) 2019 Mark Bates

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--------------------------------------------------------------------------------
Module : github.com/google/go-cmp
Version : v0.3.1
Expand Down
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"

EnforceRbacOnRefs = "enforce-rbac-on-refs"

OperatorNamespaceFlag = "operator-namespace"

ManageWebhookCertsFlag = "manage-webhook-certs"
Expand Down Expand Up @@ -142,6 +145,11 @@ func init() {
"",
"K8s namespace the operator runs in",
)
Cmd.Flags().Bool(
EnforceRbacOnRefs,
false, // Set to false for backward compatibility
"Restrict cross-namespace resource association through RBAC (eg. referencing Elasticsearch from Kibana)",
)
Cmd.Flags().String(
WebhookSecretFlag,
"",
Expand Down Expand Up @@ -302,6 +310,15 @@ func execute() {
setupWebhook(mgr, params.CertRotation, clientset)
}

enforceRbacOnRefs := viper.GetBool(EnforceRbacOnRefs)

var accessReviewer rbac.AccessReviewer
if enforceRbacOnRefs {
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 @@ -315,11 +332,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
15 changes: 15 additions & 0 deletions config/crds/all-crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,11 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is used to check access from the current
resource to a resource (eg. Elasticsearch) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
type: string
version:
description: Version of the APM Server.
type: string
Expand Down Expand Up @@ -1075,6 +1080,11 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is used to check access from the current
resource to a resource (eg. a remote Elasticsearch cluster) in a different
namespace. Can only be used if ECK is enforcing RBAC on references.
type: string
updateStrategy:
description: UpdateStrategy specifies how updates to the cluster should
be performed.
Expand Down Expand Up @@ -1505,6 +1515,11 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is used to check access from the current
resource to a resource (eg. Elasticsearch) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
type: string
version:
description: Version of Kibana.
type: string
Expand Down
5 changes: 5 additions & 0 deletions config/crds/bases/apm.k8s.elastic.co_apmservers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6183,6 +6183,11 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is used to check access from the current
resource to a resource (eg. Elasticsearch) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
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 @@ -6896,6 +6896,12 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is used to check access from the current
resource to a resource (eg. a remote Elasticsearch cluster) in a
different namespace. Can only be used if ECK is enforcing RBAC on
references.
type: string
updateStrategy:
description: UpdateStrategy specifies how updates to the cluster should
be performed.
Expand Down
5 changes: 5 additions & 0 deletions config/crds/bases/kibana.k8s.elastic.co_kibanas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6181,6 +6181,11 @@ spec:
- secretName
type: object
type: array
serviceAccountName:
description: ServiceAccountName is used to check access from the current
resource to a resource (eg. Elasticsearch) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
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 gives the permission to Kibana to access the Elasticsearch cluster
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 gives the permission to ApmServer to access the Elasticsearch cluster
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 by the APM Server to get access to the Elasticsearch cluster
serviceAccountName: apmserver-user
30 changes: 30 additions & 0 deletions docs/api-docs.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ PodTemplate provides customisation options (labels, annotations, affinity rules,
*`secureSettings`* _xref:common-k8s-elastic-co-v1-secretsource[$$[]SecretSource$$]_::
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
*`serviceAccountName`* _string_::
_(Optional)_
ServiceAccountName is used to check access from the current resource to a resource (eg. Elasticsearch) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
|===

[id="apm-k8s-elastic-co-v1-apmserverspec"]
Expand Down Expand Up @@ -120,6 +124,12 @@ _xref:common-k8s-elastic-co-v1-secretsource[$$[]SecretSource$$]_
|
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
| *`serviceAccountName`* +
_string_
|
_(Optional)_
ServiceAccountName is used to check access from the current resource to a resource (eg. Elasticsearch) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
|===
[id="{p}-apm-k8s-elastic-co-v1beta1"]
=== apm.k8s.elastic.co/v1beta1
Expand Down Expand Up @@ -1119,6 +1129,10 @@ to the empty value (`{}` in YAML).
*`secureSettings`* _xref:common-k8s-elastic-co-v1-secretsource[$$[]SecretSource$$]_::
SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for Elasticsearch.
See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-es-secure-settings.html
*`serviceAccountName`* _string_::
_(Optional)_
ServiceAccountName is used to check access from the current resource to a resource (eg. a remote Elasticsearch cluster) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
|===

[id="elasticsearch-k8s-elastic-co-v1-changebudget"]
Expand Down Expand Up @@ -1197,6 +1211,12 @@ _xref:common-k8s-elastic-co-v1-secretsource[$$[]SecretSource$$]_
|
SecureSettings is a list of references to Kubernetes secrets containing sensitive configuration options for Elasticsearch.
See: https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-es-secure-settings.html
| *`serviceAccountName`* +
_string_
|
_(Optional)_
ServiceAccountName is used to check access from the current resource to a resource (eg. a remote Elasticsearch cluster) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
|===

[id="elasticsearch-k8s-elastic-co-v1-nodeset"]
Expand Down Expand Up @@ -1510,6 +1530,10 @@ PodTemplate provides customisation options (labels, annotations, affinity rules,
*`secureSettings`* _xref:common-k8s-elastic-co-v1-secretsource[$$[]SecretSource$$]_::
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
*`serviceAccountName`* _string_::
_(Optional)_
ServiceAccountName is used to check access from the current resource to a resource (eg. Elasticsearch) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
|===

[id="kibana-k8s-elastic-co-v1-kibanaspec"]
Expand Down Expand Up @@ -1560,6 +1584,12 @@ _xref:common-k8s-elastic-co-v1-secretsource[$$[]SecretSource$$]_
|
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
| *`serviceAccountName`* +
_string_
|
_(Optional)_
ServiceAccountName is used to check access from the current resource to a resource (eg. Elasticsearch) in a different namespace.
Can only be used if ECK is enforcing RBAC on references.
|===
[id="{p}-kibana-k8s-elastic-co-v1beta1"]
=== kibana.k8s.elastic.co/v1beta1
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/go-logr/logr v0.1.0
github.com/go-test/deep v1.0.3
github.com/gobuffalo/flect v0.1.5
github.com/gogo/protobuf v1.3.1 // indirect
github.com/golang/groupcache v0.0.0-20191002201903-404acd9df4cc // indirect
github.com/google/go-cmp v0.3.1
Expand Down
Loading