Пример #1
0
def backup_dir_ok(backup_dir=None):
    '''
        Verify the backup dir can be written to.

        >>> from blockchain_backup.bitcoin.tests import utils as test_utils
        >>> test_utils.init_database()
        >>> backup_dir_ok(backup_dir='/tmp/bitcoin/backup')
        (True, None)
        >>> ok, error_message = backup_dir_ok(backup_dir='/test')
        >>> ok
        False
        >>> error_message.startswith('Unable to create /test as')
        True
        >>> backup_dir_ok(backup_dir=get_data_dir())
        (False, 'The backup and data directories must be different.')
    '''
    user = whoami()
    error = None

    if backup_dir is None:
        backup_dir = get_backup_dir()

    if backup_dir == get_data_dir():
        ok = False
        error = 'The backup and data directories must be different.'
    else:
        if os.path.exists(backup_dir):
            dir_preexists = True
        else:
            dir_preexists = False
            try:
                os.makedirs(backup_dir)
            except:  # 'bare except' because it catches more than "except Exception"
                error = f'Unable to create {backup_dir} as {user}'

        if os.path.exists(backup_dir):
            try:
                filename = os.path.join(backup_dir, '.test')
                with open(filename, "wt") as output_file:
                    output_file.write('test')
                os.remove(filename)
                ok = True
            except:  # 'bare except' because it catches more than "except Exception"
                ok = False
                error = f'Unable to write to the backup dir in {backup_dir} as {whoami()}.'
        else:
            ok = False
            error = f'Unable to create {backup_dir} as {user}'

        # don't create the directory while verifying its ok to create it
        if not dir_preexists and os.path.exists(backup_dir):
            rmtree(backup_dir)

    if error is not None:
        log(error)

    return ok, error
Пример #2
0
    def wait_for_restore(self, restore_process):
        '''
            Wait for the restore to finish and display data while waiting.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> restore_task = RestoreTask(os.path.join(gettempdir(), 'bitcoin/data/testnet3/backups/level1'))
            >>> restore_task.manager = BitcoinManager(restore_task.log_name)
            >>> restore_process = restore_task.start_restore()
            >>> restore_process is not None
            True
            >>> restore_task.wait_for_restore(restore_process)
            >>> test_utils.stop_restore()
            >>> restore_task.wait_for_restore(None)
            >>> test_utils.stop_restore()
        '''
        def show_line(line):
            if line is not None and line.startswith('Copying '):
                end_index = line.find(' to ')
                if end_index > 0:
                    line = line[:end_index].strip()
                    if line.startswith('Copying metadata '):
                        filename = line[len('Copying metadata '):]
                        title = 'Copying metadata'
                    else:
                        filename = line[len('Copying '):]
                        title = 'Copying'
                    line = f'<strong>{title}</strong> {filename}'
                self.manager.update_progress(line)

        self.log('starting to wait for restore')

        if restore_process is None:
            log_path = os.path.join(BASE_LOG_DIR, whoami(), 'bcb-restore.log')

            # wait until the log appears
            while is_restore_running() and not self.is_interrupted():

                if not os.path.exists(log_path):
                    sleep(1)

            # then display the restore details
            while is_restore_running() and not self.is_interrupted():

                with open(log_path, 'rt') as restore_log:
                    show_line(restore_log.readline())
        else:
            while (restore_process.poll() is None
                   and not self.is_interrupted()):

                show_line(restore_process.stdout.readline())

        self.log('finished waiting for restore')
Пример #3
0
    def wait_for_backup(self, backup_process):
        '''
            Wait for the backup to finish and display data while waiting.

            >>> from blockchain_backup.bitcoin.tests import utils as test_utils
            >>> test_utils.init_database()
            >>> backup_task = BackupTask()
            >>> backup_task.manager = BitcoinManager(backup_task.log_name)
            >>> backup_task.prep_backup()
            >>> backup_process, backup_pid = backup_task.start_backup()
            >>> backup_process is not None
            True
            >>> backup_pid is None
            True
            >>> backup_task.wait_for_backup(backup_process)
            >>> test_utils.stop_backup()
            >>> backup_task.wait_for_backup(None)
            >>> test_utils.stop_backup()
        '''
        def show_line(line):
            if line is not None and line.startswith('Copying:'):
                index = line.rfind(os.sep)
                if index > 0:
                    line = self.COPYING.format(line[index + 1:])
                if line != self.COPYING:
                    self.manager.update_progress(line)

        self.log('starting to wait for backup')

        if backup_process is None:
            log_path = os.path.join(BASE_LOG_DIR, whoami(), 'bcb-backup.log')

            # wait until the log appears
            while (is_backup_running() and not self.is_interrupted()):
                if not os.path.exists(log_path):
                    sleep(1)

            # then display the backup details
            while (is_backup_running() and not self.is_interrupted()):
                with open(log_path, 'rt') as backup_log:
                    show_line(backup_log.readline())
        else:
            while (backup_process.poll() is None
                   and not self.is_interrupted()):
                show_line(backup_process.stdout.readline())

        if self.is_interrupted():
            self.log('waiting for backup interrupted')
        else:
            self.log('finished waiting for backup')
Пример #4
0
def match_parent_owner(path, mode=None):
    ''' Chown to the parent dir's uid and gid.

        If mode is present, it is passed to chmod(). '''

    try:
        statinfo = os.stat(os.path.dirname(path))
        os.chown(path, statinfo.st_uid, statinfo.st_gid)
    except:   # 'bare except' because it catches more than "except Exception"
        # import delayed to avoid infinite recursion
        from denova.os.user import whoami

        # probably don't have the right to change the file at all,
        # or to change the owner to the specified user
        log.error('unable to chown: user={}, uid={}, gid={}, path={}'.
            format(whoami(), statinfo.st_uid, statinfo.st_gid, path))
        pass
    finally:
        if mode is not None:
            chmod(mode, path)
Пример #5
0
def data_dir_ok(data_dir=None):
    '''
        Verify the data dir includes the appropriate subdirs.

        >>> from blockchain_backup.bitcoin.tests import utils as test_utils
        >>> test_utils.init_database()
        >>> data_dir_ok('/tmp/bitcoin/data')
        (True, None)
        >>> result, error_message = data_dir_ok('/usr/bin/backups')
        >>> result == False
        True
        >>> error_message == 'Unable to create /usr/bin/backups as {}'.format(whoami())
        True
    '''

    user = whoami()
    error = None

    if data_dir is None:
        data_dir = get_data_dir()

    if not os.path.exists(data_dir):
        try:
            log(f'trying to make data dir at: {data_dir}')
            os.makedirs(data_dir)
        except:  # 'bare except' because it catches more than "except Exception"
            error = f'Unable to create {data_dir} as {user}'

    if os.path.exists(data_dir):
        from blockchain_backup.bitcoin.utils import is_dir_writeable

        ok, error = is_dir_writeable(data_dir)
    else:
        ok = False
        error = f'Unable to create {data_dir} as {user}'

    if error is not None:
        log(error)

    return ok, error
Пример #6
0
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'denova.django_addons.middleware.template.RequestMiddleware',
    'denova.django_addons.middleware.template.SettingsMiddleware',
    #'denova.django_addons.middleware.debug.StripCodeComments',

    #'django.middleware.cache.FetchFromCacheMiddleware', # This must be last to use caching
]

# Log everything to a file. Log errors, such as HTTP 500 error when DEBUG=False, to email.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
DJANGO_USER = whoami()
DJANGO_LOG_DIR = f'/var/local/log/{DJANGO_USER}'
DJANGO_LOG_FILENAME = f'{DJANGO_LOG_DIR}/django.log'
try:
    if not os.path.exists(DJANGO_LOG_DIR):
        os.makedirs(DJANGO_LOG_DIR)
except Exception:
    try:
        # each log needs a unique name per user or there are permission conflicts
        DJANGO_LOG_FILENAME = os.path.join(gettempdir(),
                                           f'django_{DJANGO_USER}.log')
    except Exception:
        from tempfile import NamedTemporaryFile
        DJANGO_LOG_FILENAME = NamedTemporaryFile(mode='a',
                                                 prefix='django',
                                                 dir=gettempdir())
Пример #7
0
def minimal_env(user=None):
    '''
        Get very minimal, safe chroot env.

        Be sure to validate anything that comes from environment variables
        before using it. According to David A. Wheeler, a common cracker's
        technique is to change an environment variable.

        If user is not set, gets the user from denova.os.user.whoami(). This
        can flood /var/log/auth.log, so call with user set when you can.

        >>> from denova.os.user import whoami
        >>> env = minimal_env()
        >>> '/bin:/usr/bin:/usr/local/bin' in env['PATH']
        True
        >>> if whoami() == 'root':
        ...     '/sbin:/usr/sbin:/usr/local/sbin' in env['PATH']
        ... else:
        ...     '/sbin:/usr/sbin:/usr/local/sbin' not in env['PATH']
        True
    '''

    # import delayed to avoid recursive imports
    from denova.os.user import whoami

    if not user:
        user = whoami()

    env = {}

    # use a minimal path
    if user == 'root':
        env['PATH'] = '/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin'
    else:
        env['PATH'] = '/bin:/usr/bin:/usr/local/bin'

    env_var = 'HOME'
    if env_var in os.environ:
        var = os.environ[env_var]
        # make sure the home directory is something reasonable and reasonably safe
        # Wheeler's Secure Programming warns against directories with '..'
        var = os.path.abspath(var)
        if os.path.exists(var):
            env[env_var] = var

    env_var = 'TZ'
    if env_var in os.environ:
        var = os.environ[env_var]
        # only set the variable if it's reasonable
        m = re.match(
            '^([A-Za-z]+[A-Za-z_-]*/?[A-Za-z_-]*/?[A-Za-z_-]*?[A-Za-z0-9]*)$',
            var)
        if m and (m.group(1) == var):
            env[env_var] = var

    env_var = 'IFS'
    if env_var in os.environ:
        # force the variable to a known good value
        env[env_var] = "$' \t\n'"

    env_var = 'LC_ALL'
    if env_var in os.environ:
        # force the variable to a known good value
        env[env_var] = 'C'

    return env
Пример #8
0
def hostaddress(name=None):
    ''' Get the host ip address.

        Returns None if not found.
        Default is to return this host's ip.

        Because this function uses gethostbyname(), be sure you are not
        vulnerable to the GHOST attack.
        https://security-tracker.debian.org/tracker/CVE-2015-0235
    '''

    ip = None

    host = name or hostname()
    #log.debug(f'host: {host}')

    try:
        host_by_name = socket.gethostbyname(host)

    except socket.gaierror:
        log.debug(f'no address for hostname: {host}')

    else:
        #log.debug(f'host by name: {host_by_name}')

        if name:
            ip = host_by_name

        else:
            # socket.gethostbyname(hostname()) can be wrong depending on what is in /etc/hosts
            # but interface_from_ip() requires we are root, and we want to continue if possible
            if whoami() == 'root':
                interface = interface_from_ip(host_by_name)
                if interface and not interface == 'lo':
                    log.debug(f'setting ip to host by name: {host_by_name}')
                    ip = host_by_name
                else:
                    log.warning(
                        f'socket.gethostbyname(hostname()) returned {host_by_name}, '
                        +
                        'but no interface has that address. Is /etc/hosts wrong?'
                    )

            else:
                # accept socket.gethostbyname() because we can't verify it
                ip = host_by_name

    if not ip:
        # use the first net device with an ip address, excluding 'lo'
        for interface in interfaces():
            if not ip:
                if interface != 'lo':
                    ip = device_address(interface)
                    log.debug(
                        f'set ip to {ip} from first net device {interface}')

    if ip:
        log.debug(f'ip address: {ip}')
    else:
        msg = 'no ip address'
        log.debug(msg)
        raise NetException(msg)

    return ip