diff --git a/README.ssl b/README.ssl index 6452d0f..c9fc702 100644 --- a/README.ssl +++ b/README.ssl @@ -4,7 +4,9 @@ This file describes how to encrypt your connection to the CryptoBox webserver. This is highly recommended as the encryption password for your data could be exposed to intruders in your local network otherwise. -There are several ways for setting up a SSL connection: +Below you will find detailed descriptions on how to set up an encrypted +connection to the webinterface: + - use the plugin "encrypted_webinterface" - run the CryptoBox webserver behind an ssl-enabled webserver - use stunnel to provide an SSL socket - use the a proxy server (e.g. pound) @@ -17,7 +19,27 @@ need encrypted http connections. ------------------------------------------------------------------- -1) CryptoBox behind an ssl-enabled webserver +1) using the plugin 'encrypted_webinterface' + This plugin is disabled by default. You can enable it in your + cryptobox.conf file by removing it from the 'DisabledPlugins' setting. + + The plugin does the following during startup of the CryptoBox: + - create a self-signed X.509 certificate if necessary + - run stunnel on port 443 (https) with this certificate + + Now you just need to point your browser to the URL of the CryptoBox with + 'https' instead of 'http' and to accept the certificate permanently + (the Internet Explorer is not capable of doing this - use Firefox, + Konqueror, Safari, ... instead, if you need this ability). That's it! + + Of course, this will not work, if the port 443 is already in use by + another program - in this case, you should better choose one of the + other solutions described below. + + +------------------------------------------------------------------- + +2) CryptoBox behind an ssl-enabled webserver Read the documentation of your favourite webserver to learn how to enable ssl encryption. @@ -38,16 +60,16 @@ need encrypted http connections. ------------------------------------------------------------------- -2) CryptoBox behind stunnel +3) CryptoBox behind stunnel (configured manually) You may want to tunnel the traffic between the cryptobox-server and your browser. "stunnel" is an excellent candidate for this job. If you do not have an ssl certificate yet, then you should create one first. On Debian: "apt-get install ssl-cert" and run the following - command (replace the ; a default CERT_CONF is shipped with the - cryptobox-server package): + command (the supplied example openssl.conf file resides in the doc + directory of the cryptobox-server package): - make-ssl-cert + make-ssl-cert conf-examples/openssl.conf In case, that you already have a certificate just run this command: @@ -58,9 +80,10 @@ need encrypted http connections. ------------------------------------------------------------------- -3) CryptoBox behind a proxy server +4) CryptoBox behind a proxy server As there are many proxy servers around, we cannot describe all of them. As - an example, we will explain the setup of the load-balancing proxy 'pound'. + an example, we will explain the setup of the load-balancing proxy 'pound' + (http://www.apsis.ch/pound/). Just add the following lines to you /etc/pound/pound.cfg: # Remove the X-SSL-Request header from incoming @@ -77,13 +100,15 @@ need encrypted http connections. ------------------------------------------------------------------- -4) Problems with SSL detection? +5) Problems with SSL detection? If the CryptoBox continues to complain about the unencrypted connection, even if it runs behind an ssl-enabled webserver or behind stunnel, then you can do one of the following things: + - disable the plugin 'encypted_webinterface' in the cryptobox.conf file + if you do not need it - set the request header value "X-SSL-Request" to "1" (the digit 'one') - set the environment setting "HTTPS" to a non-empty value during the - startup of the CryptoBox webserver. Maybe /etc/default/cryptobox-server - would be the right place for this. + startup of the CryptoBox webserver. Maybe + /etc/default/cryptobox-server would be the right place for this. - let the CryptoBox webserver listen to port 443 diff --git a/bin/coding_guidelines.txt b/bin/coding_guidelines.txt deleted file mode 100644 index a6fb47c..0000000 --- a/bin/coding_guidelines.txt +++ /dev/null @@ -1,18 +0,0 @@ -Maybe we can add some notes here to get a consistent coding experience :) - -------------------------------------------------------------------------------- - -comments: - - should be usable for pydoc - - ''' or """ at the beginning of every class/method - - ## for longterm comments, that are useful for understanding - - #blabla for codelines, that are out for experimenting and might be used later again - -error handling: - - unspecific error handling is evil (try: "grep -r except: .") - -unit testing: - - first write a unittest and then write the relating code until the unittest stops failing :) - - 'unittests.ClassName.py' should contain all tests for 'ClassName.py' - - commits with broken unit tests are evil (fix or disable the code (not the test ;) )) - diff --git a/plugins/encrypted_webinterface/encrypted_webinterface.py b/plugins/encrypted_webinterface/encrypted_webinterface.py index 364a84c..78870c0 100644 --- a/plugins/encrypted_webinterface/encrypted_webinterface.py +++ b/plugins/encrypted_webinterface/encrypted_webinterface.py @@ -69,9 +69,12 @@ class encrypted_webinterface(cryptobox.plugins.base.CryptoBoxPlugin): def get_status(self): - """Retrieve the status of the feature. + """Retrieve the current state of the webinterface connection """ - return "TODO" + if self.__is_encrypted(): + return "1" + else: + return "0" def get_warnings(self): @@ -85,21 +88,32 @@ class encrypted_webinterface(cryptobox.plugins.base.CryptoBoxPlugin): warnings.append((45, "Plugins.%s.MissingModuleM2Crypto" % self.get_name())) if not os.path.isfile(self.root_action.STUNNEL_BIN): warnings.append((44, "Plugins.%s.MissingProgramStunnel" % self.get_name())) - ## perform some checks for encrypted connections - ## check an environment setting - this is quite common behind proxies - ## check if it is a local connection (or via stunnel) - ## the arbitrarily chosen header is documented in README.proxy - if (cherrypy.request.scheme != "https") \ - and (not os.environ.has_key("HTTPS")) \ - and (not (cherrypy.request.headers.has_key("Remote-Host") \ - and (cherrypy.request.headers["Remote-Host"] == "127.0.0.1"))) \ - and (not (cherrypy.request.headers.has_key("X-SSL-Request") \ - and (cherrypy.request.headers["X-SSL-Request"] == "1"))): + if not self.__is_encrypted(): ## plaintext connection -> "heavy security risk" (priority=20..39) warnings.append((25, "Plugins.%s.NoSSL" % self.get_name())) return warnings + def __is_encrypted(self): + """perform some checks for encrypted connections + """ + if cherrypy.request.scheme == "https": + return True + ## check an environment setting - this is quite common behind proxies + if os.environ.has_key("HTTPS"): + return True + ## check if it is a local connection (or via stunnel) + if cherrypy.request.headers.has_key("Remote-Host") \ + and (cherrypy.request.headers["Remote-Host"] == "127.0.0.1"): + return True + ## the arbitrarily chosen header is documented in README.proxy + if cherrypy.request.headers.has_key("X-SSL-Request") \ + and (cherrypy.request.headers["X-SSL-Request"] == "1"): + return True + ## it looks like a plain connection + return False + + def handle_event(self, event, event_info=None): """Create a certificate during startup (if it does not exist) and run stunnel """ @@ -107,12 +121,14 @@ class encrypted_webinterface(cryptobox.plugins.base.CryptoBoxPlugin): cert_abs_name = self.cbox.prefs.get_misc_config_filename(CERT_FILENAME) if not os.path.isfile(cert_abs_name): try: - self.__create_certificate(cert_abs_name) - self.cbox.log.info("Created new SSL certificate: %s" % cert_abs_name) + cert = self.__get_certificate() + self.cbox.prefs.create_misc_config_file(CERT_FILENAME, cert) + self.cbox.log.info("Created new SSL certificate: %s" % \ + cert_abs_name) except IOError, err_msg: ## do not run stunnel without a certificate - self.cbox.log.warn("Failed to create new SSL certificate (%s): %s" % \ - (cert_abs_name, err_msg)) + self.cbox.log.warn("Failed to create new SSL certificate (%s): %s" \ + % (cert_abs_name, err_msg)) return self.__run_stunnel(cert_abs_name) elif event == "shutdown": @@ -181,8 +197,8 @@ class encrypted_webinterface(cryptobox.plugins.base.CryptoBoxPlugin): return False - def __create_certificate(self, filename): - """Create a self-signed certificate and store it in a file + def __get_certificate(self): + """Create a self-signed certificate and return its pem content The code is mainly inspired by: https://dev.tribler.org/browser/m2crypto/trunk/contrib/SimpleX509create.py @@ -226,17 +242,5 @@ class encrypted_webinterface(cryptobox.plugins.base.CryptoBoxPlugin): result = "" result += cert.as_pem() result += pkey.as_pem(cipher=None) - if not os.path.exists(os.path.dirname(filename)): - os.mkdir(os.path.dirname(filename)) - try: - certfile = open(filename, "w") - except IOError: - raise - try: - certfile.write(result) - except IOError: - certfile.close() - raise - certfile.close() - os.chmod(filename, 0600) + return result diff --git a/src/cryptobox/core/settings.py b/src/cryptobox/core/settings.py index 1f0ca8a..5e2a3af 100644 --- a/src/cryptobox/core/settings.py +++ b/src/cryptobox/core/settings.py @@ -100,6 +100,28 @@ class CryptoBoxSettings: 'name' should not contain slashes (no directory part!) """ return os.path.join(self.prefs["Locations"]["SettingsDir"], "misc", name) + + + def create_misc_config_file(self, name, content): + """Create a new configuration file in the 'settings' directory + + "name" should be the basename (without a directory) + "content" will be directly written to the file + this method may throw an IOException + """ + misc_conf_file = self.get_misc_config_filename(name) + misc_conf_dir = os.path.dirname(misc_conf_file) + if not os.path.isdir(misc_conf_dir): + os.mkdir(misc_conf_dir) + cfile = open(misc_conf_file, "w") + try: + cfile.write(content) + except IOError: + cfile.close() + raise + cfile.close() + ## reread all misc files automatically - this should be ok + self.reload_misc_files() def requires_partition(self):