diff options
author | Simo Sorce <simo@redhat.com> | 2015-10-13 20:53:00 -0400 |
---|---|---|
committer | Simo Sorce <simo@redhat.com> | 2015-11-11 11:37:15 -0500 |
commit | d3c907cb21416a23e8f736f156ea807f6d1d00c5 (patch) | |
tree | 78cdf77deff6c73b7475ff9b3f6699211e0c2b27 /custodia/kubernetes/authz.py | |
parent | 0abd2a6c4ac66b905430d3cad95c1b2a23bda40f (diff) | |
download | custodia-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>
Diffstat (limited to 'custodia/kubernetes/authz.py')
-rw-r--r-- | custodia/kubernetes/authz.py | 66 |
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 |