From 234cf79b0ce8c147bfff580b51d138058e94956c Mon Sep 17 00:00:00 2001 From: gholt Date: Sat, 14 Aug 2010 09:46:32 -0700 Subject: [PATCH] Cyberduck Support and How To --- bin/swift-auth-create-account | 4 +- bin/swift-auth-recreate-accounts | 3 +- doc/source/howto_cyberduck.rst | 141 ++++++++++++++++++++++++++ doc/source/howto_cyberduck_config.png | Bin 0 -> 40503 bytes doc/source/index.rst | 7 ++ etc/proxy-server.conf-sample | 1 + swift/common/auth.py | 4 +- swift/common/bufferedhttp.py | 29 ++++-- 8 files changed, 177 insertions(+), 12 deletions(-) create mode 100644 doc/source/howto_cyberduck.rst create mode 100644 doc/source/howto_cyberduck_config.png diff --git a/bin/swift-auth-create-account b/bin/swift-auth-create-account index ccaf93cf5f..cdc085d754 100755 --- a/bin/swift-auth-create-account +++ b/bin/swift-auth-create-account @@ -36,8 +36,10 @@ if __name__ == '__main__': conf = dict(c.items('auth-server')) host = conf.get('bind_ip', '127.0.0.1') port = int(conf.get('bind_port', 11000)) + ssl = conf.get('cert_file') is not None path = '/account/%s/%s' % (new_account, new_user) - conn = http_connect(host, port, 'PUT', path, {'x-auth-key':new_password}) + conn = http_connect(host, port, 'PUT', path, {'x-auth-key':new_password}, + ssl=ssl) resp = conn.getresponse() if resp.status == 204: print resp.getheader('x-storage-url') diff --git a/bin/swift-auth-recreate-accounts b/bin/swift-auth-recreate-accounts index 51212b4f4f..3bdd1e43ee 100755 --- a/bin/swift-auth-recreate-accounts +++ b/bin/swift-auth-recreate-accounts @@ -31,8 +31,9 @@ if __name__ == '__main__': conf = dict(c.items('auth-server')) host = conf.get('bind_ip', '127.0.0.1') port = int(conf.get('bind_port', 11000)) + ssl = conf.get('cert_file') is not None path = '/recreate_accounts' - conn = http_connect(host, port, 'POST', path) + conn = http_connect(host, port, 'POST', path, ssl=ssl) resp = conn.getresponse() if resp.status == 200: print resp.read() diff --git a/doc/source/howto_cyberduck.rst b/doc/source/howto_cyberduck.rst new file mode 100644 index 0000000000..135272d9b4 --- /dev/null +++ b/doc/source/howto_cyberduck.rst @@ -0,0 +1,141 @@ +=============================== +Talking to Swift with Cyberduck +=============================== + +.. note:: + Put together by Caleb Tennis, thanks Celeb! + + +#. Install Swift, or have credentials for an existing Swift installation. If + you plan to install Swift on your own server, follow the general guidelines + in the section following this one. + +#. Verify you can connect using the standard Swift Tool `st` from your + "public" URL (yes I know this resolves privately inside EC2):: + + ubuntu@domU-12-31-39-03-CD-06:/home/swift/swift/bin$ st -A https://ec2-184-72-156-130.compute-1.amazonaws.com:11000/v1.0 -U a3:b3 -K c3 stat + Account: 06228ccf-6d0a-4395-889e-e971e8de8781 + Containers: 0 + Objects: 0 + Bytes: 0 + + .. note:: + + The Swift Tool `st` can be copied from Swift sources to most any + machine with Python installed. You can grab it from + http://bazaar.launchpad.net/%7Ehudson-openstack/swift/trunk/annotate/head%3A/bin/st + if you don't have the Swift code handy. + +#. Download and extract the Cyberduck sources (3.5.1 as of this writing). They + should be available at http://trac.cyberduck.ch/ + +#. Edit the Cyberduck source. Look for lib/cloudfiles.properties, and edit + this file. Change auth_url to your public auth URL (note the https):: + + auth_url=https://ec2-184-72-156-130.compute-1.amazonaws.com:11000/v1.0 + +#. Edit source/ch/cyberduck/core/Protocol.java. Look for the line saying + "storage.clouddrive.com". Just above that, change:: + + public boolean isHostnameConfigurable() { + return true; + } + +#. In the root directory, run "make" to rebuild Cyberduck. When done, type: + `open build/Release/Cyberduck.app/` to start the program. + +#. Go to "Open Connection", select Rackspace Cloud Files, and connect. + + .. image:: howto_cyberduck_config.png + +#. If you get SSL errors, make sure your auth and proxy server are both setup + for SSL. If you get certificate errors (specifically, 'unable to find valid + certification path to requested target'), you are using a self signed + certificate, you need to perform a few more steps: + + .. note:: + + For some folks, just telling the OS to trust the cert works fine, for + others use the following steps. + +#. As outlined here: http://blogs.sun.com/andreas/entry/no_more_unable_to_find, + download http://blogs.sun.com/andreas/resource/InstallCert.java, run "javac + InstallCert.java" to compile it, then run "java InstallCert + https://your-auth-server-url:8080". This script will pull down that + certificate and put it into a Java cert store, in your local directory. The + file is jssecacerts. + +#. You need to move that file to $JAVA_HOME/jre/lib/security, so your java run + time picks it up. + +#. Restart Cyberduck, and it should now allow you to use that certificate + without an error. + + +--------------------------------------- +Installing Swift For Use With Cyberduck +--------------------------------------- + +#. Both the proxy and auth servers will ultimately need to be running with + SSL. You will need a key and certificate to do this, self signed is ok (but + a little more work getting Cyberduck to accept it). Put these in + /etc/swift/cert.crt and /etc/swift/cert.key. + + .. note:: + + Creating a self-signed cert can usually be done with:: + + cd /etc/swift + openssl req -new -x509 -nodes -out cert.crt -keyout cert.key + +#. Example proxy-server config:: + + [proxy-server] + bind_port = 8080 + user = swift + cert_file = /etc/swift/cert.crt + key_file = /etc/swift/cert.key + + [auth-server] + ssl = true + +#. Example auth-server config:: + + [auth-server] + default_cluster_url = https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1 + user = swift + cert_file = /etc/swift/cert.crt + key_file = /etc/swift/cert.key + +#. Use swift-auth-create-account to create a new account:: + + ubuntu@domU-12-31-39-03-CD-06:/home/swift/swift/bin$ swift-auth-create-account a3 b3 c3 + https://ec2-184-72-156-130.compute-1.amazonaws.com:8080/v1/06228ccf-6d0a-4395-889e-e971e8de8781 + + .. note:: + It's important that the URL that is given back to you be accessible + publicly. This URL is tied to this account, and will be served + back to Cyberduck after authorization. If this URL gives back + something like: http://127.0.0.1/v1/... this won't work, because + Cyberduck will attempt to connect to 127.0.0.1. + + This URL is specified in the auth-server config's + default_cluster_url. However, once you have created an + account/user, this URL is fixed and won't change even if you change + that configuration item. You will have to use sqlite to manually + edit the auth.db in order to change it (limitation of using the + development auth server, but perhaps someone will patch in this + ability someday). + +#. Verify you can connect using the standard Swift Tool `st`:: + + ubuntu@domU-12-31-39-03-CD-06:/home/swift/swift/bin$ st -A https://127.0.0.1:11000/v1.0 -U a3:b3 -K c3 stat + Account: 06228ccf-6d0a-4395-889e-e971e8de8781 + Containers: 0 + Objects: 0 + Bytes: 0 + +.. note:: + + Please let me know if you find any changes that need to be made: ctennis on + IRC diff --git a/doc/source/howto_cyberduck_config.png b/doc/source/howto_cyberduck_config.png new file mode 100644 index 0000000000000000000000000000000000000000..1612bbe0e6aedc62ae284f4d2f1aa6ceeea66afc GIT binary patch literal 40503 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 literal 0 HcmV?d00001 diff --git a/doc/source/index.rst b/doc/source/index.rst index 60d26dbe32..3461a9083c 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -41,6 +41,13 @@ Deployment: deployment_guide admin_guide +End User Guides: + +.. toctree:: + :maxdepth: 1 + + howto_cyberduck + Source: .. toctree:: diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index 33d612c724..f548852722 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -35,4 +35,5 @@ # class = swift.common.auth.DevAuthMiddleware # ip = 127.0.0.1 # port = 11000 +# ssl = false # node_timeout = 10 diff --git a/swift/common/auth.py b/swift/common/auth.py index 6830b60ba7..5e4e7d1716 100644 --- a/swift/common/auth.py +++ b/swift/common/auth.py @@ -35,6 +35,8 @@ class DevAuthMiddleware(object): self.conf = conf self.auth_host = conf.get('ip', '127.0.0.1') self.auth_port = int(conf.get('port', 11000)) + self.ssl = \ + conf.get('ssl', 'false').lower() in ('true', 'on', '1', 'yes') self.timeout = int(conf.get('node_timeout', 10)) def __call__(self, env, start_response): @@ -78,7 +80,7 @@ class DevAuthMiddleware(object): try: with Timeout(self.timeout): conn = http_connect(self.auth_host, self.auth_port, 'GET', - '/token/%s/%s' % (account, token)) + '/token/%s/%s' % (account, token), ssl=self.ssl) resp = conn.getresponse() resp.read() conn.close() diff --git a/swift/common/bufferedhttp.py b/swift/common/bufferedhttp.py index 94d1ba1073..6b308e5b01 100644 --- a/swift/common/bufferedhttp.py +++ b/swift/common/bufferedhttp.py @@ -30,8 +30,8 @@ from urllib import quote import logging import time -from eventlet.green.httplib import HTTPConnection, HTTPResponse, _UNKNOWN, \ - CONTINUE, HTTPMessage +from eventlet.green.httplib import CONTINUE, HTTPConnection, HTTPMessage, \ + HTTPResponse, HTTPSConnection, _UNKNOWN class BufferedHTTPResponse(HTTPResponse): @@ -106,10 +106,11 @@ class BufferedHTTPConnection(HTTPConnection): def http_connect(ipaddr, port, device, partition, method, path, - headers=None, query_string=None): + headers=None, query_string=None, ssl=False): """ - Helper function to create a HTTPConnection object that is buffered - for backend Swift services. + Helper function to create an HTTPConnection object. If ssl is set True, + HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection + will be used, which is buffered for backend Swift services. :param ipaddr: IPv4 address to connect to :param port: port to connect to @@ -119,9 +120,13 @@ def http_connect(ipaddr, port, device, partition, method, path, :param path: request path :param headers: dictionary of headers :param query_string: request query string + :param ssl: set True if SSL should be used (default: False) :returns: HTTPConnection object """ - conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port)) + if ssl: + conn = HTTPSConnection('%s:%s' % (ipaddr, port)) + else: + conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port)) path = quote('/' + device + '/' + str(partition) + path) if query_string: path += '?' + query_string @@ -135,9 +140,11 @@ def http_connect(ipaddr, port, device, partition, method, path, def http_connect_raw(ipaddr, port, method, path, headers=None, - query_string=None): + query_string=None, ssl=False): """ - Helper function to create a HTTPConnection object that is buffered. + Helper function to create an HTTPConnection object. If ssl is set True, + HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection + will be used, which is buffered for backend Swift services. :param ipaddr: IPv4 address to connect to :param port: port to connect to @@ -145,9 +152,13 @@ def http_connect_raw(ipaddr, port, method, path, headers=None, :param path: request path :param headers: dictionary of headers :param query_string: request query string + :param ssl: set True if SSL should be used (default: False) :returns: HTTPConnection object """ - conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port)) + if ssl: + conn = HTTPSConnection('%s:%s' % (ipaddr, port)) + else: + conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port)) if query_string: path += '?' + query_string conn.path = path