def dpkg_install(paths, check=True): if not hasattr(paths, 'keys'): pkgs = {} # determine package names from filenames. ideally, we would open the # package here and check for p in paths: fn = os.path.basename(p) try: name, version, tail = fn.split('_', 3) pkgs[(name, version)] = p except ValueError: raise ValueError( 'Could not determine package version from ' 'package filename {}. Please rename the .deb ' 'to standard debian convention ' '(name_version_arch.deb) or supply a specific ' 'version by passing a dictionary parameter.'.format(fn)) # log names log.debug('Package names: ' + ', '.join('{} -> {}'.format(k, v) for k, v in pkgs.items())) if check: missing = [] installed = info_installed_packages() for name, version in pkgs: if name not in installed or not installed[name].eq_version(version): missing.append((name, version)) else: missing = pkgs.keys() log.debug('Installing packages: {}'.format(missing)) if not missing: return Unchanged('Packages {!r} already installed'.format(pkgs.keys())) # FIXME: see above info_installed_packages.invalidate_cache() with fs.remote_tmpdir() as rtmp: # upload packages to be installed pkg_files = [] for idx, key in enumerate(missing): tmpdest = remote.path.join(rtmp, str(idx) + '.deb') fs.upload_file(pkgs[key], tmpdest) pkg_files.append(tmpdest) # install in a single dpkg install line # FIXME: add debconf default and such (same as apt) args = [config['cmd_dpkg'], '-i'] args.extend(pkg_files) proc.run( args, extra_env={ 'DEBIAN_FRONTEND': 'noninteractive', }) return Changed(msg='Installed packages {!r}'.format(missing))
def dpkg_install(paths, check=True): pkgs = paths if not hasattr(paths, 'keys'): pkgs = {} # determine package names from filenames. ideally, we would open the # package here and check for p in paths: fn = os.path.basename(p) try: name, version, tail = fn.split('_', 3) pkgs[(name, version)] = p except ValueError: raise ValueError( 'Could not determine package version from ' 'package filename {}. Please rename the .deb ' 'to standard debian convention ' '(name_version_arch.deb) or supply a specific ' 'version by passing a dictionary parameter.'.format(fn)) # log names log.debug('Package names: ' + ', '.join('{} -> {}'.format(k, v) for k, v in pkgs.items())) if check: missing = [] installed = info_installed_packages() for name, version in pkgs: if name not in installed or not installed[name].eq_version( version): missing.append((name, version)) else: missing = pkgs.keys() log.debug('Installing packages: {}'.format(missing)) if not missing: return Unchanged('Packages {!r} already installed'.format(pkgs.keys())) # FIXME: see above info_installed_packages.invalidate_cache() with fs.remote_tmpdir() as rtmp: # upload packages to be installed pkg_files = [] for idx, key in enumerate(missing): tmpdest = remote.path.join(rtmp, str(idx) + '.deb') fs.upload_file(pkgs[key], tmpdest) pkg_files.append(tmpdest) # install in a single dpkg install line # FIXME: add debconf default and such (same as apt) args = [config['cmd_dpkg'], '-i'] args.extend(pkg_files) proc.run(args, extra_env={'DEBIAN_FRONTEND': 'noninteractive', }) return Changed(msg='Installed packages {!r}'.format(missing))
def install_cert(cert, key, cert_name=None, key_name=None): cert_name = cert_name or os.path.basename(cert) key_name = key_name or os.path.basename(key) # small sanity check with open(cert) as f: if 'PRIVATE' in f.read(): raise ValueError( 'You seem to have passed a private key as a cert!') with open(key) as f: if 'PRIVATE' not in f.read(): raise ValueError( '{} does not seem to be a valid private key'.format(key)) # check if remote is reasonably secure cert_dir = config['sslcert_cert_dir'] cert_dir_st = remote.lstat(cert_dir) if not cert_dir_st: raise ConfigurationError('Remote SSL dir {} does not exist'.format( cert_dir)) key_dir = config['sslcert_key_dir'] key_dir_st = remote.lstat(key_dir) if not key_dir_st: raise ConfigurationError('Remote key dir {} does not exist'.format( key_dir)) SECURE_MODES = (0o700, 0o710) actual_mode = key_dir_st.st_mode & 0o777 if actual_mode not in SECURE_MODES: raise ConfigurationError( 'Mode of remote key dir {} is {:o}, should be one of {:o}'.format( key_dir, actual_mode, SECURE_MODES)) if key_dir_st.st_uid != 0: raise ConfigurationError( 'Remove key dir {} is not owned by root'.format(key_dir)) # we can safely upload the key and cert cert_rpath = remote.path.join(cert_dir, cert_name) key_rpath = remote.path.join(key_dir, key_name) changed = False changed |= fs.upload_file(cert, cert_rpath).changed changed |= fs.upload_file(key, key_rpath).changed changed |= fs.chmod(key_rpath, 0o600).changed if changed: return Changed( msg='Uploaded key pair {}/{}'.format(cert_name, key_name)) return Unchanged( msg='Key pair {}/{} already uploaded'.format(cert_name, key_name))
def install_private_key(key_file, user='******', key_type='rsa', target_path=None): if target_path is None: # FIXME: auto-determine key type if None if key_type not in ('rsa', ): raise NotImplementedError('Key type {} not supported') fn = 'id_' + key_type target_path = remote.path.join(info['posix.users'][user].home, '.ssh', fn) changed = False # blocked: SSH transport does not suppoort # with remote.umasked(0o777 - KEY_FILE_PERMS): changed |= fs.create_dir( remote.path.dirname(target_path), mode=AK_DIR_PERMS).changed changed |= fs.upload_file(key_file, target_path).changed changed |= fs.chmod(target_path, mode=KEY_FILE_PERMS).changed if changed: return Changed(msg='Installed private key {}'.format(target_path)) return Unchanged( msg='Private key {} already installed'.format(target_path))
def install_private_key(key_file, user='******', key_type='rsa', target_path=None): if target_path is None: # FIXME: auto-determine key type if None if key_type not in ('rsa', ): raise NotImplementedError('Key type {} not supported') fn = 'id_' + key_type target_path = remote.path.join(info['posix.users'][user].home, '.ssh', fn) changed = False # blocked: SSH transport does not suppoort # with remote.umasked(0o777 - KEY_FILE_PERMS): changed |= fs.create_dir(remote.path.dirname(target_path), mode=AK_DIR_PERMS).changed changed |= fs.upload_file(key_file, target_path).changed changed |= fs.chmod(target_path, mode=KEY_FILE_PERMS).changed if changed: return Changed(msg='Installed private key {}'.format(target_path)) return Unchanged( msg='Private key {} already installed'.format(target_path))
def install_hostkeys(base_dir): results = [] for key_type in ('ecdsa', 'ed25519', 'rsa'): fn = 'ssh_host_' + key_type + '_key' pub_fn = fn + '.pub' sk = os.path.join(base_dir, fn) pk = os.path.join(base_dir, pub_fn) results.append(fs.upload_file(sk, '/etc/ssh/' + fn)) results.append(fs.upload_file(pk, '/etc/ssh/' + pub_fn)) if util.any_changed(*results): systemd.reload_unit('ssh.service') return Changed(msg='Installed SSH hostkeys from {}'.format(base_dir)) return Unchanged(msg='SSH hostkeys already installed')
def install_docker_compose(): # docker: install docker compose ch = fs.upload_file(docker.webfiles["docker-compose-Linux-x86_64"], "/usr/local/bin/docker-compose").changed ch |= fs.chmod("/usr/local/bin/docker-compose", mode=0o755).changed if ch: return Changed(msg="Installed docker-compose") return Unchanged(msg="docker-compose already installed")
def install_docker_compose(): # docker: install docker compose ch = fs.upload_file(docker.webfiles['docker-compose-Linux-x86_64'], '/usr/local/bin/docker-compose').changed ch |= fs.chmod('/usr/local/bin/docker-compose', mode=0o755).changed if ch: return Changed(msg='Installed docker-compose') return Unchanged(msg='docker-compose already installed')
def install_source_list(path, name=None, main=False): if main: if name is not None: raise ValueError('Cannot provide name when uploading sources.list') op = fs.upload_file( path, config['apt_sources_list'], create_parent=True) else: op = fs.upload_file( path, remote.path.join(config['apt_sources_list_d'], name or os.path.basename(path)), create_parent=True) if op.changed: info_update_timestamp().mark_stale() return op
def install_source_list(path, name=None, main=False): if main: if name is not None: raise ValueError('Cannot provide name when uploading sources.list') op = fs.upload_file(path, config['apt_sources_list'], create_parent=True) else: op = fs.upload_file(path, remote.path.join(config['apt_sources_list_d'], name or os.path.basename(path)), create_parent=True) if op.changed: info_update_timestamp().mark_stale() return op
def install_preference(path, name=None): op = fs.upload_file(path, remote.path.join(config['apt_preferences_d'], name or os.path.basename(path)), create_parent=True) if op.changed: info_update_timestamp().mark_stale() return op
def install_preference(path, name=None): op = fs.upload_file( path, remote.path.join(config['apt_preferences_d'], name or os.path.basename(path)), create_parent=True) if op.changed: info_update_timestamp().mark_stale() return op
def install_unit_file(unit_file, reload=True): base, ext = os.path.splitext(unit_file) if ext not in UNIT_EXTS: raise ValueError('unit_file should be one of {}'.format(UNIT_EXTS)) remote_unit = os.path.join(config['systemd_unit_dir'], os.path.basename(unit_file)) if fs.upload_file(unit_file, remote_unit).changed: if reload: daemon_reload() return Changed(msg='Installed {}'.format(remote_unit)) return Unchanged(msg='{} already installed'.format(remote_unit))
def install_network_file(network_file, reload=True): base, ext = os.path.splitext(network_file) if ext not in NETWORK_EXTS: raise ValueError('network_file should be one of {}'.format( NETWORK_EXTS)) remote_network = os.path.join(config['systemd_network_dir'], os.path.basename(network_file)) if fs.upload_file(network_file, remote_network).changed: if reload: daemon_reload() return Changed(msg='Installed {}'.format(remote_network)) return Unchanged(msg='{} already installed'.format(remote_network))
def install_network_file(network_file, reload=True): base, ext = os.path.splitext(network_file) if ext not in NETWORK_EXTS: raise ValueError( 'network_file should be one of {}'.format(NETWORK_EXTS)) remote_network = os.path.join(config['systemd_network_dir'], os.path.basename(network_file)) if fs.upload_file(network_file, remote_network).changed: if reload: daemon_reload() return Changed(msg='Installed {}'.format(remote_network)) return Unchanged(msg='{} already installed'.format(remote_network))
def setup_rsyslog(server_addr): # setup papertrail # FIXME: this is part of remand now changed = False changed = apt.install_packages(['rsyslog-gnutls']).changed changed |= fs.upload_file(papertrail.files['papertrail-bundle.pem'], '/etc/papertrail-bundle.pem').changed changed |= fs.upload_string( papertrail.templates.render('papertrail.conf', addr=server_addr), '/etc/rsyslog.d/papertrail.conf', ).changed if changed: systemd.restart_unit('rsyslog.service') return Changed( msg='Setup papertrail logging to {}'.format(server_addr)) return Unchanged(msg='Papertrail already setup to {}'.format(server_addr))
def enable_letsencrypt(auto_reload=True, remove_default=True): changed = any_changed( fs.upload_file(nginx.files['acme-challenge'], '/etc/nginx/sites-available/acme-challenge'), fs.symlink('/etc/nginx/sites-available/acme-challenge', '/etc/nginx/sites-enabled/00_acme-challenge'), ) fs.create_dir('/var/www/html/.well-known') fs.create_dir('/var/www/html/.well-known/acme-challenge') fs.chmod('/var/www/html/.well-known', mode=0o755) fs.chmod('/var/www/html/.well-known/acme-challenge', mode=0o755) if remove_default: changed |= fs.remove_file('/etc/nginx/sites-enabled/default').changed if changed: if auto_reload: systemd.reload_unit('nginx.service', only_if_running=True) return Changed(msg='Enabled nginx Let\'s encrypt support') return Unchanged(msg='nginx Let\'s encrypt support already enabled')
def install_cert(cert, key, cert_name=None, key_name=None): """Installs an SSL certificate with including key on the remote Certificate filenames are unchanged, per default they will be installed in `/etc/ssl`, with the corresponding keys at `/etc/ssl/private`.""" cert_name = cert_name or os.path.basename(cert) key_name = key_name or os.path.basename(key) # small sanity check with open(cert) as f: if 'PRIVATE' in f.read(): raise ValueError( 'You seem to have passed a private key as a cert!') with open(key) as f: if 'PRIVATE' not in f.read(): raise ValueError( '{} does not seem to be a valid private key'.format(key)) # check if remote is reasonably secure cert_dir = config['sslcert_cert_dir'] cert_dir_st = remote.lstat(cert_dir) if not cert_dir_st: raise ConfigurationError( 'Remote SSL dir {} does not exist'.format(cert_dir)) key_dir = config['sslcert_key_dir'] key_dir_st = remote.lstat(key_dir) if not key_dir_st: raise ConfigurationError( 'Remote key dir {} does not exist'.format(key_dir)) SECURE_MODES = (0o700, 0o710) actual_mode = key_dir_st.st_mode & 0o777 if actual_mode not in SECURE_MODES: raise ConfigurationError( 'Mode of remote key dir {} is {:o}, should be one of {:o}'.format( key_dir, actual_mode, SECURE_MODES)) if key_dir_st.st_uid != 0: raise ConfigurationError( 'Remove key dir {} is not owned by root'.format(key_dir)) # we can safely upload the key and cert cert_rpath = remote.path.join(cert_dir, cert_name) key_rpath = remote.path.join(key_dir, key_name) changed = False changed |= fs.upload_file(cert, cert_rpath).changed changed |= fs.upload_file(key, key_rpath).changed changed |= fs.chmod(key_rpath, 0o640).changed changed |= fs.chown(key_rpath, uid='root', gid='ssl-cert').changed if changed: return Changed( msg='Uploaded key pair {}/{}'.format(cert_name, key_name)) return Unchanged( msg='Key pair {}/{} already uploaded'.format(cert_name, key_name))