From 103e0ee850404a5c8dc69bbbf79b2508a9c55d7a Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Thu, 26 Feb 2015 16:54:26 +0100 Subject: Added authentication between frontend and storage nodes --- Makefile | 2 ++ src/catlfish_web.erl | 32 ++++++++++++++++++++++---- test/config/frontend-1.config | 21 ++++++++++++++++- test/config/privatekeys/frontend-1-private.pem | 5 ++++ test/config/privatekeys/merge-1-private.pem | 5 ++++ test/config/privatekeys/storage-1-private.pem | 5 ++++ test/config/publickeys/frontend-1.pem | 4 ++++ test/config/publickeys/merge-1.pem | 4 ++++ test/config/publickeys/storage-1.pem | 4 ++++ test/config/storage-1.config | 10 +++++++- 10 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 test/config/privatekeys/frontend-1-private.pem create mode 100644 test/config/privatekeys/merge-1-private.pem create mode 100644 test/config/privatekeys/storage-1-private.pem create mode 100644 test/config/publickeys/frontend-1.pem create mode 100644 test/config/publickeys/merge-1.pem create mode 100644 test/config/publickeys/storage-1.pem diff --git a/Makefile b/Makefile index 56064ad..08f6584 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,8 @@ tests-prepare: mkdir -p test/nodes/storage-2/log cp test/config/frontend-1.config rel cp test/config/storage-1.config rel + cp -r test/config/privatekeys rel + cp -r test/config/publickeys rel rm -r rel/tests || true mkdir -p rel/tests/machine/machine-1/db printf "0" > rel/tests/machine/machine-1/db/treesize diff --git a/src/catlfish_web.erl b/src/catlfish_web.erl index 9869b21..5ee5743 100644 --- a/src/catlfish_web.erl +++ b/src/catlfish_web.erl @@ -11,15 +11,31 @@ start(Options, Module) -> end, mochiweb_http:start([{name, Module}, {loop, Loop} | Options]). + +add_auth(Path, {Code, Headers, Data}) -> + AuthHeader = http_auth:create_auth("REPLY", Path, Data), + lager:debug("sent auth header: ~p", [AuthHeader]), + {Code, [{"X-Catlfish-Auth", AuthHeader} | Headers], Data}. + loop(Req, Module) -> "/" ++ Path = Req:get(path), try Starttime = os:timestamp(), + AuthHeader = Req:get_header_value("X-Catlfish-Auth"), case Req:get(method) of 'GET' -> Query = Req:parse_qs(), - lager:debug("GET ~p ~p", [Path, Query]), - Result = Module:request(get, Path, Query), + {_, RawQuery, _} = mochiweb_util:urlsplit_path(Req:get(raw_path)), + Result = case http_auth:verify_auth(AuthHeader, "GET", "/" ++ Path, RawQuery) of + failure -> + {403, [{"Content-Type", "text/plain"}], "Invalid credentials"}; + success -> + lager:debug("GET ~p ~p", [Path, Query]), + add_auth("/" ++ Path, Module:request(get, Path, Query)); + noauth -> + lager:debug("GET ~p ~p", [Path, Query]), + Module:request(get, Path, Query) + end, lager:debug("GET finished: ~p us", [timer:now_diff(os:timestamp(), Starttime)]), case Result of none -> @@ -29,8 +45,16 @@ loop(Req, Module) -> end; 'POST' -> Body = Req:recv_body(), - lager:debug("POST ~p ~p", [Path, Body]), - Result = Module:request(post, Path, Body), + Result = case http_auth:verify_auth(AuthHeader, "POST", "/" ++ Path, Body) of + failure -> + {403, [{"Content-Type", "text/plain"}], "Invalid credentials"}; + success -> + lager:debug("POST ~p ~p", [Path, Body]), + add_auth("/" ++ Path, Module:request(post, Path, Body)); + noauth -> + lager:debug("POST ~p ~p", [Path, Body]), + Module:request(post, Path, Body) + end, lager:debug("POST finished: ~p us", [timer:now_diff(os:timestamp(), Starttime)]), case Result of none -> diff --git a/test/config/frontend-1.config b/test/config/frontend-1.config index 79d887d..35631d1 100644 --- a/test/config/frontend-1.config +++ b/test/config/frontend-1.config @@ -31,5 +31,24 @@ {treesize_path, "tests/machine/machine-1/db/treesize"}, {indexforhash_root_path, "tests/machine/machine-1/db/certindex/"}, {storage_nodes, ["https://127.0.0.1:8081/ct/storage/"]}, - {storage_nodes_quorum, 1} + {storage_nodes_quorum, 1}, + {publickey_path, "publickeys"}, + {own_key, {"frontend-1", "privatekeys/frontend-1-private.pem"}}, + {allowed_clients, [{"/ct/frontend/sendentry", noauth}, + {"/ct/frontend/sendlog", noauth}, + {"/ct/frontend/sendsth", noauth}, + {"/ct/frontend/currentposition", noauth}, + {"/ct/frontend/missingentries", noauth}, + {"/ct/v1/add-chain", noauth}, + {"/ct/v1/add-pre-chain", noauth}, + {"/ct/v1/get-sth", noauth}, + {"/ct/v1/get-sth-consistency", noauth}, + {"/ct/v1/get-proof-by-hash", noauth}, + {"/ct/v1/get-entries", noauth}, + {"/ct/v1/get-entry-and-proof", noauth}, + {"/ct/v1/get-roots", noauth} + ]}, + {allowed_servers, [{"/ct/storage/sendentry", ["storage-1"]}, + {"/ct/storage/entrycommitted", ["storage-1"]} + ]} ]}]. diff --git a/test/config/privatekeys/frontend-1-private.pem b/test/config/privatekeys/frontend-1-private.pem new file mode 100644 index 0000000..718efda --- /dev/null +++ b/test/config/privatekeys/frontend-1-private.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPER9WFIxLXvXDHTwPvGnNvBAKOB+/6ahpvuCjtlzOU8oAoGCCqGSM49 +AwEHoUQDQgAEibeLqrVV7QAE6Wytzpxi4sd0JtGNGRfXNZ9r9CNIVudDnNjtFRF5 +gwm/AxUWEuBXjnbVvq4HOLqZ0bP2qc+uRQ== +-----END EC PRIVATE KEY----- diff --git a/test/config/privatekeys/merge-1-private.pem b/test/config/privatekeys/merge-1-private.pem new file mode 100644 index 0000000..55d50b1 --- /dev/null +++ b/test/config/privatekeys/merge-1-private.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIBQcXtOVX29dno+aYqGddVOpg23FfhJmrMFOpOegyYZxoAoGCCqGSM49 +AwEHoUQDQgAExHAsjFFgKFlrcCveHhVdjE7A/Uh0gXdAeN9+P7SDGgRNe0WWDjCr +0Da3c8X5JulA1cOLlQ0h2B67Yp3WZ9ONHg== +-----END EC PRIVATE KEY----- diff --git a/test/config/privatekeys/storage-1-private.pem b/test/config/privatekeys/storage-1-private.pem new file mode 100644 index 0000000..b68d2a9 --- /dev/null +++ b/test/config/privatekeys/storage-1-private.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAjVa6lTbhiNUfrfTGELRXqHSHF0nuk13lKF8NSHzU07oAoGCCqGSM49 +AwEHoUQDQgAE1vFWiMT9PItJGvyhMKPF5TnFirHPSh5u5swetajmNLyClWIDGXql +RlXlcPwuKxTISI4rFJATBkKhNjvSZ5L3oA== +-----END EC PRIVATE KEY----- diff --git a/test/config/publickeys/frontend-1.pem b/test/config/publickeys/frontend-1.pem new file mode 100644 index 0000000..938ef29 --- /dev/null +++ b/test/config/publickeys/frontend-1.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEibeLqrVV7QAE6Wytzpxi4sd0JtGN +GRfXNZ9r9CNIVudDnNjtFRF5gwm/AxUWEuBXjnbVvq4HOLqZ0bP2qc+uRQ== +-----END PUBLIC KEY----- diff --git a/test/config/publickeys/merge-1.pem b/test/config/publickeys/merge-1.pem new file mode 100644 index 0000000..95a75f7 --- /dev/null +++ b/test/config/publickeys/merge-1.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExHAsjFFgKFlrcCveHhVdjE7A/Uh0 +gXdAeN9+P7SDGgRNe0WWDjCr0Da3c8X5JulA1cOLlQ0h2B67Yp3WZ9ONHg== +-----END PUBLIC KEY----- diff --git a/test/config/publickeys/storage-1.pem b/test/config/publickeys/storage-1.pem new file mode 100644 index 0000000..0b862a1 --- /dev/null +++ b/test/config/publickeys/storage-1.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1vFWiMT9PItJGvyhMKPF5TnFirHP +Sh5u5swetajmNLyClWIDGXqlRlXlcPwuKxTISI4rFJATBkKhNjvSZ5L3oA== +-----END PUBLIC KEY----- diff --git a/test/config/storage-1.config b/test/config/storage-1.config index b176e1f..8770f88 100644 --- a/test/config/storage-1.config +++ b/test/config/storage-1.config @@ -28,4 +28,12 @@ {newentries_path, "tests/machine/machine-1/db/newentries"}, {entryhash_root_path, "tests/machine/machine-1/db/entryhash/"}, {treesize_path, "tests/machine/machine-1/db/treesize"}, - {indexforhash_root_path, "tests/machine/machine-1/db/certindex/"}]}]. + {indexforhash_root_path, "tests/machine/machine-1/db/certindex/"}, + {publickey_path, "publickeys"}, + {own_key, {"storage-1", "privatekeys/storage-1-private.pem"}}, + {allowed_clients, [{"/ct/storage/sendentry", ["frontend-1"]}, + {"/ct/storage/entrycommitted", ["frontend-1"]}, + {"/ct/storage/fetchnewentries", noauth}, + {"/ct/storage/getentry", noauth} + ]} +]}]. -- cgit v1.1 From 90bd73177964246a0e1a5d6c5e4255dcc8ec700d Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Fri, 27 Feb 2015 13:53:32 +0100 Subject: Require authentication for merge calls --- test/config/frontend-1.config | 10 +++++----- test/config/storage-1.config | 2 +- tools/merge.py | 42 ++++++++++++++++++++++++++++++++---------- tools/testcase1.py | 2 +- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/test/config/frontend-1.config b/test/config/frontend-1.config index 35631d1..9d7e37c 100644 --- a/test/config/frontend-1.config +++ b/test/config/frontend-1.config @@ -34,11 +34,11 @@ {storage_nodes_quorum, 1}, {publickey_path, "publickeys"}, {own_key, {"frontend-1", "privatekeys/frontend-1-private.pem"}}, - {allowed_clients, [{"/ct/frontend/sendentry", noauth}, - {"/ct/frontend/sendlog", noauth}, - {"/ct/frontend/sendsth", noauth}, - {"/ct/frontend/currentposition", noauth}, - {"/ct/frontend/missingentries", noauth}, + {allowed_clients, [{"/ct/frontend/sendentry", ["merge-1"]}, + {"/ct/frontend/sendlog", ["merge-1"]}, + {"/ct/frontend/sendsth", ["merge-1"]}, + {"/ct/frontend/currentposition", ["merge-1"]}, + {"/ct/frontend/missingentries", ["merge-1"]}, {"/ct/v1/add-chain", noauth}, {"/ct/v1/add-pre-chain", noauth}, {"/ct/v1/get-sth", noauth}, diff --git a/test/config/storage-1.config b/test/config/storage-1.config index 8770f88..005a8ad 100644 --- a/test/config/storage-1.config +++ b/test/config/storage-1.config @@ -33,7 +33,7 @@ {own_key, {"storage-1", "privatekeys/storage-1-private.pem"}}, {allowed_clients, [{"/ct/storage/sendentry", ["frontend-1"]}, {"/ct/storage/entrycommitted", ["frontend-1"]}, - {"/ct/storage/fetchnewentries", noauth}, + {"/ct/storage/fetchnewentries", ["merge-1"]}, {"/ct/storage/getentry", noauth} ]} ]}]. diff --git a/tools/merge.py b/tools/merge.py index f4a007d..6becf7e 100755 --- a/tools/merge.py +++ b/tools/merge.py @@ -11,6 +11,9 @@ import urllib import urllib2 import sys import time +import ecdsa +import hashlib +import urlparse from certtools import build_merkle_tree, create_sth_signature, check_sth_signature, get_eckey_from_file, timing_point parser = argparse.ArgumentParser(description="") @@ -19,6 +22,8 @@ parser.add_argument("--frontend", action="append", metavar="url", help="Base URL parser.add_argument("--storage", action="append", metavar="url", help="Base URL for storage server", required=True) parser.add_argument("--mergedb", metavar="dir", help="Merge database directory", required=True) parser.add_argument("--keyfile", metavar="keyfile", help="File containing log key", required=True) +parser.add_argument("--own-keyname", metavar="keyname", help="The key name of the merge node", required=True) +parser.add_argument("--own-keyfile", metavar="keyfile", help="The file containing the private key of the merge node", required=True) parser.add_argument("--nomerge", action='store_true', help="Don't actually do merge") args = parser.parse_args() @@ -52,9 +57,26 @@ def add_to_logorder(key): f.write(base64.b16encode(key) + "\n") f.close() +def http_request(url, data=None): + req = urllib2.Request(url, data) + keyname = args.own_keyname + privatekey = get_eckey_from_file(args.own_keyfile) + sk = ecdsa.SigningKey.from_der(privatekey) + parsed_url = urlparse.urlparse(url) + if data == None: + data = parsed_url.query + method = "GET" + else: + method = "POST" + signature = sk.sign("%s\0%s\0%s" % (method, parsed_url.path, data), hashfunc=hashlib.sha256, + sigencode=ecdsa.util.sigencode_der) + req.add_header('X-Catlfish-Auth', base64.b64encode(signature) + ";key=" + keyname) + result = urllib2.urlopen(req).read() + return result + def get_new_entries(baseurl): try: - result = urllib2.urlopen(baseurl + "ct/storage/fetchnewentries").read() + result = http_request(baseurl + "ct/storage/fetchnewentries") parsed_result = json.loads(result) if parsed_result.get(u"result") == u"ok": return [base64.b64decode(entry) for entry in parsed_result[u"entries"]] @@ -67,7 +89,7 @@ def get_new_entries(baseurl): def get_entries(baseurl, hashes): try: params = urllib.urlencode({"hash":[base64.b64encode(hash) for hash in hashes]}, doseq=True) - result = urllib2.urlopen(baseurl + "ct/storage/getentry?" + params).read() + result = http_request(baseurl + "ct/storage/getentry?" + params) parsed_result = json.loads(result) if parsed_result.get(u"result") == u"ok": entries = dict([(base64.b64decode(entry["hash"]), base64.b64decode(entry["entry"])) for entry in parsed_result[u"entries"]]) @@ -82,7 +104,7 @@ def get_entries(baseurl, hashes): def get_curpos(baseurl): try: - result = urllib2.urlopen(baseurl + "ct/frontend/currentposition").read() + result = http_request(baseurl + "ct/frontend/currentposition") parsed_result = json.loads(result) if parsed_result.get(u"result") == u"ok": return parsed_result[u"position"] @@ -94,8 +116,8 @@ def get_curpos(baseurl): def sendlog(baseurl, submission): try: - result = urllib2.urlopen(baseurl + "ct/frontend/sendlog", - json.dumps(submission)).read() + result = http_request(baseurl + "ct/frontend/sendlog", + json.dumps(submission)) return json.loads(result) except urllib2.HTTPError, e: print "ERROR: sendlog", e.read() @@ -110,8 +132,8 @@ def sendlog(baseurl, submission): def sendentry(baseurl, entry, hash): try: - result = urllib2.urlopen(baseurl + "ct/frontend/sendentry", - json.dumps({"entry":base64.b64encode(entry), "treeleafhash":base64.b64encode(hash)})).read() + result = http_request(baseurl + "ct/frontend/sendentry", + json.dumps({"entry":base64.b64encode(entry), "treeleafhash":base64.b64encode(hash)})) return json.loads(result) except urllib2.HTTPError, e: print "ERROR: sendentry", e.read() @@ -126,8 +148,8 @@ def sendentry(baseurl, entry, hash): def sendsth(baseurl, submission): try: - result = urllib2.urlopen(baseurl + "ct/frontend/sendsth", - json.dumps(submission)).read() + result = http_request(baseurl + "ct/frontend/sendsth", + json.dumps(submission)) return json.loads(result) except urllib2.HTTPError, e: print "ERROR: sendsth", e.read() @@ -142,7 +164,7 @@ def sendsth(baseurl, submission): def get_missingentries(baseurl): try: - result = urllib2.urlopen(baseurl + "ct/frontend/missingentries").read() + result = http_request(baseurl + "ct/frontend/missingentries") parsed_result = json.loads(result) if parsed_result.get(u"result") == u"ok": return parsed_result[u"entries"] diff --git a/tools/testcase1.py b/tools/testcase1.py index a41a783..c87e8eb 100755 --- a/tools/testcase1.py +++ b/tools/testcase1.py @@ -136,7 +136,7 @@ def get_and_check_entry(timestamp, chain, leaf_index): len(submittedcertchain)) def merge(): - return subprocess.call(["./merge.py", "--baseurl", "https://127.0.0.1:8080/", "--frontend", "https://127.0.0.1:8082/", "--storage", "https://127.0.0.1:8081/", "--mergedb", "../rel/mergedb", "--keyfile", "../rel/test/eckey.pem"]) + return subprocess.call(["./merge.py", "--baseurl", "https://127.0.0.1:8080/", "--frontend", "https://127.0.0.1:8082/", "--storage", "https://127.0.0.1:8081/", "--mergedb", "../rel/mergedb", "--keyfile", "../rel/test/eckey.pem", "--own-keyname", "merge-1", "--own-keyfile", "../rel/privatekeys/merge-1-private.pem"]) print_and_check_tree_size(0) -- cgit v1.1