def handle(name, cfg, cloud, log, _args): if util.is_false(cfg.get("ssh", {}).get("emit_keys_to_console", True)): log.debug( "Skipping module named %s, logging of SSH host keys disabled", name) return helper_path = _get_helper_tool_path(cloud.distro) if not os.path.exists(helper_path): log.warning( "Unable to activate module %s, helper tool not found at %s", name, helper_path, ) return fp_blacklist = util.get_cfg_option_list(cfg, "ssh_fp_console_blacklist", []) key_blacklist = util.get_cfg_option_list(cfg, "ssh_key_console_blacklist", ["ssh-dss"]) try: cmd = [helper_path, ",".join(fp_blacklist), ",".join(key_blacklist)] (stdout, _stderr) = subp.subp(cmd) util.multi_log("%s\n" % (stdout.strip()), stderr=False, console=True) except Exception: log.warning("Writing keys to the system console failed!") raise
def handle(_name, cfg, cloud, log, _args): # Handle the old style + new config names update = _multi_cfg_bool_get(cfg, 'apt_update', 'package_update', 'repo_update') upgrade = _multi_cfg_bool_get(cfg, 'package_upgrade', 'apt_upgrade') reboot_if_required = _multi_cfg_bool_get(cfg, 'apt_reboot_if_required', 'package_reboot_if_required') pkglist = util.get_cfg_option_list(cfg, 'packages', []) # Amazon option that should also trigger an upgrade, but isn't bool upgrade_level = util.get_cfg_option_str(cfg, 'repo_upgrade', upgrade) if upgrade_level not in ('none', 'false', 'False'): upgrade = True else: # condense none, false, and False to none upgrade_level = 'none' upgrade_exclude = util.get_cfg_option_list(cfg, 'repo_upgrade_exclude', []) errors = [] if update or len(pkglist) or upgrade: try: cloud.distro.update_package_sources() except Exception as e: util.logexc(log, "Package update failed") errors.append(e) if upgrade: try: cloud.distro.upgrade_packages(upgrade_level, upgrade_exclude) except Exception as e: util.logexc(log, "Package upgrade failed") errors.append(e) if len(pkglist): try: cloud.distro.install_packages(pkglist) except Exception as e: util.logexc(log, "Failed to install packages: %s", pkglist) errors.append(e) # TODO(smoser): handle this less violently # kernel and openssl (possibly some other packages) # write a file /var/run/reboot-required after upgrading. # if that file exists and configured, then just stop right now and reboot reboot_fn_exists = os.path.isfile(REBOOT_FILE) if (upgrade or pkglist) and reboot_if_required and reboot_fn_exists: try: log.warn("Rebooting after upgrade or install per %s", REBOOT_FILE) # Flush the above warning + anything else out... logging.flushLoggers(log) _fire_reboot(log) except Exception as e: util.logexc(log, "Requested reboot did not happen!") errors.append(e) if len(errors): log.warn("%s failed with exceptions, re-raising the last one", len(errors)) raise errors[-1]
def handle(name, cfg, _cloud, log, _args): """ Call to handle ca-cert sections in cloud-config file. @param name: The module name "ca-cert" from cloud.cfg @param cfg: A nested dict containing the entire cloud config contents. @param cloud: The L{CloudInit} object in use. @param log: Pre-initialized Python logger object to use for logging. @param args: Any module arguments from cloud.cfg """ # If there isn't a ca-certs section in the configuration don't do anything if "ca-certs" not in cfg: log.debug(("Skipping module named %s," " no 'ca-certs' key in configuration"), name) return ca_cert_cfg = cfg['ca-certs'] # If there is a remove-defaults option set to true, remove the system # default trusted CA certs first. if ca_cert_cfg.get("remove-defaults", False): log.debug("Removing default certificates") remove_default_ca_certs() # If we are given any new trusted CA certs to add, add them. if "trusted" in ca_cert_cfg: trusted_certs = util.get_cfg_option_list(ca_cert_cfg, "trusted") if trusted_certs: log.debug("Adding %d certificates" % len(trusted_certs)) add_ca_certs(trusted_certs) # Update the system with the new cert configuration. log.debug("Updating certificates") update_ca_certs()
def handle(_name, cfg, cloud, log, args): if not is_key_in_nested_dict(cfg, "ssh_import_id"): log.debug("Skipping module named ssh-import-id, no 'ssh_import_id'" " directives found.") return elif not subp.which(SSH_IMPORT_ID_BINARY): log.warning( "ssh-import-id is not installed, but module ssh_import_id is " "configured. Skipping module.") return # import for "user: XXXXX" if len(args) != 0: user = args[0] ids = [] if len(args) > 1: ids = args[1:] import_ssh_ids(ids, user, log) return # import for cloudinit created users (users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro) elist = [] for (user, user_cfg) in users.items(): import_ids = [] if user_cfg["default"]: import_ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) else: try: import_ids = user_cfg["ssh_import_id"] except Exception: log.debug("User %s is not configured for ssh_import_id", user) continue try: import_ids = util.uniq_merge(import_ids) import_ids = [str(i) for i in import_ids] except Exception: log.debug("User %s is not correctly configured for ssh_import_id", user) continue if not len(import_ids): continue try: import_ssh_ids(import_ids, user, log) except Exception as exc: util.logexc(log, "ssh-import-id failed for: %s %s", user, import_ids) elist.append(exc) if len(elist): raise elist[0]
def handle(name, cfg, cloud, log, _args): helper_path = _get_helper_tool_path(cloud.distro) if not os.path.exists(helper_path): log.warning(("Unable to activate module %s," " helper tool not found at %s"), name, helper_path) return fp_blacklist = util.get_cfg_option_list(cfg, "ssh_fp_console_blacklist", []) key_blacklist = util.get_cfg_option_list(cfg, "ssh_key_console_blacklist", ["ssh-dss"]) try: cmd = [helper_path, ','.join(fp_blacklist), ','.join(key_blacklist)] (stdout, _stderr) = subp.subp(cmd) util.multi_log("%s\n" % (stdout.strip()), stderr=False, console=True) except Exception: log.warning("Writing keys to the system console failed!") raise
def handle(name, cfg, cloud, log, _args): """ Call to handle ca-cert sections in cloud-config file. @param name: The module name "ca-cert" from cloud.cfg @param cfg: A nested dict containing the entire cloud config contents. @param cloud: The L{CloudInit} object in use. @param log: Pre-initialized Python logger object to use for logging. @param args: Any module arguments from cloud.cfg """ if "ca-certs" in cfg: log.warning( "DEPRECATION: key 'ca-certs' is now deprecated. Use 'ca_certs'" " instead." ) elif "ca_certs" not in cfg: log.debug( "Skipping module named %s, no 'ca_certs' key in configuration", name, ) return if "ca-certs" in cfg and "ca_certs" in cfg: log.warning( "Found both ca-certs (deprecated) and ca_certs config keys." " Ignoring ca-certs." ) ca_cert_cfg = cfg.get("ca_certs", cfg.get("ca-certs")) distro_cfg = _distro_ca_certs_configs(cloud.distro.name) # If there is a remove_defaults option set to true, remove the system # default trusted CA certs first. if "remove-defaults" in ca_cert_cfg: log.warning( "DEPRECATION: key 'ca-certs.remove-defaults' is now deprecated." " Use 'ca_certs.remove_defaults' instead." ) if ca_cert_cfg.get("remove-defaults", False): log.debug("Removing default certificates") remove_default_ca_certs(cloud.distro.name, distro_cfg) elif ca_cert_cfg.get("remove_defaults", False): log.debug("Removing default certificates") remove_default_ca_certs(cloud.distro.name, distro_cfg) # If we are given any new trusted CA certs to add, add them. if "trusted" in ca_cert_cfg: trusted_certs = util.get_cfg_option_list(ca_cert_cfg, "trusted") if trusted_certs: log.debug("Adding %d certificates" % len(trusted_certs)) add_ca_certs(distro_cfg, trusted_certs) # Update the system with the new cert configuration. log.debug("Updating certificates") update_ca_certs(distro_cfg)
def handle(name, cfg, _cloud, log, _args): if not os.path.exists(HELPER_TOOL): log.warn(("Unable to activate module %s," " helper tool not found at %s"), name, HELPER_TOOL) return fp_blacklist = util.get_cfg_option_list(cfg, "ssh_fp_console_blacklist", []) key_blacklist = util.get_cfg_option_list(cfg, "ssh_key_console_blacklist", ["ssh-dss"]) try: cmd = [HELPER_TOOL] cmd.append(','.join(fp_blacklist)) cmd.append(','.join(key_blacklist)) (stdout, _stderr) = util.subp(cmd) util.multi_log("%s\n" % (stdout.strip()), stderr=False, console=True) except: log.warn("Writing keys to the system console failed!") raise
def handle(name, cfg, cloud, log, _args): helper_path = _get_helper_tool_path(cloud.distro) if not os.path.exists(helper_path): log.warn(("Unable to activate module %s," " helper tool not found at %s"), name, helper_path) return fp_blacklist = util.get_cfg_option_list(cfg, "ssh_fp_console_blacklist", []) key_blacklist = util.get_cfg_option_list(cfg, "ssh_key_console_blacklist", ["ssh-dss"]) try: cmd = [helper_path, ','.join(fp_blacklist), ','.join(key_blacklist)] (stdout, _stderr) = util.subp(cmd) util.multi_log("%s\n" % (stdout.strip()), stderr=False, console=True) except Exception: log.warn("Writing keys to the system console failed!") raise
def handle(_name, cfg, cloud, log, _args): # Handle the old style + new config names update = _multi_cfg_bool_get(cfg, "apt_update", "package_update") upgrade = _multi_cfg_bool_get(cfg, "package_upgrade", "apt_upgrade") reboot_if_required = _multi_cfg_bool_get( cfg, "apt_reboot_if_required", "package_reboot_if_required" ) pkglist = util.get_cfg_option_list(cfg, "packages", []) errors = [] if update or len(pkglist) or upgrade: try: cloud.distro.update_package_sources() except Exception as e: util.logexc(log, "Package update failed") errors.append(e) if upgrade: try: cloud.distro.package_command("upgrade") except Exception as e: util.logexc(log, "Package upgrade failed") errors.append(e) if len(pkglist): try: cloud.distro.install_packages(pkglist) except Exception as e: util.logexc(log, "Failed to install packages: %s", pkglist) errors.append(e) # TODO(smoser): handle this less violently # kernel and openssl (possibly some other packages) # write a file /var/run/reboot-required after upgrading. # if that file exists and configured, then just stop right now and reboot reboot_fn_exists = os.path.isfile(REBOOT_FILE) if (upgrade or pkglist) and reboot_if_required and reboot_fn_exists: try: log.warning( "Rebooting after upgrade or install per %s", REBOOT_FILE ) # Flush the above warning + anything else out... logging.flushLoggers(log) _fire_reboot(log) except Exception as e: util.logexc(log, "Requested reboot did not happen!") errors.append(e) if len(errors): log.warning( "%s failed with exceptions, re-raising the last one", len(errors) ) raise errors[-1]
def handle(_name, cfg, _cloud, log, _args): if "growpart" not in cfg: log.debug( "No 'growpart' entry in cfg. Using default: %s" % DEFAULT_CONFIG ) cfg["growpart"] = DEFAULT_CONFIG mycfg = cfg.get("growpart") if not isinstance(mycfg, dict): log.warning("'growpart' in config was not a dict") return mode = mycfg.get("mode", "auto") if util.is_false(mode): if mode != "off": log.warning( f"DEPRECATED: growpart mode '{mode}' is deprecated. " "Use 'off' instead." ) log.debug("growpart disabled: mode=%s" % mode) return if util.is_false(mycfg.get("ignore_growroot_disabled", False)): if os.path.isfile("/etc/growroot-disabled"): log.debug("growpart disabled: /etc/growroot-disabled exists") log.debug("use ignore_growroot_disabled to ignore") return devices = util.get_cfg_option_list(mycfg, "devices", ["/"]) if not len(devices): log.debug("growpart: empty device list") return try: resizer = resizer_factory(mode) except (ValueError, TypeError) as e: log.debug("growpart unable to find resizer for '%s': %s" % (mode, e)) if mode != "auto": raise e return resized = util.log_time( logfunc=log.debug, msg="resize_devices", func=resize_devices, args=(resizer, devices), ) for (entry, action, msg) in resized: if action == RESIZE.CHANGED: log.info("'%s' resized: %s" % (entry, msg)) else: log.debug("'%s' %s: %s" % (entry, action, msg))
def handle(_name, cfg, cloud, log, _args): # Handle the old style + new config names update = _multi_cfg_bool_get(cfg, 'apt_update', 'package_update') upgrade = _multi_cfg_bool_get(cfg, 'package_upgrade', 'apt_upgrade') reboot_if_required = _multi_cfg_bool_get(cfg, 'apt_reboot_if_required', 'package_reboot_if_required') pkglist = util.get_cfg_option_list(cfg, 'packages', []) errors = [] if update or len(pkglist) or upgrade: try: cloud.distro.update_package_sources() except Exception as e: util.logexc(log, "Package update failed") errors.append(e) if upgrade: try: cloud.distro.package_command("upgrade") except Exception as e: util.logexc(log, "Package upgrade failed") errors.append(e) if len(pkglist): try: cloud.distro.install_packages(pkglist) except Exception as e: util.logexc(log, "Failed to install packages: %s", pkglist) errors.append(e) # TODO(smoser): handle this less violently # kernel and openssl (possibly some other packages) # write a file /var/run/reboot-required after upgrading. # if that file exists and configured, then just stop right now and reboot reboot_fn_exists = os.path.isfile(REBOOT_FILE) if (upgrade or pkglist) and reboot_if_required and reboot_fn_exists: try: log.warn("Rebooting after upgrade or install per %s", REBOOT_FILE) # Flush the above warning + anything else out... logging.flushLoggers(log) _fire_reboot(log) except Exception as e: util.logexc(log, "Requested reboot did not happen!") errors.append(e) if len(errors): log.warn("%s failed with exceptions, re-raising the last one", len(errors)) raise errors[-1]
def handle(_name, cfg, cloud, log, args): # import for "user: XXXXX" if len(args) != 0: user = args[0] ids = [] if len(args) > 1: ids = args[1:] import_ssh_ids(ids, user, log) return # import for cloudinit created users (users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro) elist = [] for (user, user_cfg) in users.items(): import_ids = [] if user_cfg['default']: import_ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) else: try: import_ids = user_cfg['ssh_import_id'] except Exception: log.debug("User %s is not configured for ssh_import_id", user) continue try: import_ids = util.uniq_merge(import_ids) import_ids = [str(i) for i in import_ids] except Exception: log.debug("User %s is not correctly configured for ssh_import_id", user) continue if not len(import_ids): continue try: import_ssh_ids(import_ids, user, log) except Exception as exc: util.logexc(log, "ssh-import-id failed for: %s %s", user, import_ids) elist.append(exc) if len(elist): raise elist[0]
def handle(_name, cfg, cloud, log, args): # import for "user: XXXXX" if len(args) != 0: user = args[0] ids = [] if len(args) > 1: ids = args[1:] import_ssh_ids(ids, user, log) return # import for cloudinit created users (users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro) elist = [] for (user, user_cfg) in users.items(): import_ids = [] if user_cfg["default"]: import_ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) else: try: import_ids = user_cfg["ssh_import_id"] except Exception: log.debug("User %s is not configured for ssh_import_id", user) continue try: import_ids = util.uniq_merge(import_ids) import_ids = [str(i) for i in import_ids] except Exception: log.debug("User %s is not correctly configured for ssh_import_id", user) continue if not len(import_ids): continue try: import_ssh_ids(import_ids, user, log) except Exception as exc: util.logexc(log, "ssh-import-id failed for: %s %s", user, import_ids) elist.append(exc) if len(elist): raise elist[0]
def handle(_name, cfg, _cloud, log, _args): if _cloud.distro.name == "aix": return if 'growpart' not in cfg: log.debug("No 'growpart' entry in cfg. Using default: %s" % DEFAULT_CONFIG) cfg['growpart'] = DEFAULT_CONFIG mycfg = cfg.get('growpart') if not isinstance(mycfg, dict): log.warn("'growpart' in config was not a dict") return mode = mycfg.get('mode', "auto") if util.is_false(mode): log.debug("growpart disabled: mode=%s" % mode) return if util.is_false(mycfg.get('ignore_growroot_disabled', False)): if os.path.isfile("/etc/growroot-disabled"): log.debug("growpart disabled: /etc/growroot-disabled exists") log.debug("use ignore_growroot_disabled to ignore") return devices = util.get_cfg_option_list(cfg, "devices", ["/"]) if not len(devices): log.debug("growpart: empty device list") return try: resizer = resizer_factory(mode) except (ValueError, TypeError) as e: log.debug("growpart unable to find resizer for '%s': %s" % (mode, e)) if mode != "auto": raise e return resized = util.log_time(logfunc=log.debug, msg="resize_devices", func=resize_devices, args=(resizer, devices)) for (entry, action, msg) in resized: if action == RESIZE.CHANGED: log.info("'%s' resized: %s" % (entry, msg)) else: log.debug("'%s' %s: %s" % (entry, action, msg))
def handle(_name, cfg, _cloud, log, _args): if 'growpart' not in cfg: log.debug("No 'growpart' entry in cfg. Using default: %s" % DEFAULT_CONFIG) cfg['growpart'] = DEFAULT_CONFIG mycfg = cfg.get('growpart') if not isinstance(mycfg, dict): log.warn("'growpart' in config was not a dict") return mode = mycfg.get('mode', "auto") if util.is_false(mode): log.debug("growpart disabled: mode=%s" % mode) return devices = util.get_cfg_option_list(cfg, "devices", ["/"]) if not len(devices): log.debug("growpart: empty device list") return try: resizer = resizer_factory(mode) except (ValueError, TypeError) as e: log.debug("growpart unable to find resizer for '%s': %s" % (mode, e)) if mode != "auto": raise e return resized = util.log_time(logfunc=log.debug, msg="resize_devices", func=resize_devices, args=(resizer, devices)) for (entry, action, msg) in resized: if action == RESIZE.CHANGED: log.info("'%s' resized: %s" % (entry, msg)) else: log.debug("'%s' %s: %s" % (entry, action, msg))
def handle(_name, cfg, cloud, log, _args): # remove the static keys from the pristine image if cfg.get("ssh_deletekeys", True): key_pth = os.path.join("/etc/ssh/", "ssh_host_*key*") for f in glob.glob(key_pth): try: util.del_file(f) except: util.logexc(log, "Failed deleting key file %s", f) if "ssh_keys" in cfg: # if there are keys in cloud-config, use them for (key, val) in cfg["ssh_keys"].items(): if key in KEY_2_FILE: tgt_fn = KEY_2_FILE[key][0] tgt_perms = KEY_2_FILE[key][1] util.write_file(tgt_fn, val, tgt_perms) for (priv, pub) in PRIV_2_PUB.items(): if pub in cfg['ssh_keys'] or priv not in cfg['ssh_keys']: continue pair = (KEY_2_FILE[priv][0], KEY_2_FILE[pub][0]) cmd = ['sh', '-xc', KEY_GEN_TPL % pair] try: # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): util.subp(cmd, capture=False) log.debug("Generated a key for %s from %s", pair[0], pair[1]) except: util.logexc(log, "Failed generated a key for %s from %s", pair[0], pair[1]) else: # if not, generate them genkeys = util.get_cfg_option_list(cfg, 'ssh_genkeytypes', GENERATE_KEY_NAMES) for keytype in genkeys: keyfile = KEY_FILE_TPL % (keytype) util.ensure_dir(os.path.dirname(keyfile)) if not os.path.exists(keyfile): cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile] try: # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): util.subp(cmd, capture=False) except: util.logexc(log, "Failed generating key type %s to " "file %s", keytype, keyfile) try: (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ds.extract_default(users) disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", DISABLE_ROOT_OPTS) keys = cloud.get_public_ssh_keys() or [] if "ssh_authorized_keys" in cfg: cfgkeys = cfg["ssh_authorized_keys"] keys.extend(cfgkeys) apply_credentials(keys, user, disable_root, disable_root_opts) except: util.logexc(log, "Applying ssh credentials failed!")
def handle(name, cfg, _cloud, log, args): if len(args) != 0: resize_enabled = args[0] else: resize_enabled = util.get_cfg_option_str(cfg, "resizefs_enabled", True) # Warn about the old-style configuration resize_rootfs_option = util.get_cfg_option_str(cfg, "resize_rootfs") if resize_rootfs_option: log.warning("""The resize_rootfs option is deprecated, please use resizefs_enabled instead!""") resize_enabled = resize_rootfs_option # Renamed to schema_vyos to pass build tests without modifying upstream validate_cloudconfig_schema(cfg, schema_vyos) if not util.translate_bool(resize_enabled, addons=[NOBLOCK]): log.debug("Skipping module named %s, resizing disabled", name) return # Get list of partitions to resize resize_what = util.get_cfg_option_list(cfg, "resizefs_list", RESIZEFS_LIST_DEFAULT) log.debug("Filesystems to resize: %s", resize_what) # Resize all filesystems from resize_what for resize_item in resize_what: result = util.get_mount_info(resize_item, log) if not result: log.warning("Could not determine filesystem type of %s", resize_item) return (devpth, fs_type, mount_point) = result # if we have a zfs then our device path at this point # is the zfs label. For example: vmzroot/ROOT/freebsd # we will have to get the zpool name out of this # and set the resize_item variable to the zpool # so the _resize_zfs function gets the right attribute. if fs_type == 'zfs': zpool = devpth.split('/')[0] devpth = util.get_device_info_from_zpool(zpool) if not devpth: return # could not find device from zpool resize_item = zpool info = "dev=%s mnt_point=%s path=%s" % (devpth, mount_point, resize_item) log.debug("resize_info: %s" % info) devpth = maybe_get_writable_device_path(devpth, info, log) if not devpth: return # devpath was not a writable block device resizer = None if can_skip_resize(fs_type, resize_item, devpth): log.debug("Skip resize filesystem type %s for %s", fs_type, resize_item) return fstype_lc = fs_type.lower() for (pfix, root_cmd) in RESIZE_FS_PREFIXES_CMDS: if fstype_lc.startswith(pfix): resizer = root_cmd break if not resizer: log.warning("Not resizing unknown filesystem type %s for %s", fs_type, resize_item) return resize_cmd = resizer(resize_item, devpth) log.debug("Resizing %s (%s) using %s", resize_item, fs_type, ' '.join(resize_cmd)) if resize_enabled == NOBLOCK: # Fork to a child that will run # the resize command util.fork_cb(util.log_time, logfunc=log.debug, msg="backgrounded Resizing", func=do_resize, args=(resize_cmd, log)) else: util.log_time(logfunc=log.debug, msg="Resizing", func=do_resize, args=(resize_cmd, log)) action = 'Resized' if resize_enabled == NOBLOCK: action = 'Resizing (via forking)' log.debug("%s filesystem on %s (type=%s, val=%s)", action, resize_item, fs_type, resize_enabled)
def handle(name, cfg, cloud, log, _args): """Handler method activated by cloud-init.""" # If there isn't a chef key in the configuration don't do anything if 'chef' not in cfg: log.debug(("Skipping module named %s," " no 'chef' key in configuration"), name) return chef_cfg = cfg['chef'] # Ensure the chef directories we use exist chef_dirs = util.get_cfg_option_list(chef_cfg, 'directories') if not chef_dirs: chef_dirs = list(CHEF_DIRS) for d in itertools.chain(chef_dirs, REQUIRED_CHEF_DIRS): util.ensure_dir(d) # Set the validation key based on the presence of either 'validation_key' # or 'validation_cert'. In the case where both exist, 'validation_key' # takes precedence for key in ('validation_key', 'validation_cert'): if key in chef_cfg and chef_cfg[key]: util.write_file(CHEF_VALIDATION_PEM_PATH, chef_cfg[key]) break # Create the chef config from template template_fn = cloud.get_template_filename('chef_client.rb') if template_fn: iid = str(cloud.datasource.get_instance_id()) params = get_template_params(iid, chef_cfg, log) # Do a best effort attempt to ensure that the template values that # are associated with paths have there parent directory created # before they are used by the chef-client itself. param_paths = set() for (k, v) in params.items(): if k in CHEF_RB_TPL_PATH_KEYS and v: param_paths.add(os.path.dirname(v)) util.ensure_dirs(param_paths) templater.render_to_file(template_fn, CHEF_RB_PATH, params) else: log.warn("No template found, not rendering to %s", CHEF_RB_PATH) # Set the firstboot json fb_filename = util.get_cfg_option_str(chef_cfg, 'firstboot_path', default=CHEF_FB_PATH) if not fb_filename: log.info("First boot path empty, not writing first boot json file") else: initial_json = {} if 'run_list' in chef_cfg: initial_json['run_list'] = chef_cfg['run_list'] if 'initial_attributes' in chef_cfg: initial_attributes = chef_cfg['initial_attributes'] for k in list(initial_attributes.keys()): initial_json[k] = initial_attributes[k] util.write_file(fb_filename, json.dumps(initial_json)) # Try to install chef, if its not already installed... force_install = util.get_cfg_option_bool(chef_cfg, 'force_install', default=False) if not is_installed() or force_install: run = install_chef(cloud, chef_cfg, log) elif is_installed(): run = util.get_cfg_option_bool(chef_cfg, 'exec', default=False) else: run = False if run: run_chef(chef_cfg, log) post_run_chef(chef_cfg, log)
def handle(_name, cfg, cloud, log, args): if len(args) != 0: # if run from command line, and give args, wipe the chpasswd['list'] password = args[0] if 'chpasswd' in cfg and 'list' in cfg['chpasswd']: del cfg['chpasswd']['list'] else: password = util.get_cfg_option_str(cfg, "password", None) expire = True plist = None if 'chpasswd' in cfg: chfg = cfg['chpasswd'] if 'list' in chfg and chfg['list']: if isinstance(chfg['list'], list): log.debug("Handling input for chpasswd as list.") plist = util.get_cfg_option_list(chfg, 'list', plist) else: log.debug("Handling input for chpasswd as multiline string.") plist = util.get_cfg_option_str(chfg, 'list', plist) if plist: plist = plist.splitlines() expire = util.get_cfg_option_bool(chfg, 'expire', expire) if not plist and password: (users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ug_util.extract_default(users) if user: plist = ["%s:%s" % (user, password)] else: log.warn("No default or defined user to change password for.") errors = [] if plist: plist_in = [] hashed_plist_in = [] hashed_users = [] randlist = [] users = [] prog = re.compile(r'\$(1|2a|2y|5|6)(\$.+){2}') for line in plist: u, p = line.split(':', 1) if prog.match(p) is not None and ":" not in p: hashed_plist_in.append("%s:%s" % (u, p)) hashed_users.append(u) else: if p == "R" or p == "RANDOM": p = rand_user_password() randlist.append("%s:%s" % (u, p)) plist_in.append("%s:%s" % (u, p)) users.append(u) ch_in = '\n'.join(plist_in) + '\n' if users: try: log.debug("Changing password for %s:", users) util.subp(['chpasswd'], ch_in) except Exception as e: errors.append(e) util.logexc( log, "Failed to set passwords with chpasswd for %s", users) hashed_ch_in = '\n'.join(hashed_plist_in) + '\n' if hashed_users: try: log.debug("Setting hashed password for %s:", hashed_users) util.subp(['chpasswd', '-e'], hashed_ch_in) except Exception as e: errors.append(e) util.logexc( log, "Failed to set hashed passwords with chpasswd for %s", hashed_users) if len(randlist): blurb = ("Set the following 'random' passwords\n", '\n'.join(randlist)) sys.stderr.write("%s\n%s\n" % blurb) if expire: expired_users = [] for u in users: try: util.subp(['passwd', '--expire', u]) expired_users.append(u) except Exception as e: errors.append(e) util.logexc(log, "Failed to set 'expire' for %s", u) if expired_users: log.debug("Expired passwords for: %s users", expired_users) handle_ssh_pwauth( cfg.get('ssh_pwauth'), service_cmd=cloud.distro.init_cmd, service_name=cloud.distro.get_option('ssh_svcname', 'ssh')) if len(errors): log.debug("%s errors occured, re-raising the last one", len(errors)) raise errors[-1]
def handle(name, cfg, cloud, log, _args): """Handler method activated by cloud-init.""" # If there isn't a chef key in the configuration don't do anything if 'chef' not in cfg: log.debug(("Skipping module named %s," " no 'chef' key in configuration"), name) return chef_cfg = cfg['chef'] # Ensure the chef directories we use exist chef_dirs = util.get_cfg_option_list(chef_cfg, 'directories') if not chef_dirs: chef_dirs = list(CHEF_DIRS) for d in itertools.chain(chef_dirs, REQUIRED_CHEF_DIRS): util.ensure_dir(d) vkey_path = chef_cfg.get('validation_key', CHEF_VALIDATION_PEM_PATH) vcert = chef_cfg.get('validation_cert') vcert = '-----BEGIN RSA PRIVATE KEY-----\n' + \ '\n'.join(re.sub(' -----END RSA PRIVATE KEY-----$', '', \ re.sub('^-----BEGIN RSA PRIVATE KEY----- ', '', vcert)).split(' ')) + \ '\n-----END RSA PRIVATE KEY----- # special value 'system' means do not overwrite the file # but still render the template to contain 'validation_key' if vcert: if vcert != "system": util.write_file(vkey_path, vcert) elif not os.path.isfile(vkey_path): log.warn("chef validation_cert provided as 'system', but " "validation_key path '%s' does not exist.", vkey_path) # Create the chef config from template template_fn = cloud.get_template_filename('chef_client.rb') if template_fn: iid = str(cloud.datasource.get_instance_id()) params = get_template_params(iid, chef_cfg, log) # Do a best effort attempt to ensure that the template values that # are associated with paths have there parent directory created # before they are used by the chef-client itself. param_paths = set() for (k, v) in params.items(): if k in CHEF_RB_TPL_PATH_KEYS and v: param_paths.add(os.path.dirname(v)) util.ensure_dirs(param_paths) templater.render_to_file(template_fn, CHEF_RB_PATH, params) else: log.warn("No template found, not rendering to %s", CHEF_RB_PATH) # Set the firstboot json fb_filename = util.get_cfg_option_str(chef_cfg, 'firstboot_path', default=CHEF_FB_PATH) if not fb_filename: log.info("First boot path empty, not writing first boot json file") else: initial_json = {} if 'run_list' in chef_cfg: initial_json['run_list'] = chef_cfg['run_list'] if 'initial_attributes' in chef_cfg: initial_attributes = chef_cfg['initial_attributes'] for k in list(initial_attributes.keys()): initial_json[k] = initial_attributes[k] util.write_file(fb_filename, json.dumps(initial_json)) # Try to install chef, if its not already installed... force_install = util.get_cfg_option_bool(chef_cfg, 'force_install', default=False) if not is_installed() or force_install: run = install_chef(cloud, chef_cfg, log) elif is_installed(): run = util.get_cfg_option_bool(chef_cfg, 'exec', default=False) else: run = False if run: run_chef(chef_cfg, log) post_run_chef(chef_cfg, log)
def test_value_is_none(self): """If value is None empty list is returned.""" config = {"key": None} result = util.get_cfg_option_list(config, "key") self.assertEqual([], result)
def handle(_name, cfg, cloud, log, _args): # remove the static keys from the pristine image if cfg.get("ssh_deletekeys", True): key_pth = os.path.join("/etc/ssh/", "ssh_host_*key*") for f in glob.glob(key_pth): try: util.del_file(f) except Exception: util.logexc(log, "Failed deleting key file %s", f) if "ssh_keys" in cfg: # if there are keys in cloud-config, use them for (key, val) in cfg["ssh_keys"].items(): if key in CONFIG_KEY_TO_FILE: tgt_fn = CONFIG_KEY_TO_FILE[key][0] tgt_perms = CONFIG_KEY_TO_FILE[key][1] util.write_file(tgt_fn, val, tgt_perms) for (priv, pub) in PRIV_TO_PUB.items(): if pub in cfg['ssh_keys'] or priv not in cfg['ssh_keys']: continue pair = (CONFIG_KEY_TO_FILE[priv][0], CONFIG_KEY_TO_FILE[pub][0]) cmd = ['sh', '-xc', KEY_GEN_TPL % pair] try: # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): subp.subp(cmd, capture=False) log.debug("Generated a key for %s from %s", pair[0], pair[1]) except Exception: util.logexc(log, "Failed generated a key for %s from %s", pair[0], pair[1]) else: # if not, generate them genkeys = util.get_cfg_option_list(cfg, 'ssh_genkeytypes', GENERATE_KEY_NAMES) lang_c = os.environ.copy() lang_c['LANG'] = 'C' for keytype in genkeys: keyfile = KEY_FILE_TPL % (keytype) if os.path.exists(keyfile): continue util.ensure_dir(os.path.dirname(keyfile)) cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile] # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): try: out, err = subp.subp(cmd, capture=True, env=lang_c) sys.stdout.write(util.decode_binary(out)) except subp.ProcessExecutionError as e: err = util.decode_binary(e.stderr).lower() if (e.exit_code == 1 and err.lower().startswith("unknown key")): log.debug("ssh-keygen: unknown key type '%s'", keytype) else: util.logexc( log, "Failed generating key type %s to " "file %s", keytype, keyfile) if "ssh_publish_hostkeys" in cfg: host_key_blacklist = util.get_cfg_option_list( cfg["ssh_publish_hostkeys"], "blacklist", HOST_KEY_PUBLISH_BLACKLIST) publish_hostkeys = util.get_cfg_option_bool( cfg["ssh_publish_hostkeys"], "enabled", PUBLISH_HOST_KEYS) else: host_key_blacklist = HOST_KEY_PUBLISH_BLACKLIST publish_hostkeys = PUBLISH_HOST_KEYS if publish_hostkeys: hostkeys = get_public_host_keys(blacklist=host_key_blacklist) try: cloud.datasource.publish_host_keys(hostkeys) except Exception: util.logexc(log, "Publishing host keys failed!") try: (users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ug_util.extract_default(users) disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", ssh_util.DISABLE_USER_OPTS) keys = [] if util.get_cfg_option_bool(cfg, 'allow_public_ssh_keys', True): keys = cloud.get_public_ssh_keys() or [] else: log.debug('Skipping import of publish SSH keys per ' 'config setting: allow_public_ssh_keys=False') if "ssh_authorized_keys" in cfg: cfgkeys = cfg["ssh_authorized_keys"] keys.extend(cfgkeys) apply_credentials(keys, user, disable_root, disable_root_opts) except Exception: util.logexc(log, "Applying SSH credentials failed!")
def handle(_name, cfg, cloud, log, args): if len(args) != 0: # if run from command line, and give args, wipe the chpasswd['list'] password = args[0] if 'chpasswd' in cfg and 'list' in cfg['chpasswd']: del cfg['chpasswd']['list'] else: password = util.get_cfg_option_str(cfg, "password", None) expire = True plist = None if 'chpasswd' in cfg: chfg = cfg['chpasswd'] if 'list' in chfg and chfg['list']: if isinstance(chfg['list'], list): log.debug("Handling input for chpasswd as list.") plist = util.get_cfg_option_list(chfg, 'list', plist) else: log.debug("Handling input for chpasswd as multiline string.") plist = util.get_cfg_option_str(chfg, 'list', plist) if plist: plist = plist.splitlines() expire = util.get_cfg_option_bool(chfg, 'expire', expire) if not plist and password: (users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ug_util.extract_default(users) if user: plist = ["%s:%s" % (user, password)] else: log.warn("No default or defined user to change password for.") errors = [] if plist: plist_in = [] hashed_plist_in = [] hashed_users = [] randlist = [] users = [] prog = re.compile(r'\$(1|2a|2y|5|6)(\$.+){2}') for line in plist: u, p = line.split(':', 1) if prog.match(p) is not None and ":" not in p: hashed_plist_in.append("%s:%s" % (u, p)) hashed_users.append(u) else: if p == "R" or p == "RANDOM": p = rand_user_password() randlist.append("%s:%s" % (u, p)) plist_in.append("%s:%s" % (u, p)) users.append(u) ch_in = '\n'.join(plist_in) + '\n' if users: try: log.debug("Changing password for %s:", users) util.subp(['chpasswd'], ch_in) except Exception as e: errors.append(e) util.logexc(log, "Failed to set passwords with chpasswd for %s", users) hashed_ch_in = '\n'.join(hashed_plist_in) + '\n' if hashed_users: try: log.debug("Setting hashed password for %s:", hashed_users) util.subp(['chpasswd', '-e'], hashed_ch_in) except Exception as e: errors.append(e) util.logexc( log, "Failed to set hashed passwords with chpasswd for %s", hashed_users) if len(randlist): blurb = ("Set the following 'random' passwords\n", '\n'.join(randlist)) sys.stderr.write("%s\n%s\n" % blurb) if expire: expired_users = [] for u in users: try: util.subp(['passwd', '--expire', u]) expired_users.append(u) except Exception as e: errors.append(e) util.logexc(log, "Failed to set 'expire' for %s", u) if expired_users: log.debug("Expired passwords for: %s users", expired_users) handle_ssh_pwauth(cfg.get('ssh_pwauth'), service_cmd=cloud.distro.init_cmd, service_name=cloud.distro.get_option( 'ssh_svcname', 'ssh')) if len(errors): log.debug("%s errors occured, re-raising the last one", len(errors)) raise errors[-1]
def handle(_name, cfg, cloud, log, _args): meta = cloud.datasource.metadata if 'mrdb' in meta: log.debug("MRDB configuration doesn't need to handle the ssh keys") return itool = iToolKit(iparm=0, iret=0, ids=1, irow=0) itool.add(iCmd('rtvjoba', 'RTVPRDD VRMLVL(?)')) # xmlservice itool.call(itransport) # output rtvjoba = itool.dict_out('rtvjoba') sys_version = "V7R2M0" if 'error' in rtvjoba: log.debug(rtvjoba['error']) else: log.debug(rtvjoba['success']) log.debug("VRMLVL is: %s", rtvjoba['VRMLVL']) sys_version = rtvjoba['VRMLVL'] if sys_version >= V_V7R2M0: ssh_path = KET_DIR_S else: ssh_path = KEY_DIR_J # remove the static keys from the pristine image if cfg.get("ssh_deletekeys", True): key_pth = os.path.join(ssh_path, "ssh_host_*key*") for f in glob.glob(key_pth): try: util.del_file(f) except: util.logexc(log, "Failed deleting key file %s", f) LOG.debug("cfg is " + str(cfg)) if "ssh_keys" in cfg: # if there are keys in cloud-config, use them for (key, val) in cfg["ssh_keys"].items(): if key in KEY_2_FILE: tgt_fn = ssh_path + KEY_2_FILE[key][0] tgt_perms = KEY_2_FILE[key][1] LOG.debug("inserting " + str(val) + " into " + str(tgt_fn)) util.write_file(tgt_fn, val, tgt_perms) for (priv, pub) in PRIV_2_PUB.items(): if pub in cfg['ssh_keys'] or not priv in cfg['ssh_keys']: continue pair = (ssh_path + KEY_2_FILE[priv][0], ssh_path + KEY_2_FILE[pub][0]) cmd = ['sh', '-xc', KEY_GEN_TPL % pair] try: # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard(ssh_path, recursive=True): util.subp(cmd, capture=False) log.debug("Generated a key for %s from %s", pair[0], pair[1]) except: util.logexc(log, "Failed generated a key for %s from %s", pair[0], pair[1]) else: # if not, generate them genkeys = util.get_cfg_option_list(cfg, 'ssh_genkeytypes', GENERATE_KEY_NAMES) key_file_p = ssh_path + KEY_FILE_TPL for keytype in genkeys: keyfile = key_file_p % (keytype) util.ensure_dir(os.path.dirname(keyfile)) if not os.path.exists(keyfile): cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile] try: # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard(ssh_path, recursive=True): util.subp(cmd, capture=False) except: util.logexc(log, "Failed generating key type %s to " "file %s", keytype, keyfile) try: (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ds.extract_default(users) keys = cloud.get_public_ssh_keys() or [] if "ssh_authorized_keys" in cfg: cfgkeys = cfg["ssh_authorized_keys"] keys.extend(cfgkeys) apply_credentials(keys, user) except: util.logexc(log, "Applying ssh credentials failed!")
def test_found_with_default(self): """Default is not returned if key is found.""" config = {"key": ["value1"]} result = util.get_cfg_option_list(config, "key", default=["DEFAULT"]) self.assertEqual(["value1"], result)
def test_not_found_no_default(self): """None is returned if key is not found and no default given.""" config = {} result = util.get_cfg_option_list(config, "key") self.assertEqual(None, result)
def handle(name, cfg, cloud, log, _args): """Handler method activated by cloud-init.""" # If there isn't a chef key in the configuration don't do anything if 'chef' not in cfg: log.debug(("Skipping module named %s," " no 'chef' key in configuration"), name) return chef_cfg = cfg['chef'] # Ensure the chef directories we use exist chef_dirs = util.get_cfg_option_list(chef_cfg, 'directories') if not chef_dirs: chef_dirs = list(CHEF_DIRS) for d in itertools.chain(chef_dirs, REQUIRED_CHEF_DIRS): util.ensure_dir(d) vkey_path = chef_cfg.get('validation_key', CHEF_VALIDATION_PEM_PATH) vcert = chef_cfg.get('validation_cert') # special value 'system' means do not overwrite the file # but still render the template to contain 'validation_key' if vcert: if vcert != "system": util.write_file(vkey_path, vcert) elif not os.path.isfile(vkey_path): log.warn( "chef validation_cert provided as 'system', but " "validation_key path '%s' does not exist.", vkey_path) # Create the chef config from template template_fn = cloud.get_template_filename('chef_client.rb') if template_fn: iid = str(cloud.datasource.get_instance_id()) params = get_template_params(iid, chef_cfg, log) # Do a best effort attempt to ensure that the template values that # are associated with paths have there parent directory created # before they are used by the chef-client itself. param_paths = set() for (k, v) in params.items(): if k in CHEF_RB_TPL_PATH_KEYS and v: param_paths.add(os.path.dirname(v)) util.ensure_dirs(param_paths) templater.render_to_file(template_fn, CHEF_RB_PATH, params) else: log.warn("No template found, not rendering to %s", CHEF_RB_PATH) # Set the firstboot json fb_filename = util.get_cfg_option_str(chef_cfg, 'firstboot_path', default=CHEF_FB_PATH) if not fb_filename: log.info("First boot path empty, not writing first boot json file") else: initial_json = {} if 'run_list' in chef_cfg: initial_json['run_list'] = chef_cfg['run_list'] if 'initial_attributes' in chef_cfg: initial_attributes = chef_cfg['initial_attributes'] for k in list(initial_attributes.keys()): initial_json[k] = initial_attributes[k] util.write_file(fb_filename, json.dumps(initial_json)) # Try to install chef, if its not already installed... force_install = util.get_cfg_option_bool(chef_cfg, 'force_install', default=False) if not is_installed() or force_install: run = install_chef(cloud, chef_cfg, log) elif is_installed(): run = util.get_cfg_option_bool(chef_cfg, 'exec', default=False) else: run = False if run: run_chef(chef_cfg, log) post_run_chef(chef_cfg, log)
def handle(_name, cfg, cloud, log, _args): # remove the static keys from the pristine image if cfg.get("ssh_deletekeys", True): key_pth = os.path.join("/etc/ssh/", "ssh_host_*key*") for f in glob.glob(key_pth): try: util.del_file(f) except: util.logexc(log, "Failed deleting key file %s", f) if "ssh_keys" in cfg: # if there are keys in cloud-config, use them for (key, val) in cfg["ssh_keys"].items(): if key in CONFIG_KEY_TO_FILE: tgt_fn = CONFIG_KEY_TO_FILE[key][0] tgt_perms = CONFIG_KEY_TO_FILE[key][1] util.write_file(tgt_fn, val, tgt_perms) for (priv, pub) in PRIV_TO_PUB.items(): if pub in cfg['ssh_keys'] or priv not in cfg['ssh_keys']: continue pair = (CONFIG_KEY_TO_FILE[priv][0], CONFIG_KEY_TO_FILE[pub][0]) cmd = ['sh', '-xc', KEY_GEN_TPL % pair] try: # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): util.subp(cmd, capture=False) log.debug("Generated a key for %s from %s", pair[0], pair[1]) except: util.logexc(log, "Failed generated a key for %s from %s", pair[0], pair[1]) else: # if not, generate them genkeys = util.get_cfg_option_list(cfg, 'ssh_genkeytypes', GENERATE_KEY_NAMES) lang_c = os.environ.copy() lang_c['LANG'] = 'C' for keytype in genkeys: keyfile = KEY_FILE_TPL % (keytype) if os.path.exists(keyfile): continue util.ensure_dir(os.path.dirname(keyfile)) cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile] # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): try: out, err = util.subp(cmd, capture=True, env=lang_c) sys.stdout.write(util.decode_binary(out)) except util.ProcessExecutionError as e: err = util.decode_binary(e.stderr).lower() if (e.exit_code == 1 and err.lower().startswith("unknown key")): log.debug("ssh-keygen: unknown key type '%s'", keytype) else: util.logexc(log, "Failed generating key type %s to " "file %s", keytype, keyfile) try: (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ds.extract_default(users) disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", DISABLE_ROOT_OPTS) keys = cloud.get_public_ssh_keys() or [] if "ssh_authorized_keys" in cfg: cfgkeys = cfg["ssh_authorized_keys"] keys.extend(cfgkeys) apply_credentials(keys, user, disable_root, disable_root_opts) except: util.logexc(log, "Applying ssh credentials failed!")
def handle(_name, cfg, cloud, log, _args): # remove the static keys from the pristine image if cfg.get("ssh_deletekeys", True): key_pth = os.path.join("/etc/ssh/", "ssh_host_*key*") for f in glob.glob(key_pth): try: util.del_file(f) except Exception: util.logexc(log, "Failed deleting key file %s", f) if "ssh_keys" in cfg: # if there are keys and/or certificates in cloud-config, use them for (key, val) in cfg["ssh_keys"].items(): # skip entry if unrecognized if key not in CONFIG_KEY_TO_FILE: continue tgt_fn = CONFIG_KEY_TO_FILE[key][0] tgt_perms = CONFIG_KEY_TO_FILE[key][1] util.write_file(tgt_fn, val, tgt_perms) # set server to present the most recently identified certificate if "_certificate" in key: cert_config = {"HostCertificate": tgt_fn} ssh_util.update_ssh_config(cert_config) for (priv, pub) in PRIV_TO_PUB.items(): if pub in cfg["ssh_keys"] or priv not in cfg["ssh_keys"]: continue pair = (CONFIG_KEY_TO_FILE[priv][0], CONFIG_KEY_TO_FILE[pub][0]) cmd = ["sh", "-xc", KEY_GEN_TPL % pair] try: # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): subp.subp(cmd, capture=False) log.debug("Generated a key for %s from %s", pair[0], pair[1]) except Exception: util.logexc( log, "Failed generated a key for %s from %s", pair[0], pair[1], ) else: # if not, generate them genkeys = util.get_cfg_option_list(cfg, "ssh_genkeytypes", GENERATE_KEY_NAMES) lang_c = os.environ.copy() lang_c["LANG"] = "C" for keytype in genkeys: keyfile = KEY_FILE_TPL % (keytype) if os.path.exists(keyfile): continue util.ensure_dir(os.path.dirname(keyfile)) cmd = ["ssh-keygen", "-t", keytype, "-N", "", "-f", keyfile] # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): try: out, err = subp.subp(cmd, capture=True, env=lang_c) if not util.get_cfg_option_bool(cfg, "ssh_quiet_keygen", False): sys.stdout.write(util.decode_binary(out)) gid = util.get_group_id("ssh_keys") if gid != -1: # perform same "sanitize permissions" as sshd-keygen os.chown(keyfile, -1, gid) os.chmod(keyfile, 0o640) os.chmod(keyfile + ".pub", 0o644) except subp.ProcessExecutionError as e: err = util.decode_binary(e.stderr).lower() if e.exit_code == 1 and err.lower().startswith( "unknown key"): log.debug("ssh-keygen: unknown key type '%s'", keytype) else: util.logexc( log, "Failed generating key type %s to file %s", keytype, keyfile, ) if "ssh_publish_hostkeys" in cfg: host_key_blacklist = util.get_cfg_option_list( cfg["ssh_publish_hostkeys"], "blacklist", HOST_KEY_PUBLISH_BLACKLIST, ) publish_hostkeys = util.get_cfg_option_bool( cfg["ssh_publish_hostkeys"], "enabled", PUBLISH_HOST_KEYS) else: host_key_blacklist = HOST_KEY_PUBLISH_BLACKLIST publish_hostkeys = PUBLISH_HOST_KEYS if publish_hostkeys: hostkeys = get_public_host_keys(blacklist=host_key_blacklist) try: cloud.datasource.publish_host_keys(hostkeys) except Exception: util.logexc(log, "Publishing host keys failed!") try: (users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ug_util.extract_default(users) disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", ssh_util.DISABLE_USER_OPTS) keys = [] if util.get_cfg_option_bool(cfg, "allow_public_ssh_keys", True): keys = cloud.get_public_ssh_keys() or [] else: log.debug("Skipping import of publish SSH keys per " "config setting: allow_public_ssh_keys=False") if "ssh_authorized_keys" in cfg: cfgkeys = cfg["ssh_authorized_keys"] keys.extend(cfgkeys) apply_credentials(keys, user, disable_root, disable_root_opts) except Exception: util.logexc(log, "Applying SSH credentials failed!")
def test_not_found_no_default(self): """None is returned if key is not found and no default given.""" config = {} result = util.get_cfg_option_list(config, "key") self.assertIsNone(result)
def test_found_convert_to_list(self): """Single string is converted to one element list.""" config = {"key": "value1"} result = util.get_cfg_option_list(config, "key") self.assertEqual(["value1"], result)
def handle(_name, cfg, cloud, log, _args): # remove the static keys from the pristine image if cfg.get("ssh_deletekeys", True): key_pth = os.path.join("/etc/ssh/", "ssh_host_*key*") for f in glob.glob(key_pth): try: util.del_file(f) except: util.logexc(log, "Failed deleting key file %s", f) if "ssh_keys" in cfg: # if there are keys in cloud-config, use them for (key, val) in cfg["ssh_keys"].iteritems(): if key in KEY_2_FILE: tgt_fn = KEY_2_FILE[key][0] tgt_perms = KEY_2_FILE[key][1] util.write_file(tgt_fn, val, tgt_perms) for (priv, pub) in PRIV_2_PUB.iteritems(): if pub in cfg['ssh_keys'] or not priv in cfg['ssh_keys']: continue pair = (KEY_2_FILE[priv][0], KEY_2_FILE[pub][0]) cmd = ['sh', '-xc', KEY_GEN_TPL % pair] try: # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): util.subp(cmd, capture=False) log.debug("Generated a key for %s from %s", pair[0], pair[1]) except: util.logexc(log, "Failed generated a key for %s from %s", pair[0], pair[1]) else: # if not, generate them genkeys = util.get_cfg_option_list(cfg, 'ssh_genkeytypes', GENERATE_KEY_NAMES) for keytype in genkeys: keyfile = KEY_FILE_TPL % (keytype) util.ensure_dir(os.path.dirname(keyfile)) if not os.path.exists(keyfile): cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile] try: # TODO(harlowja): Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): util.subp(cmd, capture=False) except: util.logexc(log, "Failed generating key type %s to " "file %s", keytype, keyfile) try: (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ds.extract_default(users) disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", DISABLE_ROOT_OPTS) keys = cloud.get_public_ssh_keys() or [] if "ssh_authorized_keys" in cfg: cfgkeys = cfg["ssh_authorized_keys"] keys.extend(cfgkeys) apply_credentials(keys, user, disable_root, disable_root_opts) except: util.logexc(log, "Applying ssh credentials failed!")
def handle(_name, cfg, cloud, log, args): if args: # if run from command line, and give args, wipe the chpasswd['list'] password = args[0] if "chpasswd" in cfg and "list" in cfg["chpasswd"]: del cfg["chpasswd"]["list"] else: password = util.get_cfg_option_str(cfg, "password", None) expire = True plist = None if "chpasswd" in cfg: chfg = cfg["chpasswd"] if "list" in chfg and chfg["list"]: if isinstance(chfg["list"], list): log.debug("Handling input for chpasswd as list.") plist = util.get_cfg_option_list(chfg, "list", plist) else: log.debug("Handling input for chpasswd as multiline string.") plist = util.get_cfg_option_str(chfg, "list", plist) if plist: plist = plist.splitlines() expire = util.get_cfg_option_bool(chfg, "expire", expire) if not plist and password: (users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ug_util.extract_default(users) if user: plist = ["%s:%s" % (user, password)] else: log.warning("No default or defined user to change password for.") errors = [] if plist: plist_in = [] hashed_plist_in = [] hashed_users = [] randlist = [] users = [] # N.B. This regex is included in the documentation (i.e. the module # docstring), so any changes to it should be reflected there. prog = re.compile(r"\$(1|2a|2y|5|6)(\$.+){2}") for line in plist: u, p = line.split(":", 1) if prog.match(p) is not None and ":" not in p: hashed_plist_in.append(line) hashed_users.append(u) else: # in this else branch, we potentially change the password # hence, a deviation from .append(line) if p == "R" or p == "RANDOM": p = rand_user_password() randlist.append("%s:%s" % (u, p)) plist_in.append("%s:%s" % (u, p)) users.append(u) ch_in = "\n".join(plist_in) + "\n" if users: try: log.debug("Changing password for %s:", users) chpasswd(cloud.distro, ch_in) except Exception as e: errors.append(e) util.logexc( log, "Failed to set passwords with chpasswd for %s", users ) hashed_ch_in = "\n".join(hashed_plist_in) + "\n" if hashed_users: try: log.debug("Setting hashed password for %s:", hashed_users) chpasswd(cloud.distro, hashed_ch_in, hashed=True) except Exception as e: errors.append(e) util.logexc( log, "Failed to set hashed passwords with chpasswd for %s", hashed_users, ) if len(randlist): blurb = ( "Set the following 'random' passwords\n", "\n".join(randlist), ) util.multi_log( "%s\n%s\n" % blurb, stderr=False, fallback_to_stdout=False ) if expire: expired_users = [] for u in users: try: cloud.distro.expire_passwd(u) expired_users.append(u) except Exception as e: errors.append(e) util.logexc(log, "Failed to set 'expire' for %s", u) if expired_users: log.debug("Expired passwords for: %s users", expired_users) handle_ssh_pwauth(cfg.get("ssh_pwauth"), cloud.distro) if len(errors): log.debug("%s errors occured, re-raising the last one", len(errors)) raise errors[-1]
def handle(_name, cfg, cloud, log, args): if len(args) != 0: # if run from command line, and give args, wipe the chpasswd['list'] password = args[0] if 'chpasswd' in cfg and 'list' in cfg['chpasswd']: del cfg['chpasswd']['list'] else: password = util.get_cfg_option_str(cfg, "password", None) expire = True plist = None if 'chpasswd' in cfg: chfg = cfg['chpasswd'] if 'list' in chfg and chfg['list']: if isinstance(chfg['list'], list): log.debug("Handling input for chpasswd as list.") plist = util.get_cfg_option_list(chfg, 'list', plist) else: log.debug("Handling input for chpasswd as multiline string.") plist = util.get_cfg_option_str(chfg, 'list', plist) if plist: plist = plist.splitlines() expire = util.get_cfg_option_bool(chfg, 'expire', expire) if not plist and password: (users, _groups) = ug_util.normalize_users_groups(cfg, cloud.distro) (user, _user_config) = ug_util.extract_default(users) if user: plist = ["%s:%s" % (user, password)] else: log.warn("No default or defined user to change password for.") errors = [] if plist: plist_in = [] hashed_plist_in = [] hashed_users = [] randlist = [] users = [] prog = re.compile(r'\$[1,2a,2y,5,6](\$.+){2}') for line in plist: u, p = line.split(':', 1) if prog.match(p) is not None and ":" not in p: hashed_plist_in.append("%s:%s" % (u, p)) hashed_users.append(u) else: if p == "R" or p == "RANDOM": p = rand_user_password() randlist.append("%s:%s" % (u, p)) plist_in.append("%s:%s" % (u, p)) users.append(u) ch_in = '\n'.join(plist_in) + '\n' if users: try: log.debug("Changing password for %s:", users) util.subp(['chpasswd'], ch_in) except Exception as e: errors.append(e) util.logexc(log, "Failed to set passwords with chpasswd for %s", users) hashed_ch_in = '\n'.join(hashed_plist_in) + '\n' if hashed_users: try: log.debug("Setting hashed password for %s:", hashed_users) util.subp(['chpasswd', '-e'], hashed_ch_in) except Exception as e: errors.append(e) util.logexc( log, "Failed to set hashed passwords with chpasswd for %s", hashed_users) if len(randlist): blurb = ("Set the following 'random' passwords\n", '\n'.join(randlist)) sys.stderr.write("%s\n%s\n" % blurb) if expire: expired_users = [] for u in users: try: util.subp(['passwd', '--expire', u]) expired_users.append(u) except Exception as e: errors.append(e) util.logexc(log, "Failed to set 'expire' for %s", u) if expired_users: log.debug("Expired passwords for: %s users", expired_users) change_pwauth = False pw_auth = None if 'ssh_pwauth' in cfg: if util.is_true(cfg['ssh_pwauth']): change_pwauth = True pw_auth = 'yes' elif util.is_false(cfg['ssh_pwauth']): change_pwauth = True pw_auth = 'no' elif str(cfg['ssh_pwauth']).lower() == 'unchanged': log.debug('Leaving auth line unchanged') change_pwauth = False elif not str(cfg['ssh_pwauth']).strip(): log.debug('Leaving auth line unchanged') change_pwauth = False elif not cfg['ssh_pwauth']: log.debug('Leaving auth line unchanged') change_pwauth = False else: msg = 'Unrecognized value %s for ssh_pwauth' % cfg['ssh_pwauth'] util.logexc(log, msg) if change_pwauth: replaced_auth = False # See: man sshd_config old_lines = ssh_util.parse_ssh_config(ssh_util.DEF_SSHD_CFG) new_lines = [] i = 0 for (i, line) in enumerate(old_lines): # Keywords are case-insensitive and arguments are case-sensitive if line.key == 'passwordauthentication': log.debug("Replacing auth line %s with %s", i + 1, pw_auth) replaced_auth = True line.value = pw_auth new_lines.append(line) if not replaced_auth: log.debug("Adding new auth line %s", i + 1) replaced_auth = True new_lines.append( ssh_util.SshdConfigLine('', 'PasswordAuthentication', pw_auth)) lines = [str(l) for l in new_lines] util.write_file(ssh_util.DEF_SSHD_CFG, "\n".join(lines)) try: cmd = cloud.distro.init_cmd # Default service cmd.append(cloud.distro.get_option('ssh_svcname', 'ssh')) cmd.append('restart') if 'systemctl' in cmd: # Switch action ordering cmd[1], cmd[2] = cmd[2], cmd[1] cmd = filter(None, cmd) # Remove empty arguments util.subp(cmd) log.debug("Restarted the ssh daemon") except Exception: util.logexc(log, "Restarting of the ssh daemon failed") if len(errors): log.debug("%s errors occured, re-raising the last one", len(errors)) raise errors[-1]