summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2015-10-13 20:53:00 -0400
committerSimo Sorce <simo@redhat.com>2015-11-11 11:37:15 -0500
commitd3c907cb21416a23e8f736f156ea807f6d1d00c5 (patch)
tree78cdf77deff6c73b7475ff9b3f6699211e0c2b27
parent0abd2a6c4ac66b905430d3cad95c1b2a23bda40f (diff)
downloadcustodia-d3c907cb21416a23e8f736f156ea807f6d1d00c5.tar.gz
custodia-d3c907cb21416a23e8f736f156ea807f6d1d00c5.tar.xz
custodia-d3c907cb21416a23e8f736f156ea807f6d1d00c5.zip
Add authz plugin that verify kubelets requests
This patch adds a special authorization plugin that verifies the identity of the node as well as checking that the node is authorized to make a request on behalf of the pod for which it is asking secrets. If all checks pass the path is rewritten to point to the proper secrets namespace for the pod. By rewriting paths, in case of catastrophic failure of the plugin no secret can be found as the path matches nothing. Signed-off-by: Simo Sorce <simo@redhat.com>
-rw-r--r--custodia/kubernetes/authz.py66
1 files changed, 66 insertions, 0 deletions
diff --git a/custodia/kubernetes/authz.py b/custodia/kubernetes/authz.py
new file mode 100644
index 0000000..4342f6f
--- /dev/null
+++ b/custodia/kubernetes/authz.py
@@ -0,0 +1,66 @@
+# Copyright (C) 2015 Custodia Project Contributors - see LICENSE file
+
+import requests
+
+from custodia import log
+from custodia.httpd.authorizers import HTTPAuthorizer
+
+DEFAULT_API_SERVER = 'http://localhost:8080'
+
+
+class KubeAuthz(HTTPAuthorizer):
+
+ def __init__(self, config=None):
+ super(KubeAuthz, self).__init__(config)
+ self.path = self.config.get('path', '/nodes')
+ self.secrets = self.config.get('secrets', '/secrets')
+ self.label = self.config.get('secrets_label', 'secrets_namespace')
+ self.server_api = self.config.get('kube_api_server',
+ DEFAULT_API_SERVER)
+
+ def handle(self, request):
+ reqpath = path = request.get('path', '')
+ prefix = path
+
+ while prefix.startswith('/'):
+ if prefix == self.path:
+ break
+ else:
+ prefix, _ = prefix.rsplit('/', 1)
+
+ if prefix != self.path:
+ self.logger.debug("Prefix %s does not match path %s",
+ prefix, reqpath)
+ return None
+
+ trail = path[len(prefix) + 1:]
+
+ (namespace, podname, secret) = trail.split('/', 2)
+ self.logger.debug("Checking if pod %s,%s has access to secret %s",
+ namespace, podname, secret)
+
+ try:
+ r = requests.get('%s/api/v1/namespaces/%s/pods/%s' % (
+ self.server_api, namespace, podname))
+ r.raise_for_status()
+ data = r.json()
+ node_id = data["spec"]["nodeName"]
+ secrets_namespace = data["metadata"]["labels"][self.label]
+ except Exception: # pylint: disable=broad-except
+ self.logger.exception("Failed to fecth data from Kube API Server")
+ self.audit_svc_access(log.AUDIT_SVC_AUTHZ_FAIL,
+ request['client_id'], path)
+ return False
+
+ if node_id != request.get("remote_user"):
+ self.logger.debug("Node authenticated as %s, but pod is believed "
+ "to be running on %s",
+ request.get("remote_user"), node_id)
+ self.audit_svc_access(log.AUDIT_SVC_AUTHZ_FAIL,
+ request['client_id'], path)
+ return False
+
+ request['path'] = '/'.join([self.secrets, secrets_namespace, secret])
+ self.audit_svc_access(log.AUDIT_SVC_AUTHZ_PASS, request['client_id'],
+ "%s -> %s" % (path, request['path']))
+ return True