ToDo | 1 scripts/README.ssl | 84 +++++++++++++++++++++++++++++++ scripts/cfs_add_directory.py | 17 +----- scripts/cfs_add_tenant.py | 18 +----- scripts/cfs_add_volume.py | 18 +----- scripts/cfs_delete_tenant.py | 16 +----- scripts/cfs_enable_tenant.py | 18 +----- scripts/cfs_mount.py | 20 ++++++- scripts/cfs_paths.py | 1 scripts/cfs_rm_volume.py | 16 +----- scripts/cfs_start_volume.py | 16 +----- scripts/cfs_stop_volume.py | 16 +----- scripts/cfs_utils.py | 49 ++++++++++++++++++ scripts/cloudfsd.py | 106 ++++++++++++++++++++++++++++++++++++++-- scripts/ssl_examples/admin.key | 15 +++++ scripts/ssl_examples/admin.pem | 16 ++++++ scripts/ssl_examples/admin.pfx |binary scripts/ssl_examples/root.pem | 48 ++++++++++++++++++ scripts/ssl_examples/server.key | 15 +++++ scripts/ssl_examples/server.pem | 16 ++++++ scripts/ssl_examples/tenant.key | 15 +++++ scripts/ssl_examples/tenant.pem | 16 ++++++ 22 files changed, 419 insertions(+), 118 deletions(-)
New commits: commit 095f7664f5731ca79cebace7e7959fbbb6701202 Author: Jeff Darcy jdarcy@redhat.com Date: Mon May 16 15:20:02 2011 -0400
Added SSL support (disabled for now).
See scripts/README.ssl for more information.
diff --git a/ToDo b/ToDo index 2fbbcb9..48a0dc9 100644 --- a/ToDo +++ b/ToDo @@ -2,7 +2,6 @@ (nothing left)
= Medium Priority = -SSL Sanitize volume/tenant names etc. to avoid XSS/injection Add/document code to generate brick list for volume creation Handle IPv6, multi-homed hosts, localhost in scan_gfs_volfiles diff --git a/scripts/README.ssl b/scripts/README.ssl new file mode 100644 index 0000000..3547f6b --- /dev/null +++ b/scripts/README.ssl @@ -0,0 +1,84 @@ += How to set up management SSL = + +The CloudFS management system uses three roles, each identified by a separate +certificate. Only the commonName field of each certificate is checked, as +folows. + + * The servers themselves (commonName="The Server") represented by + PEM format certificate and key files. + + * An administrator (commonName="Super User") represented by a PKCS#12 + format file. This file is intended for loading into browsers; it + includes both certificate and key. + + * A generic tenant (commonName="A Tenant") represented by PEM format + certificate and key. This is used only for management operations, + and is separate from certificates/keys used for the low-level SSL + transport (though for convenience this certificate might be used as + the signing key for those certificates). + +Here is an example of how to create the administrator certificate. The others +are similar except that they don't require the last step. + + # Generate key. + openssl genrsa 1024 > admin.key + + # Generate PEM certificate. + openssl req -new -x509 -key admin.key -out admin.pem + + # Convert to a PKCS#12 certificate. + openssl pkcs12 -export -in admin.pem -inkey admin.key \ + -name "Super User" > admin.pfx + +There is a directory of sample certificates/keys included with the code, in +.../scripts/ssl_examples. Note that the file names do matter, as they are +embedded in the code. + + * server.key, server.pem: servers' private key and certificate (PEM) + + * admin.key, admin.pem: admin's private key and certificate (PEM) + + * admin.pfx: admin's combined certificate (PKCS#12, export pw = "fubar") + + * tenant.key, tenant.pem: tenants' private key and certificate (PEM) + + * root.pem: "certificate authority" list for servers (multiple PEM). + +The last issue is who trusts whom. This is represented by which certificates +are loaded into an endpoint's "certificate authority" file (which in our case +actually includes the certificates themselves rather than the authorities). +The servers need to trust each other for internal calls, admins for external +ones, and tenants for the specific operations used when mounting. Thus, the +easiest thing to do is: + + cat server.pem admin.pem tenant.pem > root.pem + +Clients don't particularly need to trust anyone, so no action is needed there. + +== Appendix A: configuration for Firefox == + +To load the admin certificate in Firefox, do the following: + + * Select the "Edit/Preferences" menu item. + + * Go to the "Advanced" section, "Encryption" tab. + + * Click the "View Certificates" button. + + * Click the "Import..." button and select the .pfx file. + +== Appendix B: configuration for Chrome == + +To load the admin certificate in Chrome, do the following: + + * Select the "Preferences" item from the configuration menu (the little + spanner at the top right). + + * Click on "Under the Hood" + + * Scroll down to "Security" and click on the "Manage Certificates..." + button. + + * Click on the "Import..." button and select the .pfx file. + + diff --git a/scripts/cfs_add_directory.py b/scripts/cfs_add_directory.py index 1c9f0b9..a1b6585 100755 --- a/scripts/cfs_add_directory.py +++ b/scripts/cfs_add_directory.py @@ -6,7 +6,6 @@ import socket import string import sys import urllib -import urllib2
from bottle import request, template import cfs_utils @@ -101,19 +100,9 @@ def add_local (path): def run_common (path): node_list = cfs_utils.get_members() blob = [] - for node in node_list: - scratch = [node, []] - if socket.gethostbyname(node) in cfs_utils.local_addrs: - url_obj = [add_local(path)] - else: - url = "http://%s:%d/volumes/add_dir_local" % ( - node, cfs_paths.CLOUDFSD_PORT) - data = urllib.urlencode([("path",path)]) - url_obj = urllib2.urlopen(url,data=data) - for line in url_obj: - scratch[1].append(line) - blob.append(scratch) - return blob + data = urllib.urlencode([("path",path)]) + return cfs_utils.do_all(blob,node_list,"volumes/add_dir_local",data, + add_local, path)
def run_www (): path = request.forms.get("path") diff --git a/scripts/cfs_add_tenant.py b/scripts/cfs_add_tenant.py index d460367..83982ce 100755 --- a/scripts/cfs_add_tenant.py +++ b/scripts/cfs_add_tenant.py @@ -6,7 +6,6 @@ import socket import string import sys import urllib -import urllib2
from bottle import request, template import cfs_utils @@ -22,20 +21,9 @@ def add_local (tn_name, tn_pw): def run_common (tn_name, tn_pw): node_list = cfs_utils.get_members() blob = [] - for node in node_list: - scratch = [node, []] - if socket.gethostbyname(node) in cfs_utils.local_addrs: - url_obj = [add_local(tn_name,tn_pw)] - else: - url = "http://%s:%d/tenants/add_local" % ( - node, cfs_paths.CLOUDFSD_PORT) - data = urllib.urlencode([("tn_name",tn_name), - ("tn_pw",tn_pw)]) - url_obj = urllib2.urlopen(url,data=data) - for line in url_obj: - scratch[1].append(line) - blob.append(scratch) - return blob + data = urllib.urlencode([("tn_name",tn_name), ("tn_pw",tn_pw)]) + return cfs_utils.do_all(blob,node_list,"tenants/add_local",data, + add_local, tn_name, tn_pw)
def run_www (): tn_name = request.forms.get("tn_name") diff --git a/scripts/cfs_add_volume.py b/scripts/cfs_add_volume.py index 32bdc60..a00e2bc 100755 --- a/scripts/cfs_add_volume.py +++ b/scripts/cfs_add_volume.py @@ -6,7 +6,6 @@ import socket import string import sys import urllib -import urllib2
from bottle import request, template import cfs_paths @@ -29,19 +28,10 @@ def run_common (vname, vtype, vcount, bricks): if sts: return [["gluster",["command failed with %d"%sts]]] blob = [] - for node in cfs_utils.get_nodes_for_vol(vname): - scratch = [node,[]] - if socket.gethostbyname(node) in cfs_utils.local_addrs: - url_obj = [add_local(vname)] - else: - url = "http://%s:%d/volumes/add_vol_local" % ( - node, cfs_paths.CLOUDFSD_PORT) - data = urllib.urlencode([("vname",vname)]) - url_obj = urllib2.urlopen(url,data=data) - for line in url_obj: - scratch[1].append(line) - blob.append(scratch) - return blob + node_list = cfs_utils.get_nodes_for_vol(vname) + data = urllib.urlencode([("vname",vname)]) + return cfs_utils.do_all(blob,node_list,"volumes/add_vol_local",data, + add_local, vname)
def run_www (): volume_id = request.forms.get("VOLUMEID") diff --git a/scripts/cfs_delete_tenant.py b/scripts/cfs_delete_tenant.py index 92c11d7..e921783 100755 --- a/scripts/cfs_delete_tenant.py +++ b/scripts/cfs_delete_tenant.py @@ -5,7 +5,6 @@ import re import socket import string import sys -import urllib2
from bottle import request, template import cfs_utils @@ -21,18 +20,9 @@ def delete_local (tn_name): def run_common (tn_name): node_list = cfs_utils.get_members() blob = [] - for node in node_list: - scratch = [node, []] - if socket.gethostbyname(node) in cfs_utils.local_addrs: - url_obj = [delete_local(tn_name)] - else: - url = "http://%s:%d/tenants/%s/delete_local" % ( - node, cfs_paths.CLOUDFSD_PORT, tn_name) - url_obj = urllib2.urlopen(url) - for line in url_obj: - scratch[1].append(line) - blob.append(scratch) - return blob + return cfs_utils.do_all(blob,node_list, + "tenants/%s/delete_local" % tn_name, None, + delete_local, tn_name)
def run_www (tn_name): blob = run_common(tn_name) diff --git a/scripts/cfs_enable_tenant.py b/scripts/cfs_enable_tenant.py index 146c785..30eb848 100755 --- a/scripts/cfs_enable_tenant.py +++ b/scripts/cfs_enable_tenant.py @@ -6,7 +6,6 @@ import socket import string import sys import urllib -import urllib2
from bottle import request, template import cfs_paths @@ -36,20 +35,9 @@ def run_common (tn_name, vol_list): vol_list = string.join(vol_list,",") node_list = cfs_utils.get_members() blob = [] - for node in node_list: - scratch = [node, []] - if socket.gethostbyname(node) in cfs_utils.local_addrs: - url_obj = [enable_local(tn_name,vol_list)] - else: - url = "http://%s:%d/tenants/enable_local" % ( - node, cfs_paths.CLOUDFSD_PORT) - data = urllib.urlencode([("tn_name",tn_name), - ("vol_list",vol_list)]) - url_obj = urllib2.urlopen(url,data=data) - for line in url_obj: - scratch[1].append(line) - blob.append(scratch) - return blob + data = urllib.urlencode([("tn_name",tn_name), ("vol_list",vol_list)]) + return cfs_utils.do_all(blob,node_list,"tenants/enable_local",data, + enable_local, tn_name, vol_list)
def run_www (tn_name): vol_list = [] diff --git a/scripts/cfs_mount.py b/scripts/cfs_mount.py index af4ad06..b3226e0 100755 --- a/scripts/cfs_mount.py +++ b/scripts/cfs_mount.py @@ -1,5 +1,6 @@ #!/usr/bin/env python
+import httplib import json import os import sys @@ -9,6 +10,18 @@ import cfs_paths import cfs_utils import volfilter
+if cfs_utils.use_ssl: + # Copied from cfs_utils (see long "monkey patch" comment there, + # modified to add tenant certs instead of server. + def my_hacked_init (self, *args, **kwargs): + apply(httplib.HTTPConnection.__init__,(self,)+args,kwargs) + self.key_file = "tenant.key" + self.cert_file = "tenant.pem" + httplib.HTTPSConnection.__init__ = my_hacked_init + proto = "https" +else: + proto = "http" + # Simple cache of brick-to-port mappings, so we don't have to keep re-fetching # the maps from the same host if it has multiple bricks. class mapper: @@ -18,8 +31,8 @@ class mapper: if self.cache.has_key(host): mydict = self.cache[host] else: - url = "http://%s:%d/%s/map" % \ - (host, cfs_paths.CLOUDFSD_PORT, volume) + url = "%s://%s:%d/%s/map" % \ + (proto, host, cfs_paths.CLOUDFSD_PORT, volume) mydict = json.load(urllib2.urlopen(url)) self.cache[host] = mydict if mydict.has_key(subv): @@ -37,7 +50,8 @@ if __name__ == "__main__": (host, volume, username, password, mount) = sys.argv[1:6]
# Fetch the GlusterFS client-side volfile. - url = "http://%s:%d/%s/fetch" % (host, cfs_paths.CLOUDFSD_PORT, volume) + url = "%s://%s:%d/%s/fetch" % \ + (proto, host, cfs_paths.CLOUDFSD_PORT, volume) vol_file = urllib2.urlopen(url)
# Load the volfile and clean out some of the crud. diff --git a/scripts/cfs_paths.py b/scripts/cfs_paths.py index bf47ebd..2f9085b 100644 --- a/scripts/cfs_paths.py +++ b/scripts/cfs_paths.py @@ -10,4 +10,3 @@ idle_subdir = os.path.join(pid_dir,".idle_ports") used_subdir = os.path.join(pid_dir,".used_ports") volfile_re = re.compile("(?P<vol>[^.]+).(?P<node>.+).(?P<path>[^.]+).vol") CLOUDFSD_PORT = 8080 - diff --git a/scripts/cfs_rm_volume.py b/scripts/cfs_rm_volume.py index bfab9b2..b99cc2a 100755 --- a/scripts/cfs_rm_volume.py +++ b/scripts/cfs_rm_volume.py @@ -6,7 +6,6 @@ import socket import string import sys import urllib -import urllib2
from bottle import request, template import cfs_paths @@ -27,18 +26,9 @@ def run_common (vname): sts = kid.wait() if sts: return template("rm_vol_done.html", name=vname, blob=blob) - for node in nodes_for_vol: - scratch = [node,[]] - if socket.gethostbyname(node) in cfs_utils.local_addrs: - url_obj = [rm_local(vname)] - else: - url = "http://%s:%d/volumes/%s/rm_local" % ( - node, cfs_paths.CLOUDFSD_PORT, vname) - url_obj = urllib2.urlopen(url) - for line in url_obj: - scratch[1].append(line) - blob.append(scratch) - return blob + return cfs_utils.do_all(blob,nodes_for_vol, + "volumes/%s/rm_local" % vname, None, + rm_local, vname)
def run_www (vname): blob = run_common(vname) diff --git a/scripts/cfs_start_volume.py b/scripts/cfs_start_volume.py index 11c992b..a9112e6 100755 --- a/scripts/cfs_start_volume.py +++ b/scripts/cfs_start_volume.py @@ -7,7 +7,6 @@ import socket import string import subprocess import sys -import urllib2
from bottle import template
@@ -171,18 +170,9 @@ def start_local (vol_name): def run_common (vol_name): node_list = cfs_utils.get_nodes_for_vol(vol_name) blob = [] - for node in node_list: - scratch = [node, []] - if socket.gethostbyname(node) in cfs_utils.local_addrs: - url_obj = [start_local(vol_name)] - else: - url = "http://%s:%d/volumes/%s/start_local" % ( - node, cfs_paths.CLOUDFSD_PORT, vol_name) - url_obj = urllib2.urlopen(url) - for line in url_obj: - scratch[1].append(line) - blob.append(scratch) - return blob + return cfs_utils.do_all(blob,node_list, + "volumes/%s/start_local" % vol_name, None, + start_local, vol_name)
def run_www (vol_name): blob = run_common(vol_name) diff --git a/scripts/cfs_stop_volume.py b/scripts/cfs_stop_volume.py index b93b438..ec8126d 100755 --- a/scripts/cfs_stop_volume.py +++ b/scripts/cfs_stop_volume.py @@ -6,7 +6,6 @@ import os import socket import subprocess import sys -import urllib2
from bottle import template
@@ -46,18 +45,9 @@ def stop_local (vol_name): def run_common (vol_name): node_list = cfs_utils.get_nodes_for_vol(vol_name) blob = [] - for node in node_list: - scratch = [node, []] - if socket.gethostbyname(node) in cfs_utils.local_addrs: - url_obj = [stop_local(vol_name)] - else: - url = "http://%s:%d/volumes/%s/stop_local" % ( - node, cfs_paths.CLOUDFSD_PORT, vol_name) - url_obj = urllib2.urlopen(url) - for line in url_obj: - scratch[1].append(line) - blob.append(scratch) - return blob + return cfs_utils.do_all(blob,node_list, + "volumes/%s/stop_local" % vol_name, None, + stop_local, vol_name)
def run_www (vol_name): blob = run_common(vol_name) diff --git a/scripts/cfs_utils.py b/scripts/cfs_utils.py index 39a36e0..21db9ec 100644 --- a/scripts/cfs_utils.py +++ b/scripts/cfs_utils.py @@ -1,8 +1,10 @@ import dbm import glob +import httplib import os import socket import subprocess +import urllib2
import cfs_paths
@@ -15,6 +17,9 @@ good_fs_types = [ "ext2", "ext3", "ext4", "xfs", "btrfs" ] # Sudo is kind of pointless if we're already running as root. use_sudo = False
+# Disabled for developer convenience; turn on for production. +use_ssl = False + # We use the class cache to avoid having to call through the shell to find # executables on $PATH every single time. To do this, we use the shell *once* # to find the local path and assume all nodes are configured similarly. @@ -162,6 +167,50 @@ def open_db (): db_path = os.path.join(cfs_paths.info_dir,"config.db") return dbm.open(db_path,"c",0600)
+# Yes, this is nasty. My first choice would have been to pass the key/cert as +# arguments to urllib2.urlopen, but it doesn't give me that option. My second +# choice would have been to use a clean registration interface to have urllib2 +# create my httplib.HTTPSConnection the way I want, but that's created in +# AbtractHTTPHandler.do_open with fixed arguments, and there's no "hook" to +# add the needed values to the object before that very same code starts using +# it. Subclassing HTTPSConnection would mean also subclassing HTTPSHandler to +# use it, building a new OpenerDirector to use *that* subclass, and generally +# reinventing half of urllib2 to get one little bit of functionality. A pox +# on developers who use terms like "modular" and "abstract" in code that is +# in fact marked by bad linkage and hidden dependencies, to the point where +# even a monkey-patch like this is cleaner than their alternative. + +if use_ssl: + def my_hacked_init (self, *args, **kwargs): + apply(httplib.HTTPConnection.__init__,(self,)+args,kwargs) + self.key_file = "server.key" + self.cert_file = "server.pem" + httplib.HTTPSConnection.__init__ = my_hacked_init + +# Loop over a set of nodes, issuing a command either directly or via HTTP(S). +# "Blob" should be a list of [node,text...] and hence mutable, but it's also +# returned for convenience. +def do_all (blob, node_list, rmt_cmd, data, lcl_cmd, *args): + for node in node_list: + scratch = [node, []] + if socket.gethostbyname(node) in local_addrs: + url_obj = [apply(lcl_cmd,args)] + else: + if use_ssl: + url = "https://%s:%d/%s" % ( + node, cfs_paths.CLOUDFSD_PORT,rmt_cmd) + else: + url = "http://%s:%d/%s" % ( + node, cfs_paths.CLOUDFSD_PORT,rmt_cmd) + if data != None: + url_obj = urllib2.urlopen(url,data=data) + else: + url_obj = urllib2.urlopen(url) + for line in url_obj: + scratch[1].append(line) + blob.append(scratch) + return blob + # Print a "blob" of [node, [line1, line2]] tuples/lists for CLI/debugging. def print_blob (blob): for node, text in blob: diff --git a/scripts/cloudfsd.py b/scripts/cloudfsd.py index 8255a44..d081aed 100755 --- a/scripts/cloudfsd.py +++ b/scripts/cloudfsd.py @@ -1,11 +1,14 @@ #!/usr/bin/python
-from bottle import route, post, run, view, debug, request
import os import socket +import ssl import string
+import wsgiref.simple_server as ss +from bottle import route, post, run, view, debug, request, ServerAdapter, abort + import cfs_paths import cfs_utils import volmap @@ -20,16 +23,34 @@ import cfs_add_tenant import cfs_delete_tenant import cfs_enable_tenant
+def authorized (request, *users): + if not cfs_utils.use_ssl: + return True + path = request.environ["PATH_INFO"] + cert = request.environ["wsgi.input"]._sock._sslobj.peer_certificate() + for part in cert["subject"]: + if part[0][0] == "commonName": + print "got %s from %s" % (path, part[0][1]) + return (part[0][1] in users) + return False + +server_id = "The Server" +admin_id = "Super User" + @route("/") @route("/cfg") @route("/cfgmain") @view("cfgmain.html") def cfg_main(): + if not authorized(request,admin_id): + abort(401,"Forbidden") return dict()
@route("/cluster") @view("cluster.html") def show_cluster(): + if not authorized(request,admin_id): + abort(401,"Forbidden") # TBD: handle glusterd presence/startup check sanely cfs_utils.run_cmd("chkconfig","--add glusterd") cfs_utils.run_cmd("chkconfig","glusterd on") @@ -39,6 +60,8 @@ def show_cluster():
@post("/cluster/add_node") def add_node(): + if not authorized(request,admin_id): + abort(401,"Forbidden") return cfs_add_node.run_www()
# TBD: implement remove_node @@ -46,6 +69,8 @@ def add_node(): @route("/volumes") @view("volumes.html") def show_volumes(): + if not authorized(request,admin_id): + abort(401,"Forbidden") brick_list = cfs_utils.get_bricks() mount_list = cfs_utils.get_mounts(brick_list) # TBD: allow adding arbitrary directories instead of just mountpoints @@ -53,53 +78,77 @@ def show_volumes():
@post("/volumes/add_directory") def add_directory (): + if not authorized(request,admin_id): + abort(401,"Forbidden") return cfs_add_directory.run_www()
@post("/volumes/add_dir_local") def add_dir_local (): + if not authorized(request,server_id): + abort(401,"Forbidden") path = request.forms.get("path") return cfs_add_directory.add_local(path)
@post("/volumes/add_volume") def add_volume(): + if not authorized(request,admin_id): + abort(401,"Forbidden") return cfs_add_volume.run_www()
@post("/volumes/add_vol_local") def add_vol_local (): + if not authorized(request,server_id): + abort(401,"Forbidden") vname = request.forms.get("vname") return cfs_add_volume.add_local(vname)
@route("volumes/:name/remove") def rm_volume (name): + if not authorized(request,admin_id): + abort(401,"Forbidden") return cfs_rm_volume.run_www(name)
@route("/volumes/:name/rm_local") def rm_vol_local (name): + if not authorized(request,server_id): + abort(401,"Forbidden") return cfs_rm_volume.rm_local(name)
@route("/volumes/:vol_name/start") def start_volume(vol_name): + if not authorized(request,admin_id): + abort(401,"Forbidden") return cfs_start_volume.run_www(vol_name)
@route("/volumes/:vol_name/start_local") def start_local (vol_name): + if not authorized(request,server_id): + abort(401,"Forbidden") return cfs_start_volume.start_local(vol_name)
@route("/volumes/:vol_name/stop") def stop_volume(vol_name): + if not authorized(request,admin_id): + abort(401,"Forbidden") return cfs_stop_volume.run_www(vol_name)
@route("/volumes/:vol_name/stop_local") def stop_local (vol_name): + if not authorized(request,server_id): + abort(401,"Forbidden") return cfs_stop_volume.stop_local(vol_name)
# Used by mount.cloudfs @route("/:vol_name/fetch") def fetch_client_vf(vol_name): + if not authorized(request,"A Tenant"): + return "crap" vf_path = "%s/vols/%s/%s-fuse.vol" % (cfs_paths.gfs_dir, vol_name, vol_name) return open(vf_path,"r")
-# Used by mount.cloudfs +# Used by mount.cloudfs; for these two we don't really care who they are, so +# long as they do provide an identity that's part of this cluster. Merely +# getting past the SSL handshake using the tenant ID is therefore sufficient. @route("/:vol_name/map") def map_paths(vol_name): return volmap.vol_map(vol_name) @@ -107,30 +156,42 @@ def map_paths(vol_name): @route("/tenants") @view("tenants.html") def show_tenants (): + if not authorized(request,admin_id): + abort(401,"Forbidden") db_obj = cfs_utils.open_db() return dict(tenants=db_obj)
@post("/tenants/add") def add_tenant(): + if not authorized(request,admin_id): + abort(401,"Forbidden") return cfs_add_tenant.run_www()
@post("/tenants/add_local") def add_tenant_local (): + if not authorized(request,server_id): + abort(401,"Forbidden") tn_name = request.forms.get("tn_name") tn_pw = request.forms.get("tn_pw") return cfs_add_tenant.add_local(tn_name,tn_pw)
@route("/tenants/:name/delete") def delete_tenant (name): + if not authorized(request,admin_id): + abort(401,"Forbidden") return cfs_delete_tenant.run_www(name)
@route("/tenants/:tn_name/delete_local") def delete_tenant_local (tn_name): + if not authorized(request,server_id): + abort(401,"Forbidden") return cfs_delete_tenant.delete_local(tn_name)
@route("/tenants/:tn_name/volumes") @view("tenant_volumes.html") def show_tenant_volumes (tn_name): + if not authorized(request,admin_id): + abort(401,"Forbidden") db_obj = cfs_utils.open_db() all_vols = [v[3:] for v in db_obj.keys() if v.startswith("vt_")] print all_vols @@ -141,20 +202,59 @@ def show_tenant_volumes (tn_name):
@post("/tenants/:tn_name/enable") def enable_tenant_volumes (tn_name): + if not authorized(request,admin_id): + abort(401,"Forbidden") return cfs_enable_tenant.run_www(tn_name)
@post("/tenants/enable_local") def add_tenant_local (): + if not authorized(request,server_id): + abort(401,"Forbidden") tn_name = request.forms.get("tn_name") vol_list = request.forms.get("vol_list") return cfs_enable_tenant.enable_local(tn_name,vol_list)
+# We don't really care if anyone sees our styles. @route("/styles/:sheet") def get_style (sheet): return file("styles/%s"%sheet,"r")
+class SecureServer (ss.WSGIServer): + def server_activate (self): + ss.WSGIServer.server_activate(self) + self.socket = ssl.wrap_socket(self.socket,server_side=True, + certfile="server.pem", keyfile="server.key", + cert_reqs=ssl.CERT_REQUIRED, ca_certs="root.pem") + +class SecureHandler (ss.WSGIRequestHandler): + def handle (self): + for part in self.connection.getpeercert()["subject"]: + if part[0][0] == "commonName": + #print "### client is %s" % part[0][1] + break + else: + raise ssl.CertificateError, "no matching user" + ss.WSGIRequestHandler.handle(self) + # For some reason clients hang if we try to handle more than + # one request on a connection with WSGIServer (expecting a + # switch to non-SSL after a login page?) so we force a close + # here. + self.connection.shutdown(socket.SHUT_RDWR) + self.connection.close() + +class SecureAdapter (ServerAdapter): + def run (self, handler): + srv = ss.make_server(self.host,self.port,handler, + server_class=SecureServer,handler_class=SecureHandler, + **self.options) + srv.serve_forever() + if __name__ == "__main__": debug(True) #run(host='0.0.0.0',port=cfs_paths.CLOUDFSD_PORT,server=CherryPyServer) - run(host='0.0.0.0',port=cfs_paths.CLOUDFSD_PORT) + if cfs_utils.use_ssl: + run(host='0.0.0.0',port=cfs_paths.CLOUDFSD_PORT, + server=SecureAdapter) + else: + run(host='0.0.0.0',port=cfs_paths.CLOUDFSD_PORT)
diff --git a/scripts/ssl_examples/admin.key b/scripts/ssl_examples/admin.key new file mode 100644 index 0000000..bd1011d --- /dev/null +++ b/scripts/ssl_examples/admin.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDp/8qCWAKyJLDMPC+V6pDyngE+h+K/ZWA6hjIvjuOktRFEl36Y +mfM03L0JN4rjNS7JQkH4e2YWP9IrR5fKwlMwz1xqNaPSCFLwbUs52VUAPRT+LtQL +5bpi8VdwvqAlGJg1SazWjfsXyPSmhYhgCZi9aGApkUStnNAtJ7rgnOFfywIDAQAB +AoGAWnym0/ayvC7CC4huolt9x8RgGM01WuwZ5SfFumxYDXZTgiHPO7W0vclqdGj9 +FVWYjQ1JM4yMcqglXsUlpqu0vRrdt+WmsKZWhbJdUKIcKO3ztW8f++mWhYJINf/S +srHQzpDIIOTIUrTOsWqJtLZF2bOdeIShany3QhBlpCSobokCQQD859wvTppW3L9K +IggG5/G0cDLl5NyeE86pwlVDhoXqH90pMBJKLXvpiUHQ8DJ1LOdTTYbeodnQpdKV +/bB2da+lAkEA7Ny2eiZS4suULDYvX4ZKKUes5st++/ycp9oJWTaBsLkCdgTi7Gx9 +Lu8K87yzxGCF31d3Fsg2Iia1m07a5CO2rwJBAPyT44crjWCq3jB/hFzBUNfQJkUL +KHqHdaJ8/wKNWzjT0eDMrbd/bX1zI1Q0T3BPBEhh6Qx3wD1tHr/FuXLVXjECQGVl +SnxZ4YuS6JhQjdEPtLmdJEgcfvyyNAGq1eup6LTVTldlWhspiiFIVWDnu/Dp/QUR +9Tn8dSgDeCTHCe811qMCQGscWcY5yA85Ax379RJd7/3WXeUOF8ZMwuZsmbWVMKkO +QXiiLDMnCzumSaDRiUAZxAz1WQ2WmgyhXF3vbzmowAI= +-----END RSA PRIVATE KEY----- diff --git a/scripts/ssl_examples/admin.pem b/scripts/ssl_examples/admin.pem new file mode 100644 index 0000000..2d76c95 --- /dev/null +++ b/scripts/ssl_examples/admin.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICfDCCAeWgAwIBAgIJAKW4Tv+M90fKMA0GCSqGSIb3DQEBBQUAMFcxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQxEzARBgNVBAMMClN1cGVyIFVzZXIwHhcNMTEwNTE2MjAyMjEy +WhcNMTEwNjE1MjAyMjEyWjBXMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVs +dCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRMwEQYDVQQDDApT +dXBlciBVc2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDp/8qCWAKyJLDM +PC+V6pDyngE+h+K/ZWA6hjIvjuOktRFEl36YmfM03L0JN4rjNS7JQkH4e2YWP9Ir +R5fKwlMwz1xqNaPSCFLwbUs52VUAPRT+LtQL5bpi8VdwvqAlGJg1SazWjfsXyPSm +hYhgCZi9aGApkUStnNAtJ7rgnOFfywIDAQABo1AwTjAdBgNVHQ4EFgQUh/pgdfSK +cnALftl4NALNBHZ84FQwHwYDVR0jBBgwFoAUh/pgdfSKcnALftl4NALNBHZ84FQw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQASXLjNSj91lbrWpn8FpU1F +2X4EJEXNArKR+d1F/+WJxFSFQbyuhc0cmjuo30Z6Wzova1s2Icnw+EDgYVfXGdIC +OJuDrtN9uonNPGeVIq+WrbHQF06FNiBgxCdJcpiQG5J6K6jAFC6cJHfWPFvDBSz6 +MkOGZT7Q9qApCepdUct3OA== +-----END CERTIFICATE----- diff --git a/scripts/ssl_examples/admin.pfx b/scripts/ssl_examples/admin.pfx new file mode 100644 index 0000000..ac91a21 Binary files /dev/null and b/scripts/ssl_examples/admin.pfx differ diff --git a/scripts/ssl_examples/root.pem b/scripts/ssl_examples/root.pem new file mode 100644 index 0000000..ee2928a --- /dev/null +++ b/scripts/ssl_examples/root.pem @@ -0,0 +1,48 @@ +-----BEGIN CERTIFICATE----- +MIICfDCCAeWgAwIBAgIJAIBSkSFer4KiMA0GCSqGSIb3DQEBBQUAMFcxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQxEzARBgNVBAMMClRoZSBTZXJ2ZXIwHhcNMTEwNTE2MjAyMTU3 +WhcNMTEwNjE1MjAyMTU3WjBXMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVs +dCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRMwEQYDVQQDDApU +aGUgU2VydmVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDNGcrorj2eI6D +FUOu6fgOFAloTSrbfzhCosquyTznCi3MiswOrAWy3Y1aiL2aLSRURnmsaIoVC5FS +1bZoE63cFkTvv2rKoVmBWmWy497nb/h8DMrsfOT2Xn3y+u79Wt4RCkunRJEfnWsg +wrW6ULyxVAVeHJEtiudtHU0htAcR4wIDAQABo1AwTjAdBgNVHQ4EFgQUa2eD7AFo +sPsSX5nMBACmReQBTJIwHwYDVR0jBBgwFoAUa2eD7AFosPsSX5nMBACmReQBTJIw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBJM6ESfZzgb6qwuHB28niU +Azvf3zSPO2ji5C/gcPZa/vknxiaydP9c3JmwzLN1Qx7c5dHxLD5n9in1vcnJKR2m +THv9PkLYH0bIEehL+IBD8F4VfvL8ZAtHos1D+rnMeTtzsR4BqY0qzXjtu1/Q98qF +nhBvjaJoutWy9+nwxvPHHA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICfDCCAeWgAwIBAgIJAKW4Tv+M90fKMA0GCSqGSIb3DQEBBQUAMFcxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQxEzARBgNVBAMMClN1cGVyIFVzZXIwHhcNMTEwNTE2MjAyMjEy +WhcNMTEwNjE1MjAyMjEyWjBXMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVs +dCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRMwEQYDVQQDDApT +dXBlciBVc2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDp/8qCWAKyJLDM +PC+V6pDyngE+h+K/ZWA6hjIvjuOktRFEl36YmfM03L0JN4rjNS7JQkH4e2YWP9Ir +R5fKwlMwz1xqNaPSCFLwbUs52VUAPRT+LtQL5bpi8VdwvqAlGJg1SazWjfsXyPSm +hYhgCZi9aGApkUStnNAtJ7rgnOFfywIDAQABo1AwTjAdBgNVHQ4EFgQUh/pgdfSK +cnALftl4NALNBHZ84FQwHwYDVR0jBBgwFoAUh/pgdfSKcnALftl4NALNBHZ84FQw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQASXLjNSj91lbrWpn8FpU1F +2X4EJEXNArKR+d1F/+WJxFSFQbyuhc0cmjuo30Z6Wzova1s2Icnw+EDgYVfXGdIC +OJuDrtN9uonNPGeVIq+WrbHQF06FNiBgxCdJcpiQG5J6K6jAFC6cJHfWPFvDBSz6 +MkOGZT7Q9qApCepdUct3OA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICeDCCAeGgAwIBAgIJAPVYsIHSJcudMA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQxETAPBgNVBAMMCEEgVGVuYW50MB4XDTExMDUxNjIwMjIyM1oX +DTExMDYxNTIwMjIyM1owVTELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQg +Q2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDERMA8GA1UEAwwIQSBU +ZW5hbnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMNWfsmPx0y2zdrNCl3w +Puki+AYyYOzGPZRZeRBz5dFXxvJ0mdQMl1Mv4cbgjfaEnT+qT2vV17GFf7XWARd2 +oGLYfHZO2iINRDlyG5g/FDNkeUnVTm0ZBtsBo5hpX49C/MB1cHzmWAu5nopi8fhB +nej+rAP1GIZkUkWyWIG1jbbRAgMBAAGjUDBOMB0GA1UdDgQWBBTzqu/xb6WUQ2mz +bDwEGW9XfUEJ1DAfBgNVHSMEGDAWgBTzqu/xb6WUQ2mzbDwEGW9XfUEJ1DAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAHvZ0Az8LLfKc72FXxS6llSrlB40 +Y/D4XTHcBMaiFDZClJvYJ+UmqrxYCJH5py/YRd9GSp5Ry+ghPWg/+eFsSGFtQFTI +r+r70ux4lRYUk+adSqIiyg0xNGMqgOJKNNN5ixrCvN9k5BeeWyiKLbgcD+oG6sZM +ImJ71ZY4QkH1MLb+ +-----END CERTIFICATE----- diff --git a/scripts/ssl_examples/server.key b/scripts/ssl_examples/server.key new file mode 100644 index 0000000..b188d77 --- /dev/null +++ b/scripts/ssl_examples/server.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXwIBAAKBgQDDNGcrorj2eI6DFUOu6fgOFAloTSrbfzhCosquyTznCi3MiswO +rAWy3Y1aiL2aLSRURnmsaIoVC5FS1bZoE63cFkTvv2rKoVmBWmWy497nb/h8DMrs +fOT2Xn3y+u79Wt4RCkunRJEfnWsgwrW6ULyxVAVeHJEtiudtHU0htAcR4wIDAQAB +AoGBAIxEuqIzcr/BT37IJ/OnspTDNyNY2CQT3eScQBKrDnVi7hgd4JXmbM3jwDA/ +NCd2qrVYUxRdpmOLBWlFoqZVBcdvms4AlaOy9f6tMIwVt1FMgYp5GgPWKv2kZddR ++zwPBuwhkR41aGFAypsS9b5ZAdn5GP6ZfTw3vDtxA7vwoqU5AkEA8NwasAlFJy1E +ESlqG7GAe1YzjtkO+wu2YCCer5eXxE+G3b8s+QOnc6qaSnG6PABk5JptjUwFKOzo +m7ZuFk3I5wJBAM95nvVYHZBXutNEOZIINsxvaEMfwbpeQqTwWrGJQY55NpeRIzvg +lUGDiJNuOdRcWNA6gvSRAjHS1sH9FwSLI6UCQQDcVm4vlftmEHnRPVKtTN8ddUkr +J5QVwqwvGggw1/vlgV7+IjKRBm+8V1hYO9vDohSqMD+B4AZkXv2X3PaufrAHAkEA +rW4t+Uq6E9GyAy4xraeeHxA1qH6gU2i97uBX/7YLjcw3XUVenYvjWEtaXFs0jhbP +yuhOVZ/tpLZo/OnSVuL0XQJBAKQgA9RpoaFqSb4vWStlO54mKmzRA/xXVhDSzYYb +N0VC77hav1p527nfWAiMuSvuhOStcwGQ32sgWN5Ecgzrg/s= +-----END RSA PRIVATE KEY----- diff --git a/scripts/ssl_examples/server.pem b/scripts/ssl_examples/server.pem new file mode 100644 index 0000000..2c08fe3 --- /dev/null +++ b/scripts/ssl_examples/server.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICfDCCAeWgAwIBAgIJAIBSkSFer4KiMA0GCSqGSIb3DQEBBQUAMFcxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQxEzARBgNVBAMMClRoZSBTZXJ2ZXIwHhcNMTEwNTE2MjAyMTU3 +WhcNMTEwNjE1MjAyMTU3WjBXMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVs +dCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRMwEQYDVQQDDApU +aGUgU2VydmVyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDNGcrorj2eI6D +FUOu6fgOFAloTSrbfzhCosquyTznCi3MiswOrAWy3Y1aiL2aLSRURnmsaIoVC5FS +1bZoE63cFkTvv2rKoVmBWmWy497nb/h8DMrsfOT2Xn3y+u79Wt4RCkunRJEfnWsg +wrW6ULyxVAVeHJEtiudtHU0htAcR4wIDAQABo1AwTjAdBgNVHQ4EFgQUa2eD7AFo +sPsSX5nMBACmReQBTJIwHwYDVR0jBBgwFoAUa2eD7AFosPsSX5nMBACmReQBTJIw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBJM6ESfZzgb6qwuHB28niU +Azvf3zSPO2ji5C/gcPZa/vknxiaydP9c3JmwzLN1Qx7c5dHxLD5n9in1vcnJKR2m +THv9PkLYH0bIEehL+IBD8F4VfvL8ZAtHos1D+rnMeTtzsR4BqY0qzXjtu1/Q98qF +nhBvjaJoutWy9+nwxvPHHA== +-----END CERTIFICATE----- diff --git a/scripts/ssl_examples/tenant.key b/scripts/ssl_examples/tenant.key new file mode 100644 index 0000000..9124136 --- /dev/null +++ b/scripts/ssl_examples/tenant.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDDVn7Jj8dMts3azQpd8D7pIvgGMmDsxj2UWXkQc+XRV8bydJnU +DJdTL+HG4I32hJ0/qk9r1dexhX+11gEXdqBi2Hx2TtoiDUQ5chuYPxQzZHlJ1U5t +GQbbAaOYaV+PQvzAdXB85lgLuZ6KYvH4QZ3o/qwD9RiGZFJFsliBtY220QIDAQAB +AoGAChPLRLDDMmT358LONxxu0m44Z3Fv6KcthBq9kSi5gXxKyjVTvknMAMGmnzD0 +gfRDfIZXxJNqpkDh3sqkkcZP7dJ0VVGk6swoV24kvt5vtai6dqVD9klmSApDE3Ec +KsAwJShRp9O2sBrtXMrtq1Q79SfmufJE0vcE73cfqQLijvUCQQDhof+ynTPWHPf6 +LxU6kl/MUaEG1MDQdS2WmUUrwjImWmBJjaPkpnpVX+eFenQt+nd1IpZQwuBWbTeP +pVZ6z6x/AkEA3aC1Tk6da2NBR8Ef9zZ7DDhdVkQZXVkouGohQNMOyxg9wrqhelkR +dWWm2ykNqYlxZYEcZc87m5G6xDH8KvM0rwJBAK7WWAuw0r0EH4dmun1zdPYe/rcL +Xwlo81VyGO5qgW/Esj3smmYQNlU3hnCgzavfHHfQwEd+alWuNdKCXLu3dsMCQDQ7 +DJ+AzX6ibJ8Rd4wWTddqbSzIbcXfHkaf1GhnlSPt+ZgrzaR82y10oGcj/LFIz+2h +COVBeoXGSWK1eP1SRccCQQC+16Mrw/C9ZWCu+1LUtCWTjoP/S82ooRTF6Ug3XIo6 +sROrv9p85+vf0AUF7s07soh4XrVFa6Cy4u8C/vxsmokT +-----END RSA PRIVATE KEY----- diff --git a/scripts/ssl_examples/tenant.pem b/scripts/ssl_examples/tenant.pem new file mode 100644 index 0000000..593c56f --- /dev/null +++ b/scripts/ssl_examples/tenant.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICeDCCAeGgAwIBAgIJAPVYsIHSJcudMA0GCSqGSIb3DQEBBQUAMFUxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQxETAPBgNVBAMMCEEgVGVuYW50MB4XDTExMDUxNjIwMjIyM1oX +DTExMDYxNTIwMjIyM1owVTELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQg +Q2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDERMA8GA1UEAwwIQSBU +ZW5hbnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMNWfsmPx0y2zdrNCl3w +Puki+AYyYOzGPZRZeRBz5dFXxvJ0mdQMl1Mv4cbgjfaEnT+qT2vV17GFf7XWARd2 +oGLYfHZO2iINRDlyG5g/FDNkeUnVTm0ZBtsBo5hpX49C/MB1cHzmWAu5nopi8fhB +nej+rAP1GIZkUkWyWIG1jbbRAgMBAAGjUDBOMB0GA1UdDgQWBBTzqu/xb6WUQ2mz +bDwEGW9XfUEJ1DAfBgNVHSMEGDAWgBTzqu/xb6WUQ2mzbDwEGW9XfUEJ1DAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAHvZ0Az8LLfKc72FXxS6llSrlB40 +Y/D4XTHcBMaiFDZClJvYJ+UmqrxYCJH5py/YRd9GSp5Ry+ghPWg/+eFsSGFtQFTI +r+r70ux4lRYUk+adSqIiyg0xNGMqgOJKNNN5ixrCvN9k5BeeWyiKLbgcD+oG6sZM +ImJ71ZY4QkH1MLb+ +-----END CERTIFICATE-----
cloudfs-devel@lists.fedorahosted.org