def update_ldap_users_from_ad():
    # Setup the ldap connection
    dn = "cn=admin,dc=canvas,dc=virtue,dc=com"
    ldap = LDAP('', '')
    ldap.get_ldap_connection()
    # FIXME - Figure out a soluton for the Hardcoded password
    # TODO - Fix Hardcoded password for ldap and AD connection
    ldap.conn.simple_bind_s(dn, "Test123!")

    # Bind the AD connection
    ad = LDAP('*****@*****.**', 'Test123!')
    ad.bind_ad()

    # Query AD for all users
    ad_users = ad.query_ad('objectClass', 'user')

    ldap_usernames = get_ldap_usernames(ldap)

    # Go through all users in AD
    for user in ad_users:
        cn = user[1]['cn'][0]
        ad_username = user[1]['sAMAccountName'][0]
        # If a username in AD is not found in ldap then add it
        if ad_username not in ldap_usernames:
            new_user = {
                'username': ad_username,
                'authorizedRoleIds': [],
                'name': cn
            }
            new_ldap_user = ldap_tools.to_ldap(new_user, 'OpenLDAPuser')
            ldap.add_obj(new_ldap_user, 'users', 'cusername', throw_error=True)

            # Check to make sure the key directory exists
            if (not os.path.exists('{0}/user-keys'.format(
                    os.environ['HOME']))):
                os.mkdir('{0}/user-keys'.format(os.environ['HOME']))

            # Create/Add keys for each user
            subprocess.check_call([
                'ssh-keygen', '-t', 'rsa', '-f',
                '{}/user-keys/{}.pem'.format(os.environ['HOME'], ad_username),
                '-C', '"For Virtue user {0}"'.format(ad_username), '-N', ''
            ])

            # Create a dummy kerberos file for the user.
            # It'll be regenerated as soon as the user logs in.
            with open('/tmp/krb5cc_{}'.format(ad_username), 'w') as f:
                f.write("Error 404: Easter Egg text not found")

    return ldap
Exemple #2
0
class EndPoint_Security:

    rethinkdb_host = 'rethinkdb.galahad.com'
    ca_cert = '/var/private/ssl/rethinkdb_cert.pem'
    excalibur_key_file = '/var/private/ssl/excalibur_private_key.pem'
    virtue_key_dir = os.environ['HOME'] + '/user-keys'
    wait_for_ack = 30  # seconds

    def __init__(self, user, password):

        self.inst = LDAP(user, password)
        dn = 'cn=admin,dc=canvas,dc=virtue,dc=com'
        self.inst.get_ldap_connection()
        self.inst.conn.simple_bind_s(dn, 'Test123!')

        self.rdb_manager = RethinkDbManager()

        self.conn = None

    def transducer_list(self):
        '''
        Lists all transducers currently available in the system

        Args:

        Returns:
            list: Transducer objects for known transducers
        '''

        try:
            transducers_raw = self.inst.get_objs_of_type('OpenLDAPtransducer')
            if transducers_raw is None:
                return self.__error(
                    'unspecifiedError',
                    details='Unable to get transducer objects from LDAP')

            transducers_ret = []

            for transducer in transducers_raw:
                ldap_tools.parse_ldap(transducer[1])
                transducers_ret.append(transducer[1])

            return json.dumps(transducers_ret)

        except Exception as e:
            return self.__error('unspecifiedError',
                                details='Unable to get transducer objects: ' +
                                str(e))

    def transducer_get(self, transducerId):
        '''
        Gets information about the indicated Transducer. Does not include information about any
        instantiation in Virtues.

        Args:
            transducerId (str): The ID of the Transducer to get.

        Returns:
            Transducer: information about the indicated transducer
        '''

        try:
            transducer = self.inst.get_obj('cid', transducerId,
                                           'openLDAPtransducer', True)
            if transducer is None or transducer == ():
                return self.__error('invalidTransducerId')
            ldap_tools.parse_ldap(transducer)

            if DEBUG_PERMISSIONS:
                return json.dumps(transducer)

            return json.dumps(transducer)

        except Exception as e:
            return self.__error('unspecifiedError',
                                details='Unable to get transducer object: ' +
                                str(e))

    def transducer_enable(self, transducerId, virtueId, configuration):
        '''
        Enables the indicated Transducer in the indicated Virtue.

        Args:
            transducerId (str) : The ID of the Transducer to enable.
            virtueId (str)     : The ID of the Virtue in which to enable the Transducer.
            configuration (object): The configuration to apply to the Transducer when it is enabled. Format
                        is Transducer-specific. This overrides any existing configuration with the same keys.

        Returns:
            bool: True if the transducer was enabled, false otherwise

        '''
        ret = self.__enable_disable(transducerId, virtueId, configuration,
                                    True)
        if type(ret) is bool and ret == True:
            return self.__error('success')
        return ret

    def transducer_disable(self, transducerId, virtueId):
        '''
        Disables the indicated Transducer in the indicated Virtue

        Args:
            transducerId (str) : The ID of the Transducer to disable
            virtueId (str)     : The ID of the Virtue in which to enable the Transducer.

        Returns:
            bool: True if the transducer was enabled, false otherwise
        '''
        ret = self.__enable_disable(transducerId, virtueId, None, False)
        if type(ret) is bool and ret == True:
            return self.__error('success')
        return ret

    def transducer_get_enabled(self, transducerId, virtueId):
        '''
        Gets the current enabled status for the indicated Transducer in the indicated Virtue.

        Args:
            transducerId (str) : The ID of the Transducer
            virtueId (str)     : The ID of the Virtue in which to enable the Transducer.

        Returns:
            bool: True if the Transducer is enabled in the Virtue, false if it is not

        '''

        if self.conn is None:
            self.__connect_rethinkdb()
        try:
            row = r.db('transducers').table('acks')\
                .get([virtueId, transducerId]).run(self.conn)
        except r.ReqlError as e:
            return self.__error(
                'unspecifiedError',
                details='Failed to get info about transducer: ' + str(e))

        # Transducers are enabled by default
        if row is None:
            return json.dumps({'enabled': True})

        verified = self.__verify_message(row)
        if (verified != True):
            return verified

        return json.dumps({'enabled': row['enabled']})

    def transducer_get_configuration(self, transducerId, virtueId):
        '''
        Gets the current configuration for the indicated Transducer in the indicated Virtue.

        Args:
            transducerId (str) : The ID of the Transducer
            virtueId (str)     : The ID of the Virtue in which to enable the Transducer.

        Returns:
            TransducerConfig: Configuration information for the indicated Transducer in the indicated Virtue
        '''

        if self.conn is None:
            self.__connect_rethinkdb()
        try:
            row = r.db('transducers').table('acks')\
                .get([virtueId, transducerId]).run(self.conn)
        except r.ReqlError as e:
            return self.__error(
                'unspecifiedError',
                details='Failed to get info about transducer: ' + str(e))

        # If there's no row, there's no set config
        if row is None:
            return json.dumps(None)

        verified = self.__verify_message(row)
        if (verified != True):
            return verified

        # By definition, the configuration is a JSON object
        return row['configuration']

    def transducer_list_enabled(self, virtueId):
        '''
        Lists all Transducers currently that are currently enabled in the indicated Virtue.

        Args:
            virtueId (str)     : The ID of the Virtue in which to enable the Transducer.

        Returns:
            list(Transducer): List of enabled transducers within the specified Virtue
        '''

        enabled_transducers = []

        if self.conn is None:
            self.__connect_rethinkdb()
        try:
            for row in r.db('transducers').table('acks')\
                .filter({'virtue_id': virtueId}).run(self.conn):

                verified = self.__verify_message(row)
                if (verified != True):
                    return verified

                if ('enabled' in row) and row['enabled']:
                    enabled_transducers.append(row['transducer_id'])

        except r.ReqlError as e:
            return self.__error('unspecifiedError',
                                details='Failed to get enabled transducers: ' +
                                str(e))

        return json.dumps(enabled_transducers)

    def transducer_all_virtues(self, transducerId, configuration, isEnabled):
        '''
        Sets the given transducer to true for all running and stopped virtues.
        Updates roles and starting configuration to match, so additionally created
        or started virtues will share the same config.

        :param transducerId:
        :param isEnabled::o
        :return:
        '''

        try:
            ret = self.__update_roles_transducer(transducerId, isEnabled)
            if ret != True:
                return ret

            ret = self.__update_transducer_starting_config(
                transducerId, configuration)
            if ret != True:
                return ret

            virtues_raw = self.inst.get_objs_of_type('OpenLDAPvirtue')
            if (virtues_raw == None):
                return json.dumps(ErrorCodes.user['unspecifiedError'])

            for virtue in virtues_raw:
                ldap_tools.parse_ldap(virtue[1])
                ret = self.__enable_disable(transducerId, virtue[1]['id'],
                                            configuration, isEnabled)
                if (ret != True):
                    return json.dumps(ret)

            return self.__error('success')
        except:
            print('Error:\n{0}'.format(traceback.format_exc()))
            return json.dumps(ErrorCodes.user['unspecifiedError'])

    def __connect_rethinkdb(self):
        # RethinkDB connection
        # This connection will fail if setup_rethinkdb.py hasn't been run, because
        # there won't be an excalibur user and it won't have the specified password.
        with open(self.excalibur_key_file, 'r') as f:
            key = f.read()
            self.excalibur_key = RSA.importKey(key)
            try:
                self.conn = r.connect(host=self.rethinkdb_host,
                                      user='******',
                                      password=key,
                                      ssl={'ca_certs': self.ca_cert})
            except r.ReqlDriverError as e:
                return self.__error('unspecifiedError', details=\
                    'Failed to connect to RethinkDB at host: ' + \
                    self.rethinkdb_host + ' because: ' + str(e))
        return True

    def __enable_disable(self, transducerId, virtueId, configuration,
                         isEnable):

        # Make sure transducer exists
        transducer = self.inst.get_obj('cid', transducerId,
                                       'openLDAPtransducer', True)
        if transducer is None or transducer == ():
            return self.__error('invalidTransducerId')

        ldap_tools.parse_ldap(transducer)

        # Make sure virtue exists
        virtue = self.inst.get_obj('cid', virtueId, 'openLDAPvirtue', True)
        if virtue is None or virtue == ():
            return self.__error('invalidVirtueId')
        ldap_tools.parse_ldap(virtue)
        virtue_running = (virtue['state'] == 'RUNNING')

        # Change the ruleset
        ret = self.__change_ruleset(virtueId,
                                    transducerId,
                                    transducer['type'],
                                    isEnable,
                                    virtue_running,
                                    config=configuration)
        if ret != True:
            return ret

        if virtue_running:
            # Update the virtue's list of transducers
            # Call loads because transducer_list_enabled returns a string
            new_t_list = json.loads(self.transducer_list_enabled(virtueId))
            if type(new_t_list) is dict and new_t_list['status'] == 'failed':
                # Couldn't retrieve new list of transducers
                return self.__error(
                    'unspecifiedError',
                    details='Unable to update virtue\'s list of transducers')

            virtue['transducerIds'] = new_t_list
            ret = self.inst.modify_obj(
                'cid', virtueId, ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue'),
                'OpenLDAPvirtue', True)

        else:
            # Update list of transducers in LDAP without syncing it
            # with rethink (Non-running virutes are not in rethink)
            new_t_list = virtue['transducerIds']

            if isEnable:
                if transducerId not in new_t_list:
                    new_t_list.append(transducerId)
            else:
                if transducerId in new_t_list:
                    new_t_list.remove(transducerId)

            virtue['transducerIds'] = new_t_list
            ret = self.inst.modify_obj(
                'cid', virtueId, ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue'),
                'OpenLDAPvirtue', True)

        if ret != 0:
            return self.__error(
                'unspecifiedError',
                details='Unable to update virtue\'s list of transducers')

        return True

    def __sign_message(self, row):
        required_keys = [
            'virtue_id', 'transducer_id', 'type', 'configuration', 'enabled',
            'timestamp'
        ]
        if not all([(key in row) for key in required_keys]):
            return (False,
                self.__error('unspecifiedError', details='Missing required keys in row: ' +\
                str(filter((lambda key: key not in row), required_keys))))

        message = '|'.join([
            row['virtue_id'], row['transducer_id'], row['type'],
            str(row['configuration']),
            str(row['enabled']),
            str(row['timestamp'])
        ])
        h = SHA.new(str(message))
        signer = PKCS1_v1_5.new(self.excalibur_key)
        signature = signer.sign(h)
        return (True, signature)

    def __verify_message(self, row):
        if row is None:
            return self.__error('unspecifiedError',
                                details='No match found in database')

        required_keys = [
            'virtue_id', 'transducer_id', 'type', 'configuration', 'enabled',
            'timestamp', 'signature'
        ]
        if not all([(key in row) for key in required_keys]):
            return self.__error('unspecifiedError', details='Missing required keys in row: ' +\
                str(filter((lambda key: key not in row), required_keys)))

        message = '|'.join([
            row['virtue_id'], row['transducer_id'], row['type'],
            str(row['configuration']),
            str(row['enabled']),
            str(row['timestamp'])
        ])

        virtue_public_key = os.path.join(self.virtue_key_dir,
                                         row['virtue_id'] + '.pem.pub')
        if not os.path.isfile(virtue_public_key):
            return self.__error('invalidOrMissingParameters', details=\
                'No file found for Virtue public key at: ' + \
                virtue_public_key)
        with open(virtue_public_key) as f:
            virtue_key = RSA.importKey(f.read())

        h = SHA.new(str(message))
        verifier = PKCS1_v1_5.new(virtue_key)
        verified = verifier.verify(h, row['signature'])
        if not verified:
            printable_msg = deepcopy(row)
            del printable_msg['signature']
            return self.__error('unspecifiedError', details=\
                'Unable to validate signature of ACK message: ' + \
                json.dumps(printable_msg, indent=2))

        return True

    def __change_ruleset(self,
                         virtue_id,
                         trans_id,
                         transducer_type,
                         enable,
                         virtue_running,
                         config=None):
        if self.conn is None:
            ret = self.__connect_rethinkdb()
            # Return if error
            if ret != True:
                return ret

        if type(transducer_type) is list:
            transducer_type = transducer_type[0]

        timestamp = int(time.time())

        row = {
            'id': [virtue_id, trans_id],
            'virtue_id': virtue_id,
            'transducer_id': trans_id,
            'type': transducer_type,
            'configuration': config,
            'enabled': enable,
            'timestamp': timestamp
        }
        (success, signature) = self.__sign_message(row)
        if not success:
            # Return error code
            return signature

        row['signature'] = r.binary(signature)

        # Send command to change ruleset
        try:
            res = r.db('transducers').table('commands')\
                .insert(row, conflict='replace').run(self.conn)
            if res['errors'] > 0:
                return self.__error(
                    'unspecifiedError',
                    details=
                    'Failed to insert into commands table; first error: ' +
                    res['first_error'])
        except r.ReqlError as e:
            return self.__error(
                'unspecifiedError',
                details='Failed to insert into commands table: ' + str(e))

        # If the virtue isn't running yet, don't bother waiting for an ACK
        if not virtue_running:
            return True

        # Wait for ACK from the virtue that the ruleset has been changed
        # try:
        cursor = r.db('transducers').table('acks')\
            .get([virtue_id, trans_id])\
            .changes(squash=False).run(self.conn)
        # except r.ReqlError as e:
        #       print 'ERROR: Failed to read from the ACKs table because:', e
        #       return False

        retry = True
        while retry:
            try:
                retry = False
                # Wait max 30 seconds - if we miss the real ACK, hopefully
                # at least the next heartbeat will suffice
                print 'INFO: Waiting for ACK'
                change = cursor.next(wait=self.wait_for_ack)
                row = change['new_val']

                verified = self.__verify_message(row)
                if (verified != True):
                    return verified

                if row['timestamp'] >= timestamp:
                    if row['enabled'] == enable:
                        print 'INFO: ACK received!'
                        return True
                    else:
                        return self.__error(
                            'unspecifiedError',
                            details=
                            'Received ACK with incorrect value for enabled: ' +
                            str(enable) + ' vs ' + str(row['enabled']))
                else:
                    print 'WARN: Timestamp incorrect:', timestamp, row[
                        'timestamp']
                    # Retry once in case that was just a wayward ACK
                    retry = True

            except (r.ReqlCursorEmpty, r.ReqlDriverError) as e:
                return self.__error(
                    'unspecifiedError',
                    details='Failed to receive ACK before timeout')
            finally:
                cursor.close()
        return self.__error('unspecifiedError',
                            details='Failed to receive ACK before timeout')

    def __error(self, key, details=None):
        if key not in ErrorCodes.security:
            print "Error type '" + key + "' not found!  Using UnspecifiedError."
            key = 'unspecifiedError'
        e = deepcopy(ErrorCodes.security[key])
        if details is not None:
            # e['details'] = details
            e['result'].append(details)
        return json.dumps(e)

    def __update_roles_transducer(self, transducerId, isEnabled):
        # Make sure transducer exists
        transducer = self.inst.get_obj('cid', transducerId,
                                       'openLDAPtransducer', True)
        if transducer is None or transducer == ():
            return self.__error('invalidTransducerId')

        try:
            ldap_roles = self.inst.get_objs_of_type('OpenLDAProle')
            assert ldap_roles != None

            roles = ldap_tools.parse_ldap_list(ldap_roles)

            for role in roles:
                new_t_list = role['startingTransducerIds']

                if isEnabled:
                    if transducerId not in new_t_list:
                        new_t_list.append(transducerId)
                else:
                    if transducerId in new_t_list:
                        new_t_list.remove(transducerId)

                role['startingTransducerIds'] = new_t_list
                ret = self.inst.modify_obj(
                    'cid', role['id'],
                    ldap_tools.to_ldap(role,
                                       'OpenLDAProle'), 'OpenLDAProle', True)
                if ret != 0:
                    return self.__error(
                        'unspecifiedError',
                        details='Unable to update virtue\'s list of transducers'
                    )
            return True

        except Exception as e:
            print('Error:\n{0}'.format(traceback.format_exc()))
            return json.dumps(ErrorCodes.admin['unspecifiedError'])

    def __update_transducer_starting_config(self, transducerId, configuration):
        transducer = self.inst.get_obj('cid', transducerId,
                                       'openLDAPtransducer', True)
        if transducer is None or transducer == ():
            return self.__error('invalidTransducerId')

        ldap_tools.parse_ldap(transducer)
        transducer['startingConfiguration'] = configuration

        ret = self.inst.modify_obj(
            'cid', transducer['id'],
            ldap_tools.to_ldap(transducer, 'openLDAPtransducer'),
            'openLDAPtransducer', True)
        if ret != 0:
            return self.__error(
                'unspecifiedError',
                details='Unable to update virtue\'s list of transducers')

        return True
Exemple #3
0
class AssembleRoleThread(threading.Thread):
    def __init__(self,
                 ldap_user,
                 ldap_password,
                 role,
                 base_img_name,
                 use_ssh=True):
        super(AssembleRoleThread, self).__init__()

        self.inst = LDAP(ldap_user, ldap_password)

        # Going to need to write to LDAP
        # self.inst.bind_ldap()
        dn = 'cn=admin,dc=canvas,dc=virtue,dc=com'
        self.inst.get_ldap_connection()
        self.inst.conn.simple_bind_s(dn, 'Test123!')

        self.role = role

        self.base_img_name = base_img_name
        self.use_ssh = use_ssh

    def run(self):

        self.role['state'] = 'CREATING'

        ldap_role = ldap_tools.to_ldap(self.role, 'OpenLDAProle')
        ret = self.inst.add_obj(ldap_role, 'roles', 'cid', throw_error=True)

        assert ret == 0

        virtue_path = 'images/non_provisioned_virtues/' + self.role[
            'id'] + '.img'
        key_path = USER_KEY_DIR + '/default-virtue-key.pem'

        valor_manager = ValorManager()

        try:

            # Create the role standby image files
            standby_roles = StandbyRoles(self.base_img_name, self.role)
            standby_roles.create_role_image_file()

            if (self.use_ssh):
                # Launch by adding a 'virtue' to RethinkDB
                valor = valor_manager.get_empty_valor()
                virtue_ip = valor_manager.add_virtue(
                    valor['address'], valor['valor_id'], self.role['id'],
                    virtue_path, True)  # send role_create=True)

                time.sleep(5)

                # Get Docker login command
                # Use the AWS Account ID of the Account which has the docker registry,
                # currently this is in the StarLab account.
                docker_cmd = subprocess.check_output(
                    shlex.split(
                        'aws ecr get-login --registry-ids {} --no-include-email '
                        '--region us-east-2'.format(AWS_ECR_ACCOUNT_NUMBER)))

                print('docker_cmd: ' + docker_cmd)

                # Run assembler
                assembler = Assembler(work_dir='{0}/.{1}_assembly'.format(
                    os.environ['HOME'], self.role['id']))
                assembler.assemble_running_vm(self.role['applicationIds'],
                                              docker_cmd, key_path, virtue_ip)

            # Create the virtue standby image files
            standby_virtues = StandbyVirtues(self.role['id'])
            standby_virtues.create_standby_virtues()

            self.role['state'] = 'CREATED'
            ldap_role = ldap_tools.to_ldap(self.role, 'OpenLDAProle')
            ret = self.inst.modify_obj('cid',
                                       self.role['id'],
                                       ldap_role,
                                       objectClass='OpenLDAProle',
                                       throw_error=True)

            assert ret == 0

        except:
            print('Error while assembling role {0}:\n{1}'.format(
                self.role['id'], traceback.format_exc()))

            self.role['state'] = 'FAILED'
            ldap_role = ldap_tools.to_ldap(self.role, 'OpenLDAProle')
            ret = self.inst.modify_obj('cid',
                                       self.role['id'],
                                       ldap_role,
                                       objectClass='OpenLDAProle',
                                       throw_error=True)
        finally:
            if valor_manager.rethinkdb_manager.get_virtue(self.role['id']):
                valor_manager.rethinkdb_manager.remove_virtue(self.role['id'])
Exemple #4
0
class CreateVirtueThread(threading.Thread):
    def __init__(self,
                 ldap_user,
                 ldap_password,
                 role_id,
                 user,
                 virtue_id,
                 role=None):
        super(CreateVirtueThread, self).__init__()

        self.inst = LDAP(ldap_user, ldap_password)

        self.role_id = role_id
        self.role = role

        self.username = user
        self.virtue_id = virtue_id

    def run(self):

        thread_list.append(self)

        # Going to need to write to LDAP
        # self.inst.bind_ldap()
        dn = 'cn=admin,dc=canvas,dc=virtue,dc=com'
        self.inst.get_ldap_connection()
        self.inst.conn.simple_bind_s(dn, 'Test123!')

        if (self.role == None):
            role = self.inst.get_obj('cid',
                                     self.role_id,
                                     objectClass='OpenLDAProle')
            if (role == () or role == None):
                return
            ldap_tools.parse_ldap(role)
        else:
            role = self.role

        virtue = {
            'id': self.virtue_id,
            'username': self.username,
            'roleId': self.role_id,
            'applicationIds': [],
            'resourceIds': role['startingResourceIds'],
            'transducerIds': role['startingTransducerIds'],
            'networkRules': role['networkRules'],
            'state': 'CREATING',
            'ipAddress': 'NULL'
        }
        ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue')
        assert self.inst.add_obj(ldap_virtue, 'virtues', 'cid') == 0

        virtue_path = 'images/provisioned_virtues/' + virtue['id'] + '.img'

        try:

            # For now generate keys and store in local dir
            subprocess.check_output(
                shlex.split(('ssh-keygen -t rsa -f {0}/{1}.pem -C'
                             ' "Virtue Key for {1}" -N ""').format(
                                 USER_KEY_DIR, virtue['id'])))

            with open(USER_KEY_DIR + '/' + virtue['id'] + '.pem',
                      'r') as virtue_key_file:
                virtue_key = virtue_key_file.read().strip()
            with open(GALAHAD_KEY_DIR + '/excalibur_pub.pem',
                      'r') as excalibur_key_file:
                excalibur_key = excalibur_key_file.read().strip()

            user_key = subprocess.check_output([
                'ssh-keygen', '-y', '-f',
                '{0}/{1}.pem'.format(USER_KEY_DIR, self.username)
            ])

            with open(GALAHAD_KEY_DIR + '/rethinkdb_cert.pem',
                      'r') as rdb_cert_file:
                rdb_cert = rdb_cert_file.read().strip()

            with open('/tmp/networkRules-' + virtue['id'],
                      'w+') as iprules_file:
                for rule in role['networkRules']:
                    iprules_file.write(rule + '\n')

            # Create the virtue standby image files
            standby_virtues = StandbyVirtues(self.role_id)
            standby_virtues.create_virtue_image_file(self.virtue_id)

            subprocess.check_call(['sudo', 'python',
                                   os.environ['HOME'] + '/galahad/excalibur/'
                                   + \
                                   'call_provisioner.py',
                                   '-u', self.username,
                                   '-i', virtue['id'],
                                   '-n', '/tmp/networkRules-' + virtue['id'],
                                   '-b',
                                   '/mnt/efs/images/non_provisioned_virtues/' +
                                   role['id'] + '.img',
                                   '-o', '/mnt/efs/' + virtue_path,
                                   '-v', virtue_key,
                                   '-e', excalibur_key,
                                   '--user_key', user_key,
                                   '-r', rdb_cert])

            # Enable/disable sensors as specified by the role
            transducers = {}
            for tid in role['startingTransducerIds']:
                transducer = self.inst.get_obj('cid', tid,
                                               'OpenLDAPtransducer', True)
                if (transducer == ()):
                    continue
                ldap_tools.parse_ldap(transducer)
                transducers[tid] = transducer

            eps = EndPoint_Security(self.inst.email, self.inst.password)
            all_transducers = json.loads(eps.transducer_list())
            for transducer in all_transducers:
                if transducer['id'] in transducers:
                    config = transducer['startingConfiguration']
                    ret = eps.transducer_enable(transducer['id'],
                                                self.virtue_id, config)
                else:
                    ret = eps.transducer_disable(transducer['id'],
                                                 self.virtue_id)

            virtue['state'] = 'STOPPED'
            ldap_virtue = ldap_tools.to_ldap(virtue, 'OpenLDAPvirtue')
            assert self.inst.modify_obj('cid', virtue['id'], ldap_virtue) == 0
        except:
            print('Error while creating Virtue for role {0}:\n{1}'.format(
                role['id'], traceback.format_exc()))
            assert self.inst.del_obj('cid',
                                     virtue['id'],
                                     objectClass='OpenLDAPvirtue',
                                     throw_error=True) == 0
            os.remove('{0}/{1}.pem'.format(USER_KEY_DIR, virtue['id']))
            os.remove('{0}/{1}.pem.pub'.format(USER_KEY_DIR, virtue['id']))
            os.remove('/mnt/efs/' + virtue_path)