diff options
| -rw-r--r-- | README.rst | 33 | ||||
| -rw-r--r-- | common/changes/create_db_cmd | 2 | ||||
| -rw-r--r-- | common/src/leap/soledad/common/command.py | 17 | ||||
| -rw-r--r-- | common/src/leap/soledad/common/couch.py | 19 | ||||
| -rw-r--r-- | common/src/leap/soledad/common/tests/test_command.py | 4 | ||||
| -rw-r--r-- | common/src/leap/soledad/common/tests/test_couch.py | 4 | ||||
| -rw-r--r-- | server/changes/create_db_cmd | 3 | ||||
| -rwxr-xr-x | server/pkg/create-user-db | 16 | 
8 files changed, 79 insertions, 19 deletions
| @@ -55,3 +55,36 @@ to run tests in development mode you must do the following::  Note that to run CouchDB tests, be sure you have ``CouchDB`` installed on your  system. + + +Privileges +----- +In order to prevent privilege escalation, Soledad should not be run as a +database administrator. This implies the following side effects: + +----------------- +Database creation: +----------------- +Can be done via a script located in ``server/pkg/create-user-db`` +It reads a netrc file that should be placed on +``/etc/couchdb/couchdb-admin.netrc``. +That file holds the admin credentials in netrc format and should be accessible +only by 'soledad-admin' user. + +The debian package will do the following in order to automate this: + +* create a user ``soledad-admin`` +* make this script available as ``create-user-db`` in ``/usr/bin`` +* grant restricted sudo access, that only enables user ``soledad`` to call this +  exact command via ``soledad-admin`` user. + +The server side process, configured via ``/etc/leap/soledad-server.conf``, will +then use a parameter called 'create_cmd' to know which command is used to +allocate new databases. All steps of creation process is then handled +automatically by the server, following the same logic as u1db server. + +------------------ +Database deletion: +------------------ +No code at all handles this and privilege to do so needs to be removed as +explained before. This can be automated via a simple cron job. diff --git a/common/changes/create_db_cmd b/common/changes/create_db_cmd new file mode 100644 index 00000000..00bbdf71 --- /dev/null +++ b/common/changes/create_db_cmd @@ -0,0 +1,2 @@ +  o Add a sanitized command executor for database creation and re-enable +  user database creation on CouchServerState via command line. diff --git a/common/src/leap/soledad/common/command.py b/common/src/leap/soledad/common/command.py index 978cec91..811bf135 100644 --- a/common/src/leap/soledad/common/command.py +++ b/common/src/leap/soledad/common/command.py @@ -24,26 +24,27 @@ Utility to sanitize and run shell commands.  import subprocess -def exec_validated_cmd(cmd, args, validator=None): +def exec_validated_cmd(cmd, argument, validator=None):      """ -    Executes cmd, validating args with validator. +    Executes cmd, validating argument with a validator function.      :param cmd: command.      :type dbname: str -    :param args: arguments. -    :type args: str -    :param validator: optional function to validate args +    :param argument: argument. +    :type argument: str +    :param validator: optional function to validate argument      :type validator: function      :return: exit code and stdout or stderr (if code != 0)      :rtype: (int, str)      """ -    if validator and not validator(args): +    if validator and not validator(argument):          return 1, "invalid argument"      command = cmd.split(' ') -    command.append(args) +    command.append(argument)      try: -        process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) +        process = subprocess.Popen(command, stdout=subprocess.PIPE, +                                   stderr=subprocess.PIPE)      except OSError, e:          return 1, e      (out, err) = process.communicate() diff --git a/common/src/leap/soledad/common/couch.py b/common/src/leap/soledad/common/couch.py index d9ed5026..4c5f6400 100644 --- a/common/src/leap/soledad/common/couch.py +++ b/common/src/leap/soledad/common/couch.py @@ -435,7 +435,7 @@ class CouchDatabase(CommonBackend):              self._set_replica_uid(replica_uid)          if ensure_ddocs:              self.ensure_ddocs_on_db() -            self.ensure_security() +            self.ensure_security_ddoc()          self._cache = None      @property @@ -468,10 +468,15 @@ class CouchDatabase(CommonBackend):                          getattr(ddocs, ddoc_name)))                  self._database.save(ddoc) -    def ensure_security(self): +    def ensure_security_ddoc(self):          """          Make sure that only soledad user is able to access this database as -        a member. +        an unprivileged member, meaning that administration access will +        be forbidden even inside an user database. +        The goal is to make sure that only the lowest access level is given +        to the unprivileged CouchDB user set on the server process. +        This is achieved by creating a _security design document, see: +        http://docs.couchdb.org/en/latest/api/database/security.html          """          security = self._database.security          security['members'] = {'names': ['soledad'], 'roles': []} @@ -1386,12 +1391,9 @@ class CouchSyncTarget(CommonSyncTarget):              source_replica_transaction_id) -DB_NAME_MASK = "^user-[a-f0-9]+$" - -  def is_db_name_valid(name):      """ -    Validate a user database using DB_NAME_MASK. +    Validate a user database using a regular expression.      :param name: database name.      :type name: str @@ -1399,7 +1401,8 @@ def is_db_name_valid(name):      :return: boolean for name vailidity      :rtype: bool      """ -    return re.match(DB_NAME_MASK, name) is not None +    db_name_regex = "^user-[a-f0-9]+$" +    return re.match(db_name_regex, name) is not None  class CouchServerState(ServerState): diff --git a/common/src/leap/soledad/common/tests/test_command.py b/common/src/leap/soledad/common/tests/test_command.py index af4903eb..420f91ae 100644 --- a/common/src/leap/soledad/common/tests/test_command.py +++ b/common/src/leap/soledad/common/tests/test_command.py @@ -50,4 +50,6 @@ class ExecuteValidatedCommandTest(unittest.TestCase):      def test_return_status_code_number_on_failure(self):          status, out = exec_validated_cmd("ls", "user-bebacafe")          self.assertEquals(status, 2) -        self.assertIn('ls: cannot access user-bebacafe: No such file or directory\n', out) +        self.assertIn( +            'ls: cannot access user-bebacafe: No such file or directory\n', +            out) diff --git a/common/src/leap/soledad/common/tests/test_couch.py b/common/src/leap/soledad/common/tests/test_couch.py index 3622bb56..d1a07a3a 100644 --- a/common/src/leap/soledad/common/tests/test_couch.py +++ b/common/src/leap/soledad/common/tests/test_couch.py @@ -1504,11 +1504,11 @@ class CouchDatabaseExceptionsTests(CouchDBTestCase):      def test_ensure_security_doc(self):          """          Ensure_security creates a _security ddoc to ensure that only soledad -        will have member access to a db. +        will have the lowest privileged access to an user db.          """          self.create_db(ensure=False)          self.assertFalse(self.db._database.security) -        self.db.ensure_security() +        self.db.ensure_security_ddoc()          security_ddoc = self.db._database.security          self.assertIn('admins', security_ddoc)          self.assertFalse(security_ddoc['admins']['names']) diff --git a/server/changes/create_db_cmd b/server/changes/create_db_cmd new file mode 100644 index 00000000..cee0a935 --- /dev/null +++ b/server/changes/create_db_cmd @@ -0,0 +1,3 @@ +  o Adds a new config parameter 'create_cmd', which allows sysadmin to specify +  which command will create a database. That command was added in +  pkg/create-user-db and debian package automates steps needed for sudo access. diff --git a/server/pkg/create-user-db b/server/pkg/create-user-db index edcb8a82..dd68f792 100755 --- a/server/pkg/create-user-db +++ b/server/pkg/create-user-db @@ -1,4 +1,20 @@  #!/usr/bin/env python +# -*- coding: utf-8 -*- +# create-user-db +# Copyright (C) 2015 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>.  import os  import sys  import netrc | 
