From bc6a6674f749efc1693c4b6bd58a27f8c37a0ae0 Mon Sep 17 00:00:00 2001 From: Krzysztof Opasiak Date: Mon, 23 Nov 2020 22:02:51 +0100 Subject: [MSB] Make consul run as non-root Start whole consul container as non-root and ensure that both dumb-init and consul are able to start and run as non-root. Issue-ID: REQ-362 Change-Id: If5a737953122cc6cd22b8d43ac603b40f4b22727 Signed-off-by: Krzysztof Opasiak --- .../msb-consul/resources/docker-entrypoint.sh | 100 +++++++++++++++++++++ .../msb/charts/msb-consul/templates/configmap.yaml | 27 ++++++ .../charts/msb-consul/templates/deployment.yaml | 17 ++++ kubernetes/msb/charts/msb-consul/values.yaml | 5 ++ 4 files changed, 149 insertions(+) create mode 100755 kubernetes/msb/charts/msb-consul/resources/docker-entrypoint.sh create mode 100644 kubernetes/msb/charts/msb-consul/templates/configmap.yaml (limited to 'kubernetes/msb/charts/msb-consul') diff --git a/kubernetes/msb/charts/msb-consul/resources/docker-entrypoint.sh b/kubernetes/msb/charts/msb-consul/resources/docker-entrypoint.sh new file mode 100755 index 0000000000..0cd46167e4 --- /dev/null +++ b/kubernetes/msb/charts/msb-consul/resources/docker-entrypoint.sh @@ -0,0 +1,100 @@ +#!/usr/bin/dumb-init /bin/sh +set -e +set -x + +# Note above that we run dumb-init as PID 1 in order to reap zombie processes +# as well as forward signals to all processes in its session. Normally, sh +# wouldn't do either of these functions so we'd leak zombies as well as do +# unclean termination of all our sub-processes. +# As of docker 1.13, using docker run --init achieves the same outcome. + +# You can set CONSUL_BIND_INTERFACE to the name of the interface you'd like to +# bind to and this will look up the IP and pass the proper -bind= option along +# to Consul. +CONSUL_BIND= +if [ -n "$CONSUL_BIND_INTERFACE" ]; then + CONSUL_BIND_ADDRESS=$(ip -o -4 addr list $CONSUL_BIND_INTERFACE | head -n1 | awk '{print $4}' | cut -d/ -f1) + if [ -z "$CONSUL_BIND_ADDRESS" ]; then + echo "Could not find IP for interface '$CONSUL_BIND_INTERFACE', exiting" + exit 1 + fi + + CONSUL_BIND="-bind=$CONSUL_BIND_ADDRESS" + echo "==> Found address '$CONSUL_BIND_ADDRESS' for interface '$CONSUL_BIND_INTERFACE', setting bind option..." +fi + +# You can set CONSUL_CLIENT_INTERFACE to the name of the interface you'd like to +# bind client intefaces (HTTP, DNS, and RPC) to and this will look up the IP and +# pass the proper -client= option along to Consul. +CONSUL_CLIENT= +if [ -n "$CONSUL_CLIENT_INTERFACE" ]; then + CONSUL_CLIENT_ADDRESS=$(ip -o -4 addr list $CONSUL_CLIENT_INTERFACE | head -n1 | awk '{print $4}' | cut -d/ -f1) + if [ -z "$CONSUL_CLIENT_ADDRESS" ]; then + echo "Could not find IP for interface '$CONSUL_CLIENT_INTERFACE', exiting" + exit 1 + fi + + CONSUL_CLIENT="-client=$CONSUL_CLIENT_ADDRESS" + echo "==> Found address '$CONSUL_CLIENT_ADDRESS' for interface '$CONSUL_CLIENT_INTERFACE', setting client option..." +fi + +# CONSUL_DATA_DIR is exposed as a volume for possible persistent storage. The +# CONSUL_CONFIG_DIR isn't exposed as a volume but you can compose additional +# config files in there if you use this image as a base, or use CONSUL_LOCAL_CONFIG +# below. +CONSUL_DATA_DIR=/consul/data +CONSUL_CONFIG_DIR=/consul/config + +# You can also set the CONSUL_LOCAL_CONFIG environemnt variable to pass some +# Consul configuration JSON without having to bind any volumes. +if [ -n "$CONSUL_LOCAL_CONFIG" ]; then + echo "$CONSUL_LOCAL_CONFIG" > "$CONSUL_CONFIG_DIR/local.json" +fi + +# If the user is trying to run Consul directly with some arguments, then +# pass them to Consul. +if [ "${1:0:1}" = '-' ]; then + set -- consul "$@" +fi + +# Look for Consul subcommands. +if [ "$1" = 'agent' ]; then + shift + set -- consul agent \ + -data-dir="$CONSUL_DATA_DIR" \ + -config-dir="$CONSUL_CONFIG_DIR" \ + $CONSUL_BIND \ + $CONSUL_CLIENT \ + "$@" +elif [ "$1" = 'version' ]; then + # This needs a special case because there's no help output. + set -- consul "$@" +elif consul --help "$1" 2>&1 | grep -q "consul $1"; then + # We can't use the return code to check for the existence of a subcommand, so + # we have to use grep to look for a pattern in the help output. + set -- consul "$@" +fi + +# If we are running Consul, make sure it executes as the proper user. +if [ "$1" = 'consul' ]; then + # If the data or config dirs are bind mounted then chown them. + # Note: This checks for root ownership as that's the most common case. + if [ "$(stat -c %u /consul/data)" != "$(id -u consul)" ]; then + chown consul:consul /consul/data + fi + if [ "$(stat -c %u /consul/config)" != "$(id -u consul)" ]; then + chown consul:consul /consul/config + fi + + # If requested, set the capability to bind to privileged ports before + # we drop to the non-root user. Note that this doesn't work with all + # storage drivers (it won't work with AUFS). + if [ ! -z ${CONSUL_ALLOW_PRIVILEGED_PORTS+x} ]; then + setcap "cap_net_bind_service=+ep" /bin/consul + fi + +# Instead of using this we run our pod as a non-root user. +# set -- su-exec consul:consul "$@" +fi + +exec "$@" diff --git a/kubernetes/msb/charts/msb-consul/templates/configmap.yaml b/kubernetes/msb/charts/msb-consul/templates/configmap.yaml new file mode 100644 index 0000000000..32adcaec5f --- /dev/null +++ b/kubernetes/msb/charts/msb-consul/templates/configmap.yaml @@ -0,0 +1,27 @@ +{{/* +# Copyright © 2020 Samsung Electronics +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +*/}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.fullname" . }}-entrypoint + namespace: {{ include "common.namespace" . }} + labels: + app: {{ include "common.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ include "common.release" . }} + heritage: {{ .Release.Service }} +data: +{{ tpl (.Files.Glob "resources/*").AsConfig . | indent 2 }} diff --git a/kubernetes/msb/charts/msb-consul/templates/deployment.yaml b/kubernetes/msb/charts/msb-consul/templates/deployment.yaml index 2639a8ed36..c7472cca72 100644 --- a/kubernetes/msb/charts/msb-consul/templates/deployment.yaml +++ b/kubernetes/msb/charts/msb-consul/templates/deployment.yaml @@ -41,6 +41,16 @@ spec: - name: {{ include "common.name" . }} image: "{{ .Values.global.dockerHubRepository | default .Values.dockerHubRepository }}/{{ .Values.image }}" imagePullPolicy: {{ .Values.global.pullPolicy | default .Values.pullPolicy }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + runAsGroup: {{ .Values.securityContext.runAsGroup }} + command: + - docker-entrypoint.sh + args: + - "agent" + - "-dev" + - "-client" + - "0.0.0.0" ports: - containerPort: {{ .Values.service.internalPort }} # disable liveness probe when breakpoints set in debugger @@ -62,6 +72,9 @@ spec: - mountPath: /etc/localtime name: localtime readOnly: true + - mountPath: /usr/local/bin/docker-entrypoint.sh + name: entrypoint + subPath: docker-entrypoint.sh resources: {{ include "common.resources" . | indent 12 }} {{- if .Values.nodeSelector }} @@ -76,5 +89,9 @@ spec: - name: localtime hostPath: path: /etc/localtime + - name: entrypoint + configMap: + name: {{ include "common.fullname" . }}-entrypoint + defaultMode: 0777 imagePullSecrets: - name: "{{ include "common.namespace" . }}-docker-registry-key" diff --git a/kubernetes/msb/charts/msb-consul/values.yaml b/kubernetes/msb/charts/msb-consul/values.yaml index e87144c87b..4704f3b24d 100644 --- a/kubernetes/msb/charts/msb-consul/values.yaml +++ b/kubernetes/msb/charts/msb-consul/values.yaml @@ -83,3 +83,8 @@ resources: cpu: 20m memory: 100Mi unlimited: {} + +securityContext: + fsGroup: 1000 + runAsUser: 100 + runAsGroup: 1000 -- cgit 1.2.3-korg