summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2015-10-02 21:30:35 -0400
committerSimo Sorce <simo@redhat.com>2015-10-19 12:18:26 -0400
commitb20b47b100b2716273a5abfe2850e994c1d3e69d (patch)
treeae028008985d65ea2009ebc0d8871676b18593b6
parent2380852ef8007cd9862d2db5f6af7e4e10bd6aad (diff)
downloadcustodia-b20b47b100b2716273a5abfe2850e994c1d3e69d.tar.gz
custodia-b20b47b100b2716273a5abfe2850e994c1d3e69d.tar.xz
custodia-b20b47b100b2716273a5abfe2850e994c1d3e69d.zip
Add forwarder plugin
This pugin allows to mangle and forward requests to another custodia server, locally or on the network. Signed-off-by: Simo Sorce <simo@redhat.com> Reviewed-by: Christian Heimes <cheimes@redhat.com>
-rw-r--r--custodia.conf14
-rw-r--r--custodia/forwarder.py72
-rw-r--r--tests/custodia.py24
3 files changed, 109 insertions, 1 deletions
diff --git a/custodia.conf b/custodia.conf
index 1a987df..c77cf8b 100644
--- a/custodia.conf
+++ b/custodia.conf
@@ -81,3 +81,17 @@ handler = custodia.root.Secrets
allowed_keytypes = simple kem
store = encrypted
+# Forward
+[authz:forwarders]
+handler = custodia.httpd.authorizers.SimplePathAuthz
+paths = /forwarder /forwarder_loop
+
+[/forwarder]
+handler = custodia.forwarder.Forwarder
+forward_uri = http+unix://%2e%2fserver_socket/secrets
+forward_headers = {"CUSTODIA_AUTH_ID": "test", "CUSTODIA_AUTH_KEY": "foo-host-key"}
+
+[/forwarder_loop]
+handler = custodia.forwarder.Forwarder
+forward_uri = http+unix://%2e%2fserver_socket/forwarder_loop
+forward_headers = {"REMOTE_USER": "test"}
diff --git a/custodia/forwarder.py b/custodia/forwarder.py
new file mode 100644
index 0000000..03fcfef
--- /dev/null
+++ b/custodia/forwarder.py
@@ -0,0 +1,72 @@
+# Copyright (C) 2015 Custodia Project Contributors - see LICENSE file
+
+import json
+import uuid
+
+from custodia import log
+from custodia.client import CustodiaHTTPClient
+from custodia.httpd.consumer import HTTPConsumer
+from custodia.httpd.server import HTTPError
+
+
+class Forwarder(HTTPConsumer):
+
+ def __init__(self, *args, **kwargs):
+ super(Forwarder, self).__init__(*args, **kwargs)
+ self._auditlog = log.AuditLog(self.config)
+ self.client = CustodiaHTTPClient(self.config['forward_uri'])
+ self.headers = json.loads(self.config.get('forward_headers', '{}'))
+ self.uuid = str(uuid.uuid4())
+ self.headers['X-LOOP-CUSTODIA'] = self.uuid
+
+ def _path(self, request):
+ trail = request.get('trail', [])
+ prefix = request.get('remote_user', 'guest')
+ return '/'.join([prefix.rstrip('/')] + trail)
+
+ def _headers(self, request):
+ headers = {}
+ headers.update(self.headers)
+ loop = request['headers'].get('X-LOOP-CUSTODIA', None)
+ if loop is not None:
+ headers['X-LOOP-CUSTODIA'] += ',' + loop
+ return headers
+
+ def _response(self, reply, response):
+ if reply.status_code < 200 or reply.status_code > 299:
+ raise HTTPError(reply.status_code)
+ response['code'] = reply.status_code
+ if reply.content:
+ response['output'] = reply.content
+
+ def _request(self, cmd, request, response, path, **kwargs):
+ if self.uuid in request['headers'].get('X-LOOP-CUSTODIA', ''):
+ raise HTTPError(502, "Loop detected")
+ reply = cmd(path, **kwargs)
+ self._response(reply, response)
+
+ def GET(self, request, response):
+ self._request(self.client.get, request, response,
+ self._path(request),
+ params=request.get('query', None),
+ headers=self._headers(request))
+
+ def PUT(self, request, response):
+ self._request(self.client.put, request, response,
+ self._path(request),
+ data=request.get('body', None),
+ params=request.get('query', None),
+ headers=self._headers(request))
+
+ def DELETE(self, request, response):
+ self._request(self.client.delete, request, response,
+ self._path(request),
+ params=request.get('query', None),
+ headers=self._headers(request))
+
+ def POST(self, request, response):
+ self._request(self.client.post, request, response,
+ self._path(request),
+ data=request.get('body', None),
+ params=request.get('query', None),
+ headers=self._headers(request))
diff --git a/tests/custodia.py b/tests/custodia.py
index 54563d5..ed80010 100644
--- a/tests/custodia.py
+++ b/tests/custodia.py
@@ -30,6 +30,8 @@ class CustodiaTests(unittest.TestCase):
time.sleep(1)
cls.client = CustodiaClient('http+unix://%2E%2Fserver_socket/secrets')
cls.client.headers['REMOTE_USER'] = 'test'
+ cls.fwd = CustodiaClient('http+unix://%2E%2Fserver_socket/forwarder')
+ cls.fwd.headers['REMOTE_USER'] = 'test'
@classmethod
def tearDownClass(cls):
@@ -68,9 +70,29 @@ class CustodiaTests(unittest.TestCase):
r = self.client.list_container('test')
self.assertEqual(r.json(), [])
- def test_6_delete_container(self):
+ def test_6_create_forwarded_container(self):
+ self.fwd.create_container('dir')
+ r = self.client.list_container('test/dir')
+ self.assertEqual(r.json(), [])
+
+ def test_7_delete_forwarded_container(self):
+ self.fwd.delete_container('dir')
+ try:
+ self.client.list_container('test/dir')
+ except HTTPError as e:
+ self.assertEqual(e.response.status_code, 404)
+
+ def test_8_delete_container(self):
self.client.delete_container('test')
try:
self.client.list_container('test')
except HTTPError as e:
self.assertEqual(e.response.status_code, 404)
+
+ def test_9_loop(self):
+ loop = CustodiaClient('http+unix://%2E%2Fserver_socket/forwarder_loop')
+ loop.headers['REMOTE_USER'] = 'test'
+ try:
+ loop.list_container('test')
+ except HTTPError as e:
+ self.assertEqual(e.response.status_code, 502)