diff --git a/doc/source/user/index.rst b/doc/source/user/index.rst
index 6af2ae083c..73734242b1 100644
--- a/doc/source/user/index.rst
+++ b/doc/source/user/index.rst
@@ -369,6 +369,10 @@ the table are linked to more details elsewhere in the user guide.
| `service_cluster_ip_range` | IPv4 CIDR for k8s | 10.254.0.0/16 |
| | service portals | |
+---------------------------------------+--------------------+---------------+
+| `keystone_auth_enabled`_ | see below | false |
++---------------------------------------+--------------------+---------------+
+| `k8s_keystone_auth_tag`_ | see below | see below |
++---------------------------------------+--------------------+---------------+
Cluster
-------
@@ -1183,6 +1187,16 @@ _`cloud_provider_enabled`
'volume_driver', it is implied that the cloud provider will be enabled since
they are combined.
+_`keystone_auth_enabled`
+ If this label is set to True, Kubernetes will support use Keystone for
+ authorization and authentication.
+
+_`k8s_keystone_auth_tag`
+ This label allows users to select `a specific k8s_keystone_auth
+ version, based on its container tag
+ `_.
+ Stein-default: 1.13.0
+
External load balancer for services
-----------------------------------
@@ -1253,6 +1267,39 @@ in a Cluster
`_
for more info.
+Keystone authN and authZ
+------------------------
+
+Now `cloud-provider-openstack
+`_
+provides a good webhook between OpenStack Keystone and Kubernetes, so that
+user can do authorization and authentication with a Keystone user/role against
+the Kubernetes cluster. If label `keystone-auth-enabled` is set True, then
+user can use their OpenStack credentials and roles to access resources in
+Kubernetes.
+
+Assume you have already got the configs with command
+`eval $(openstack coe cluster config )`, then to configure the
+kubectl client, the following commands are needed:
+
+1. Run `kubectl config set-credentials openstackuser --auth-provider=openstack`
+
+2. Run `kubectl config set-context --cluster=
+ --user=openstackuser openstackuser@kubernetes`
+
+3. Run `kubectl config use-context openstackuser@kubernetes` to activate the
+ context
+
+
+**NOTE:** Please make sure the version of kubectl is 1.8+ and make sure
+OS_DOMAIN_NAME is included in the rc file.
+
+Now try `kubectl get pods`, you should be able to see response from Kubernetes
+based on current user's role.
+
+Please refer the doc of `k8s-keystone-auth in cloud-provider-openstack
+`_
+for more information.
Swarm
=====
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
index 1caf605abc..ac7ffeab39 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
+++ b/magnum/drivers/common/templates/kubernetes/fragments/configure-kubernetes-master.sh
@@ -79,6 +79,36 @@ if [ -n "$TRUST_ID" ] && [ "$(echo "${CLOUD_PROVIDER_ENABLED}" | tr '[:upper:]'
KUBE_API_ARGS="$KUBE_API_ARGS --cloud-provider=external"
fi
+if [ "$KEYSTONE_AUTH_ENABLED" == "True" ]; then
+ KEYSTONE_WEBHOOK_CONFIG=/etc/kubernetes/keystone_webhook_config.yaml
+
+ [ -f ${KEYSTONE_WEBHOOK_CONFIG} ] || {
+echo "Writing File: $KEYSTONE_WEBHOOK_CONFIG"
+mkdir -p $(dirname ${KEYSTONE_WEBHOOK_CONFIG})
+cat << EOF > ${KEYSTONE_WEBHOOK_CONFIG}
+---
+apiVersion: v1
+kind: Config
+preferences: {}
+clusters:
+ - cluster:
+ insecure-skip-tls-verify: true
+ server: https://127.0.0.1:8443/webhook
+ name: webhook
+users:
+ - name: webhook
+contexts:
+ - context:
+ cluster: webhook
+ user: webhook
+ name: webhook
+current-context: webhook
+EOF
+}
+ KUBE_API_ARGS="$KUBE_API_ARGS --authentication-token-webhook-config-file=/etc/kubernetes/keystone_webhook_config.yaml --authorization-webhook-config-file=/etc/kubernetes/keystone_webhook_config.yaml"
+ webhook_auth="--authorization-mode=Node,Webhook,RBAC"
+ KUBE_API_ARGS=${KUBE_API_ARGS/--authorization-mode=Node,RBAC/$webhook_auth}
+fi
sed -i '
/^KUBE_API_ADDRESS=/ s/=.*/="'"${KUBE_API_ADDRESS}"'"/
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/enable-keystone-auth.sh b/magnum/drivers/common/templates/kubernetes/fragments/enable-keystone-auth.sh
new file mode 100644
index 0000000000..e65d77d90f
--- /dev/null
+++ b/magnum/drivers/common/templates/kubernetes/fragments/enable-keystone-auth.sh
@@ -0,0 +1,185 @@
+#!/bin/sh
+
+. /etc/sysconfig/heat-params
+
+step="enable-keystone-auth"
+printf "Starting to run ${step}\n"
+
+if [ "$(echo $KEYSTONE_AUTH_ENABLED | tr '[:upper:]' '[:lower:]')" != "false" ]; then
+ _prefix=${CONTAINER_INFRA_PREFIX:-docker.io/k8scloudprovider/}
+ CERT_DIR=/etc/kubernetes/certs
+
+ # Create policy configmap for keystone auth
+ KEYSTONE_AUTH_POLICY=/srv/magnum/kubernetes/keystone-auth-policy.yaml
+
+ [ -f ${KEYSTONE_AUTH_POLICY} ] || {
+ echo "Writing File: $KEYSTONE_AUTH_POLICY"
+ mkdir -p $(dirname ${KEYSTONE_AUTH_POLICY})
+ cat << EOF > ${KEYSTONE_AUTH_POLICY}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: k8s-keystone-auth
+ namespace: kube-system
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+ labels:
+ kubernetes.io/bootstrapping: rbac-defaults
+ name: system:k8s-keystone-auth
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ - services
+ - pods
+ verbs:
+ - get
+ - list
+ - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+ annotations:
+ rbac.authorization.kubernetes.io/autoupdate: "true"
+ labels:
+ kubernetes.io/bootstrapping: rbac-defaults
+ name: system:k8s-keystone-auth
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: system:k8s-keystone-auth
+subjects:
+- kind: ServiceAccount
+ name: k8s-keystone-auth
+ namespace: kube-system
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: k8s-keystone-auth-policy
+ namespace: kube-system
+data:
+ policies: |
+ [
+ {
+ "resource": {
+ "verbs": ["list"],
+ "resources": ["pods", "services", "deployments", "pvc"],
+ "version": "*",
+ "namespace": "default"
+ },
+ "match": [
+ {
+ "type": "role",
+ "values": ["member"]
+ },
+ {
+ "type": "project",
+ "values": ["$PROJECT_ID"]
+ }
+ ]
+ }
+ ]
+EOF
+ }
+
+ # Generate k8s-keystone-auth service manifest file
+ KEYSTONE_AUTH_DEPLOY=/srv/magnum/kubernetes/manifests/k8s-keystone-auth.yaml
+
+ [ -f ${KEYSTONE_AUTH_DEPLOY} ] || {
+ echo "Writing File: $KEYSTONE_AUTH_DEPLOY"
+ mkdir -p $(dirname ${KEYSTONE_AUTH_DEPLOY})
+ cat << EOF > ${KEYSTONE_AUTH_DEPLOY}
+---
+apiVersion: extensions/v1beta1
+kind: DaemonSet
+metadata:
+ labels:
+ component: k8s-keystone-auth
+ tier: control-plane
+ name: k8s-keystone-auth
+ namespace: kube-system
+spec:
+ # The controllers can only have a single active instance.
+ template:
+ metadata:
+ name: k8s-keystone-auth
+ namespace: kube-system
+ labels:
+ k8s-app: k8s-keystone-auth
+ spec:
+ serviceAccountName: k8s-keystone-auth
+ tolerations:
+ # Make sure the pod can be scheduled on master kubelet.
+ - effect: NoSchedule
+ operator: Exists
+ # Mark the pod as a critical add-on for rescheduling.
+ - key: CriticalAddonsOnly
+ operator: Exists
+ - effect: NoExecute
+ operator: Exists
+ nodeSelector:
+ node-role.kubernetes.io/master: ""
+ containers:
+ - name: k8s-keystone-auth
+ image: ${_prefix}k8s-keystone-auth:${K8S_KEYSTONE_AUTH_TAG}
+ imagePullPolicy: Always
+ args:
+ - ./bin/k8s-keystone-auth
+ - --tls-cert-file
+ - ${CERT_DIR}/server.crt
+ - --tls-private-key-file
+ - ${CERT_DIR}/server.key
+ - --policy-configmap-name
+ - k8s-keystone-auth-policy
+ - --keystone-url
+ - ${AUTH_URL}
+ - --keystone-ca-file
+ - /etc/kubernetes/ca-bundle.crt
+ - --listen
+ - 127.0.0.1:8443
+ volumeMounts:
+ - mountPath: ${CERT_DIR}
+ name: k8s-certs
+ readOnly: true
+ - mountPath: /etc/kubernetes
+ name: ca-certs
+ readOnly: true
+ resources:
+ requests:
+ cpu: 200m
+ ports:
+ - containerPort: 8443
+ hostPort: 8443
+ name: https
+ protocol: TCP
+ hostNetwork: true
+ volumes:
+ - hostPath:
+ path: ${CERT_DIR}
+ type: DirectoryOrCreate
+ name: k8s-certs
+ - hostPath:
+ path: /etc/kubernetes
+ type: DirectoryOrCreate
+ name: ca-certs
+EOF
+ }
+
+ until [ "ok" = "$(curl --silent http://127.0.0.1:8080/healthz)" ]
+ do
+ echo "Waiting for Kubernetes API..."
+ sleep 5
+ done
+
+ /usr/bin/kubectl apply -f ${KEYSTONE_AUTH_POLICY}
+ /usr/bin/kubectl apply -f ${KEYSTONE_AUTH_DEPLOY}
+
+fi
+
+printf "Finished running ${step}\n"
\ No newline at end of file
diff --git a/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.yaml b/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.yaml
index 44c0ea0710..89562f560b 100644
--- a/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.yaml
+++ b/magnum/drivers/common/templates/kubernetes/fragments/write-heat-params-master.yaml
@@ -78,3 +78,6 @@ write_files:
PROMETHEUS_TAG="$PROMETHEUS_TAG"
GRAFANA_TAG="$GRAFANA_TAG"
HEAT_CONTAINER_AGENT_TAG="$HEAT_CONTAINER_AGENT_TAG"
+ KEYSTONE_AUTH_ENABLED="$KEYSTONE_AUTH_ENABLED"
+ K8S_KEYSTONE_AUTH_TAG="$K8S_KEYSTONE_AUTH_TAG"
+ PROJECT_ID="$PROJECT_ID"
diff --git a/magnum/drivers/heat/k8s_fedora_template_def.py b/magnum/drivers/heat/k8s_fedora_template_def.py
index 33d27c9c17..b3720021b0 100644
--- a/magnum/drivers/heat/k8s_fedora_template_def.py
+++ b/magnum/drivers/heat/k8s_fedora_template_def.py
@@ -113,7 +113,8 @@ class K8sFedoraTemplateDefinition(k8s_template_def.K8sTemplateDefinition):
'cloud_provider_tag',
'prometheus_tag',
'grafana_tag',
- 'heat_container_agent_tag']
+ 'heat_container_agent_tag',
+ 'keystone_auth_enabled', 'k8s_keystone_auth_tag']
for label in label_list:
label_value = cluster.labels.get(label)
@@ -135,6 +136,8 @@ class K8sFedoraTemplateDefinition(k8s_template_def.K8sTemplateDefinition):
ca_cert.get_private_key(),
ca_cert.get_private_key_passphrase()).replace("\n", "\\n")
+ extra_params['project_id'] = cluster.project_id
+
return super(K8sFedoraTemplateDefinition,
self).get_params(context, cluster_template, cluster,
extra_params=extra_params,
diff --git a/magnum/drivers/k8s_fedora_atomic_v1/templates/kubecluster.yaml b/magnum/drivers/k8s_fedora_atomic_v1/templates/kubecluster.yaml
index 92c5243a5e..c4edbfafe3 100644
--- a/magnum/drivers/k8s_fedora_atomic_v1/templates/kubecluster.yaml
+++ b/magnum/drivers/k8s_fedora_atomic_v1/templates/kubecluster.yaml
@@ -519,6 +519,23 @@ parameters:
description: tag of the heat_container_agent system container
default: stein-dev
+ keystone_auth_enabled:
+ type: boolean
+ description: >
+ true if the keystone authN and authZ should be enabled
+ default:
+ false
+
+ k8s_keystone_auth_tag:
+ type: string
+ description: tag of the k8s_keystone_auth container
+ default: 1.13.0
+
+ project_id:
+ type: string
+ description: >
+ project id of current project
+
resources:
######################################################################
@@ -745,6 +762,9 @@ resources:
prometheus_tag: {get_param: prometheus_tag}
grafana_tag: {get_param: grafana_tag}
heat_container_agent_tag: {get_param: heat_container_agent_tag}
+ keystone_auth_enabled: {get_param: keystone_auth_enabled}
+ k8s_keystone_auth_tag: {get_param: k8s_keystone_auth_tag}
+ project_id: {get_param: project_id}
kube_cluster_config:
type: OS::Heat::SoftwareConfig
@@ -770,6 +790,7 @@ resources:
$enable-ingress-traefik: {get_file: ../../common/templates/kubernetes/fragments/enable-ingress-traefik.sh}
template: {get_file: ../../common/templates/kubernetes/fragments/enable-ingress-controller.sh}
- get_file: ../../common/templates/kubernetes/fragments/kube-dashboard-service.sh
+ - get_file: ../../common/templates/kubernetes/fragments/enable-keystone-auth.sh
kube_cluster_deploy:
type: OS::Heat::SoftwareDeployment
diff --git a/magnum/drivers/k8s_fedora_atomic_v1/templates/kubemaster.yaml b/magnum/drivers/k8s_fedora_atomic_v1/templates/kubemaster.yaml
index 4983c38b05..355cb32391 100644
--- a/magnum/drivers/k8s_fedora_atomic_v1/templates/kubemaster.yaml
+++ b/magnum/drivers/k8s_fedora_atomic_v1/templates/kubemaster.yaml
@@ -398,6 +398,22 @@ parameters:
type: string
description: tag of the heat_container_agent system container
+ keystone_auth_enabled:
+ type: boolean
+ description: >
+ true if the keystone authN and authZ should be enabled
+ default:
+ false
+
+ k8s_keystone_auth_tag:
+ type: string
+ description: tag of the k8s_keystone_auth container
+
+ project_id:
+ type: string
+ description: >
+ project id of current project
+
resources:
######################################################################
#
@@ -499,6 +515,9 @@ resources:
"$PROMETHEUS_TAG": {get_param: prometheus_tag}
"$GRAFANA_TAG": {get_param: grafana_tag}
"$HEAT_CONTAINER_AGENT_TAG": {get_param: heat_container_agent_tag}
+ "$KEYSTONE_AUTH_ENABLED": {get_param: keystone_auth_enabled}
+ "$K8S_KEYSTONE_AUTH_TAG": {get_param: k8s_keystone_auth_tag}
+ "$PROJECT_ID": {get_param: project_id}
install_openstack_ca:
type: OS::Heat::SoftwareConfig
diff --git a/magnum/tests/unit/conductor/handlers/test_k8s_cluster_conductor.py b/magnum/tests/unit/conductor/handlers/test_k8s_cluster_conductor.py
index 305e2c96e6..9c10ecbb01 100644
--- a/magnum/tests/unit/conductor/handlers/test_k8s_cluster_conductor.py
+++ b/magnum/tests/unit/conductor/handlers/test_k8s_cluster_conductor.py
@@ -112,6 +112,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'service_cluster_ip_range': '10.254.0.0/16'},
'master_flavor_id': 'master_flavor_id',
'flavor_id': 'flavor_id',
+ 'project_id': 'project_id',
}
self.context.user_name = 'fake_user'
self.context.project_id = 'fake_tenant'
@@ -290,6 +291,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'kube_service_account_key': 'public_key',
'kube_service_account_private_key': 'private_key',
'portal_network_cidr': '10.254.0.0/16',
+ 'project_id': 'project_id'
}
if missing_attr is not None:
expected.pop(mapping[missing_attr], None)
@@ -410,6 +412,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'kube_service_account_key': 'public_key',
'kube_service_account_private_key': 'private_key',
'portal_network_cidr': '10.254.0.0/16',
+ 'project_id': 'project_id'
}
self.assertEqual(expected, definition)
@@ -517,6 +520,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'kube_service_account_key': 'public_key',
'kube_service_account_private_key': 'private_key',
'portal_network_cidr': '10.254.0.0/16',
+ 'project_id': 'project_id'
}
self.assertEqual(expected, definition)
self.assertEqual(
@@ -951,6 +955,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'kube_service_account_key': 'public_key',
'kube_service_account_private_key': 'private_key',
'portal_network_cidr': '10.254.0.0/16',
+ 'project_id': 'project_id'
}
self.assertEqual(expected, definition)
self.assertEqual(
diff --git a/magnum/tests/unit/drivers/test_template_definition.py b/magnum/tests/unit/drivers/test_template_definition.py
index a9b5bb5380..1dbac30deb 100644
--- a/magnum/tests/unit/drivers/test_template_definition.py
+++ b/magnum/tests/unit/drivers/test_template_definition.py
@@ -409,6 +409,11 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
'grafana_tag')
heat_container_agent_tag = mock_cluster.labels.get(
'heat_container_agent_tag')
+ keystone_auth_enabled = mock_cluster.labels.get(
+ 'keystone_auth_enabled')
+ k8s_keystone_auth_tag = mock_cluster.labels.get(
+ 'k8s_keystone_auth_tag')
+ project_id = mock_cluster.project_id
k8s_def = k8sa_tdef.AtomicK8sTemplateDefinition()
@@ -461,6 +466,9 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
'prometheus_tag': prometheus_tag,
'grafana_tag': grafana_tag,
'heat_container_agent_tag': heat_container_agent_tag,
+ 'keystone_auth_enabled': keystone_auth_enabled,
+ 'k8s_keystone_auth_tag': k8s_keystone_auth_tag,
+ 'project_id': project_id,
}}
mock_get_params.assert_called_once_with(mock_context,
mock_cluster_template,
@@ -588,6 +596,11 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
'grafana_tag')
heat_container_agent_tag = mock_cluster.labels.get(
'heat_container_agent_tag')
+ keystone_auth_enabled = mock_cluster.labels.get(
+ 'keystone_auth_enabled')
+ k8s_keystone_auth_tag = mock_cluster.labels.get(
+ 'k8s_keystone_auth_tag')
+ project_id = mock_cluster.project_id
k8s_def = k8sa_tdef.AtomicK8sTemplateDefinition()
@@ -642,6 +655,9 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
'prometheus_tag': prometheus_tag,
'grafana_tag': grafana_tag,
'heat_container_agent_tag': heat_container_agent_tag,
+ 'keystone_auth_enabled': keystone_auth_enabled,
+ 'k8s_keystone_auth_tag': k8s_keystone_auth_tag,
+ 'project_id': project_id,
}}
mock_get_params.assert_called_once_with(mock_context,
mock_cluster_template,
diff --git a/releasenotes/notes/k8s-keystone-auth-6c88c1a2d406fb61.yaml b/releasenotes/notes/k8s-keystone-auth-6c88c1a2d406fb61.yaml
new file mode 100644
index 0000000000..d4f7dc97cf
--- /dev/null
+++ b/releasenotes/notes/k8s-keystone-auth-6c88c1a2d406fb61.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Now cloud-provider-openstack of Kubernetes has a webhook to support
+ Keystone authorization and authentication. With this feature, user can use
+ a new label 'keystone-auth-enabled' to enable the keystone authN and authZ.
+