summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Heimes <christian@python.org>2015-10-02 14:45:36 +0200
committerChristian Heimes <christian@python.org>2015-10-02 14:45:36 +0200
commita07fa7ae9d08416de8bc9994adce1353310d4ba3 (patch)
tree62d89167c7fb34323f9f1ef82583d6dfd6a7a4c5
downloadgustodia-a07fa7ae9d08416de8bc9994adce1353310d4ba3.tar.gz
gustodia-a07fa7ae9d08416de8bc9994adce1353310d4ba3.tar.xz
gustodia-a07fa7ae9d08416de8bc9994adce1353310d4ba3.zip
Initial commit with first working hack
-rw-r--r--.gitignore11
-rw-r--r--Makefile46
-rw-r--r--README7
-rw-r--r--custodia.conf47
-rwxr-xr-xentrypoint.sh8
-rw-r--r--gustodia.go112
-rwxr-xr-xgustodia.py71
7 files changed, 302 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2dd975c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+*.pyc
+.*swp
+__pycache__
+
+venv
+
+custodia.audit.log
+secrets.db
+server_socket
+
+gustodia
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..534ad1f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,46 @@
+VENV=venv
+PYTHON=python2.7
+CUSTODIA=${VENV}/bin/custodia
+REQUIREMENTS=cryptography jwcrypto git+https://github.com/simo5/custodia
+CURL_CMD=curl --unix-socket server_socket -H "REMOTE_USER: user"
+
+all: ${CUSTODIA}
+
+${VENV}:
+ virtualenv --python=${PYTHON} ${VENV}
+ ${VENV}/bin/pip install --upgrade pip setuptools
+
+${CUSTODIA}: | ${VENV}
+ ${VENV}/bin/pip install ${REQUIREMENTS}
+
+gustodia: gustodia.go
+ go build $<
+
+.PHONY=example
+example: gustodia
+ env -i EXAMPLE=foo CUSTODIA_SECRET_DB_PASSWORD=tests/mysecret \
+ ./gustodia ./entrypoint.sh apache
+
+.PHONY=run_server
+run_server: ${CUSTODIA}
+ ${CUSTODIA}
+
+.PHONY=init_secret
+init_secret:
+ ${CURL_CMD} -X POST http://localhost/secrets/tests/
+ ${CURL_CMD} -H "Content-Type: application/json" \
+ -X PUT \
+ -d '{"type": "simple", "value": "SuperSecretPassword"}' \
+ http://localhost/secrets/tests/mysecret
+
+.PHONY=upgrade
+upgrade: | ${VENV}
+ ${VENV}/bin/pip install --upgrade ${REQUIREMENTS}
+
+.PHONY=clean
+clean:
+ @rm -rf ${VENV}
+ @rm -rf secrets.db
+ @rm -f custodia.audit.log
+ @rm -f gustodia
+ @rm -f server_socket
diff --git a/README b/README
new file mode 100644
index 0000000..830d2fd
--- /dev/null
+++ b/README
@@ -0,0 +1,7 @@
+$ make run_server
+
+$ make init_db
+$ make example
+
+curl --unix-socket server_socket -H "REMOTE_USER: user" http://localhost/secrets/tests/mysecret
+
diff --git a/custodia.conf b/custodia.conf
new file mode 100644
index 0000000..40f513e
--- /dev/null
+++ b/custodia.conf
@@ -0,0 +1,47 @@
+[global]
+server_version = "Secret/0.0.7"
+debug = True
+
+#[auth:simple]
+#handler = custodia.httpd.authenticators.SimpleCredsAuth
+#uid = 48
+#gid = 48
+
+[auth:header]
+handler = custodia.httpd.authenticators.SimpleHeaderAuth
+name = REMOTE_USER
+
+[authz:paths]
+handler = custodia.httpd.authorizers.SimplePathAuthz
+paths = /.
+
+[authz:namespaces]
+handler = custodia.secrets.Namespaces
+path = /secrets/
+store = simple
+
+[store:simple]
+handler = custodia.store.sqlite.SqliteStore
+dburi = secrets.db
+table = secrets
+
+[/]
+handler = custodia.root.Root
+store = simple
+
+
+# Multi-tenant example
+[store:tenant1]
+handler = custodia.store.sqlite.SqliteStore
+dburi = secrets.db
+table = tenant1
+
+[authz:tenant1]
+handler = custodia.secrets.Namespaces
+path = /tenant1/secrets/
+store = tenant1
+
+[/tenant1/secrets]
+handler = custodia.root.Secrets
+store = tenant1
+
diff --git a/entrypoint.sh b/entrypoint.sh
new file mode 100755
index 0000000..3b432ec
--- /dev/null
+++ b/entrypoint.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# sample entry point
+
+echo "arg0: $0"
+echo "args: $@"
+echo "Env:"
+env
diff --git a/gustodia.go b/gustodia.go
new file mode 100644
index 0000000..9757542
--- /dev/null
+++ b/gustodia.go
@@ -0,0 +1,112 @@
+package main
+
+import (
+ "fmt"
+ "encoding/json"
+ "errors"
+ "net"
+ "net/http"
+ "os"
+ "strings"
+ "syscall"
+)
+
+const PREFIX string = "CUSTODIA_"
+const SECRET_PREFIX string = PREFIX + "SECRET_"
+const BASE_PATH string = "http://localhost/secrets/"
+const SOCKET string = "./server_socket"
+
+type CustodiaMessage struct {
+ Type string `json:"type"`
+ Value string `json:"value"`
+}
+
+type CustodiaSecret struct {
+ Name string
+ Value string
+ Secret string
+}
+
+func unixDial(proto, addr string) (conn net.Conn, err error) {
+ typ := "unix"
+ conn, err = net.DialUnix(typ, nil, &net.UnixAddr{SOCKET, typ})
+ return conn, err
+}
+
+func query_custodia(secrets []*CustodiaSecret) {
+ tr := &http.Transport{
+ Dial: unixDial,
+ }
+ client := &http.Client{Transport: tr}
+
+ for _, sec := range secrets {
+ path := BASE_PATH + sec.Value
+ req, err := http.NewRequest(
+ "GET", path, nil)
+ if err != nil {
+ panic(err)
+ }
+ req.Header.Add("REMOTE_USER", "gustodia")
+ resp, err := client.Do(req)
+ if err != nil {
+ panic(err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ msg := fmt.Sprintf("%s: %s", path, resp.Status)
+ err := errors.New(msg)
+ panic(err)
+ }
+ var m CustodiaMessage
+ body := json.NewDecoder(resp.Body)
+ body.Decode(&m)
+ sec.Secret = m.Value;
+ }
+}
+
+func get_custodia_envs() ([]*CustodiaSecret) {
+ secrets := []*CustodiaSecret{}
+ for _, env := range os.Environ() {
+ if strings.HasPrefix(env, SECRET_PREFIX) {
+ pair := strings.SplitN(env, "=", 2)
+ sec := &CustodiaSecret{
+ Name: pair[0][len(SECRET_PREFIX):],
+ Value: pair[1],
+ Secret: "",
+ }
+ secrets = append(secrets, sec)
+ }
+ }
+ return secrets
+}
+
+func make_env(secrets []*CustodiaSecret) []string {
+ environ := []string{}
+ for _, env := range os.Environ() {
+ if ! strings.HasPrefix(env, PREFIX) {
+ environ = append(environ, env)
+ }
+ }
+ for _, sec := range secrets {
+ env := fmt.Sprintf("%s=%s", sec.Name, sec.Secret)
+ environ = append(environ, env)
+ }
+ return environ
+}
+
+func main() {
+ secrets := get_custodia_envs()
+ query_custodia(secrets)
+ environ := make_env(secrets)
+ /*
+ for _, sec := range secrets {
+ fmt.Printf("%+v\n", *sec)
+ }
+ for _, env := range environ {
+ fmt.Println(env)
+ }
+ */
+ args := os.Args[1:]
+ syscall.Exec(args[0], args, environ)
+}
diff --git a/gustodia.py b/gustodia.py
new file mode 100755
index 0000000..4958cbd
--- /dev/null
+++ b/gustodia.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python2.7
+from __future__ import print_function
+
+import httplib
+import json
+import os
+import socket
+import sys
+
+PREFIX = 'CUSTODIA_'
+SECRET_PREFIX = PREFIX + 'SECRET_'
+SOCK = './server_socket'
+
+class HTTPUnixConnection(httplib.HTTPConnection):
+ def __init__(self, socket_file):
+ self.socket_file = socket_file
+ httplib.HTTPConnection.__init__(self, 'localhost')
+
+ def connect(self):
+ sock = socket.socket(socket.AF_UNIX)
+ sock.connect(self.socket_file)
+ self.sock = sock
+
+
+def get_custodia_envs():
+ envs = {}
+ for name, value in os.environ.iteritems():
+ if name.startswith(SECRET_PREFIX):
+ name = name[len(SECRET_PREFIX):]
+ envs[name] = value
+ return envs
+
+
+def query_custodia(envs, sock=SOCK):
+ conn = HTTPUnixConnection(sock)
+ headers = {'REMOTE_USER': 'gustodia'}
+ result = {}
+ for name, path in envs.iteritems():
+ conn.request(
+ 'GET', 'http://localhost/secrets/' + path, headers=headers
+ )
+ response = conn.getresponse()
+ if response.status != 200:
+ print(r1.status, r1.reason)
+ continue
+ value = json.loads(response.read())
+ result[name] = value['value']
+ return result
+
+
+def filtered_env():
+ env = {}
+ for name, value in os.environ.iteritems():
+ if not name.startswith(PREFIX):
+ env[name] = value
+ return env
+
+
+def main():
+ cenvs = get_custodia_envs()
+ print('env vars', cenvs)
+ secrets = query_custodia(cenvs)
+ print('secrets from custodia', secrets)
+ envp = filtered_env()
+ envp.update(secrets)
+ argv = sys.argv[1:]
+ filename = argv[0]
+ print("execve(%s, %s, %s)" % (filename, argv, envp))
+
+if __name__ == '__main__':
+ main()