diff --git a/elasticsearch/README.md b/elasticsearch/README.md index 38b48131a..1e622a627 100644 --- a/elasticsearch/README.md +++ b/elasticsearch/README.md @@ -139,6 +139,7 @@ support multiple versions with minimal changes. | `minimumMasterNodes` | The value for [discovery.zen.minimum_master_nodes][]. Should be set to `(master_eligible_nodes / 2) + 1`. Ignored in Elasticsearch versions >= 7 | `2` | | `nameOverride` | Overrides the `clusterName` when used in the naming of resources | `""` | | `networkHost` | Value for the [network.host Elasticsearch setting][] | `0.0.0.0` | +| `networkPolicy` | The [NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/) to set. See [`values.yaml`](./values.yaml) for an example | `{http.enabled: false,transport.enabled: false}`| | `nodeAffinity` | Value for the [node affinity settings][] | `{}` | | `nodeGroup` | This is the name that will be used for each group of nodes in the cluster. The name will be `clusterName-nodeGroup-X` , `nameOverride-nodeGroup-X` if a `nameOverride` is specified, and `fullnameOverride-X` if a `fullnameOverride` is specified | `master` | | `nodeSelector` | Configurable [nodeSelector][] so that you can target specific nodes for your Elasticsearch cluster | `{}` | diff --git a/elasticsearch/examples/networkpolicy/Makefile b/elasticsearch/examples/networkpolicy/Makefile new file mode 100644 index 000000000..38dd40d3d --- /dev/null +++ b/elasticsearch/examples/networkpolicy/Makefile @@ -0,0 +1,13 @@ +default: test +include ../../../helpers/examples.mk + +RELEASE := helm-es-networkpolicy + +install: + helm upgrade --wait --timeout=600s --install $(RELEASE) --values ./values.yaml ../../ ; \ + +restart: + helm upgrade --set terminationGracePeriod=121 --wait --timeout=600s --install $(RELEASE) --values ./values.yaml ../../ ; \ + +purge: + helm del --purge $(RELEASE) diff --git a/elasticsearch/examples/networkpolicy/values.yml b/elasticsearch/examples/networkpolicy/values.yml new file mode 100644 index 000000000..2f8178529 --- /dev/null +++ b/elasticsearch/examples/networkpolicy/values.yml @@ -0,0 +1,37 @@ +networkPolicy: + http: + enabled: true + explicitNamespacesSelector: + # Accept from namespaces with all those different rules (from whitelisted Pods) + matchLabels: + role: frontend + matchExpressions: + - {key: role, operator: In, values: [frontend]} + additionalRules: + - podSelector: + matchLabels: + role: frontend + - podSelector: + matchExpressions: + - key: role + operator: In + values: + - frontend + transport: + enabled: true + allowExternal: true + explicitNamespacesSelector: + matchLabels: + role: frontend + matchExpressions: + - {key: role, operator: In, values: [frontend]} + additionalRules: + - podSelector: + matchLabels: + role: frontend + - podSelector: + matchExpressions: + - key: role + operator: In + values: + - frontend diff --git a/elasticsearch/templates/networkpolicy.yaml b/elasticsearch/templates/networkpolicy.yaml new file mode 100644 index 000000000..80c0c9ed4 --- /dev/null +++ b/elasticsearch/templates/networkpolicy.yaml @@ -0,0 +1,61 @@ +{{- if (or .Values.networkPolicy.http.enabled .Values.networkPolicy.transport.enabled) }} +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: {{ template "elasticsearch.uname" . }} + labels: + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} + chart: "{{ .Chart.Name }}" + app: "{{ template "elasticsearch.uname" . }}" +spec: + podSelector: + matchLabels: + app: "{{ template "elasticsearch.uname" . }}" + ingress: # Allow inbound connections + +{{- if .Values.networkPolicy.http.enabled }} + # For HTTP access + - ports: + - port: {{ .Values.httpPort }} + from: + # From authorized Pods (having the correct label) + - podSelector: + matchLabels: + {{ template "elasticsearch.uname" . }}-http-client: "true" +{{- with .Values.networkPolicy.http.explicitNamespacesSelector }} + # From authorized namespaces + namespaceSelector: +{{ toYaml . | indent 12 }} +{{- end }} +{{- with .Values.networkPolicy.transport.additionalRules }} + # Or from custom additional rules +{{ toYaml . | indent 8 }} +{{- end }} +{{- end }} + +{{- if .Values.networkPolicy.transport.enabled }} + # For transport access + - ports: + - port: {{ .Values.transportPort }} + from: + # From authorized Pods (having the correct label) + - podSelector: + matchLabels: + {{ template "elasticsearch.uname" . }}-transport-client: "true" +{{- with .Values.networkPolicy.transport.explicitNamespacesSelector }} + # From authorized namespaces + namespaceSelector: +{{ toYaml . | indent 12 }} +{{- end }} +{{- with .Values.networkPolicy.transport.additionalRules }} + # Or from custom additional rules +{{ toYaml . | indent 8 }} +{{- end }} + # Or from other ElasticSearch Pods + - podSelector: + matchLabels: + app: "{{ template "elasticsearch.uname" . }}" +{{- end }} + +{{- end }} diff --git a/elasticsearch/tests/elasticsearch_test.py b/elasticsearch/tests/elasticsearch_test.py index 435b316a0..9340eba9e 100755 --- a/elasticsearch/tests/elasticsearch_test.py +++ b/elasticsearch/tests/elasticsearch_test.py @@ -1367,3 +1367,97 @@ def test_hostaliases(): r = helm_template(config) hostAliases = r["statefulset"][uname]["spec"]["template"]["spec"]["hostAliases"] assert {"ip": "127.0.0.1", "hostnames": ["foo.local", "bar.local"]} in hostAliases + + +def test_network_policy(): + config = """ +networkPolicy: + http: + enabled: true + explicitNamespacesSelector: + # Accept from namespaces with all those different rules (from whitelisted Pods) + matchLabels: + role: frontend + matchExpressions: + - {key: role, operator: In, values: [frontend]} + additionalRules: + - podSelector: + matchLabels: + role: frontend + - podSelector: + matchExpressions: + - key: role + operator: In + values: + - frontend + transport: + enabled: true + allowExternal: true + explicitNamespacesSelector: + matchLabels: + role: frontend + matchExpressions: + - {key: role, operator: In, values: [frontend]} + additionalRules: + - podSelector: + matchLabels: + role: frontend + - podSelector: + matchExpressions: + - key: role + operator: In + values: + - frontend + +""" + r = helm_template(config) + ingress = r["networkpolicy"][uname]["spec"]["ingress"] + pod_selector = r["networkpolicy"][uname]["spec"]["podSelector"] + http = ingress[0] + transport = ingress[1] + assert http["from"] == [ + { + "podSelector": { + "matchLabels": {"elasticsearch-master-http-client": "true"} + }, + "namespaceSelector": { + "matchExpressions": [ + {"key": "role", "operator": "In", "values": ["frontend"]} + ], + "matchLabels": {"role": "frontend"}, + }, + }, + {"podSelector": {"matchLabels": {"role": "frontend"}}}, + { + "podSelector": { + "matchExpressions": [ + {"key": "role", "operator": "In", "values": ["frontend"]} + ] + } + }, + ] + assert http["ports"][0]["port"] == 9200 + assert transport["from"] == [ + { + "podSelector": { + "matchLabels": {"elasticsearch-master-transport-client": "true"} + }, + "namespaceSelector": { + "matchExpressions": [ + {"key": "role", "operator": "In", "values": ["frontend"]} + ], + "matchLabels": {"role": "frontend"}, + }, + }, + {"podSelector": {"matchLabels": {"role": "frontend"}}}, + { + "podSelector": { + "matchExpressions": [ + {"key": "role", "operator": "In", "values": ["frontend"]} + ] + } + }, + {"podSelector": {"matchLabels": {"app": "elasticsearch-master"}}}, + ] + assert transport["ports"][0]["port"] == 9300 + assert pod_selector == {"matchLabels": {"app": "elasticsearch-master",}} diff --git a/elasticsearch/values.yaml b/elasticsearch/values.yaml index ebdd04458..ab03d73eb 100755 --- a/elasticsearch/values.yaml +++ b/elasticsearch/values.yaml @@ -124,7 +124,7 @@ podSecurityPolicy: - secret - configMap - persistentVolumeClaim - - emptyDir + - emptyDir persistence: enabled: true @@ -283,6 +283,61 @@ sysctlInitContainer: keystore: [] +networkPolicy: + ## Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## In order for a Pod to access Elasticsearch, it needs to have the following label: + ## {{ template "uname" . }}-client: "true" + ## Example for default configuration to access HTTP port: + ## elasticsearch-master-http-client: "true" + ## Example for default configuration to access transport port: + ## elasticsearch-master-transport-client: "true" + + http: + enabled: false + ## if explicitNamespacesSelector is not set or set to {}, only client Pods being in the networkPolicy's namespace + ## and matching all criteria can reach the DB. + ## But sometimes, we want the Pods to be accessible to clients from other namespaces, in this case, we can use this + ## parameter to select these namespaces + ## + # explicitNamespacesSelector: + # # Accept from namespaces with all those different rules (only from whitelisted Pods) + # matchLabels: + # role: frontend + # matchExpressions: + # - {key: role, operator: In, values: [frontend]} + + ## Additional NetworkPolicy Ingress "from" rules to set. Note that all rules are OR-ed. + ## + # additionalRules: + # - podSelector: + # matchLabels: + # role: frontend + # - podSelector: + # matchExpressions: + # - key: role + # operator: In + # values: + # - frontend + + transport: + ## Note that all Elasticsearch Pods can talks to themselves using transport port even if enabled. + enabled: false + # explicitNamespacesSelector: + # matchLabels: + # role: frontend + # matchExpressions: + # - {key: role, operator: In, values: [frontend]} + # additionalRules: + # - podSelector: + # matchLabels: + # role: frontend + # - podSelector: + # matchExpressions: + # - key: role + # operator: In + # values: + # - frontend + # Deprecated # please use the above podSecurityContext.fsGroup instead fsGroup: ""