def handle_ssh_pwauth(pw_auth, distro): """Apply sshd PasswordAuthentication changes. @param pw_auth: config setting from 'pw_auth'. Best given as True, False, or "unchanged". @param distro: an instance of the distro class for the target distribution @return: None""" cfg_name = "PasswordAuthentication" if util.is_true(pw_auth): cfg_val = "yes" elif util.is_false(pw_auth): cfg_val = "no" else: bmsg = "Leaving SSH config '%s' unchanged." % cfg_name if pw_auth is None or pw_auth.lower() == "unchanged": LOG.debug("%s ssh_pwauth=%s", bmsg, pw_auth) else: LOG.warning("%s Unrecognized value: ssh_pwauth=%s", bmsg, pw_auth) return updated = update_ssh_config({cfg_name: cfg_val}) if not updated: LOG.debug("No need to restart SSH service, %s not updated.", cfg_name) return distro.manage_service("restart", distro.get_option("ssh_svcname", "ssh")) LOG.debug("Restarted the SSH daemon.")
def test_not_modified(self): mycfg = self.tmp_path("ssh_config_2") util.write_file(mycfg, self.cfgdata) with patch("cloudinit.ssh_util.util.write_file") as m_write_file: ret = ssh_util.update_ssh_config({"MyKey": "ORIG_VAL"}, mycfg) self.assertFalse(ret) self.assertEqual(self.cfgdata, util.load_file(mycfg)) m_write_file.assert_not_called()
def test_modified(self): mycfg = self.tmp_path("ssh_config_1") util.write_file(mycfg, self.cfgdata) ret = ssh_util.update_ssh_config({"MyKey": "NEW_VAL"}, mycfg) self.assertTrue(ret) found = util.load_file(mycfg) self.assertEqual(self.cfgdata.replace("ORIG_VAL", "NEW_VAL"), found) # assert there is a newline at end of file (LP: #1677205) self.assertEqual('\n', found[-1])
def handle_ssh_pwauth(pw_auth, service_cmd=None, service_name="ssh"): """Apply sshd PasswordAuthentication changes. @param pw_auth: config setting from 'pw_auth'. Best given as True, False, or "unchanged". @param service_cmd: The service command list (['service']) @param service_name: The name of the sshd service for the system. @return: None""" cfg_name = "PasswordAuthentication" if service_cmd is None: service_cmd = ["service"] if util.is_true(pw_auth): cfg_val = 'yes' elif util.is_false(pw_auth): cfg_val = 'no' else: bmsg = "Leaving ssh config '%s' unchanged." % cfg_name if pw_auth is None or pw_auth.lower() == 'unchanged': LOG.debug("%s ssh_pwauth=%s", bmsg, pw_auth) else: LOG.warning("%s Unrecognized value: ssh_pwauth=%s", bmsg, pw_auth) return updated = update_ssh_config({cfg_name: cfg_val}) if not updated: LOG.debug("No need to restart ssh service, %s not updated.", cfg_name) return if 'systemctl' in service_cmd: cmd = list(service_cmd) + ["restart", service_name] else: cmd = list(service_cmd) + [service_name, "restart"] util.subp(cmd) LOG.debug("Restarted the ssh daemon.")
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 handle_ssh_pwauth(pw_auth, distro: Distro): """Apply sshd PasswordAuthentication changes. @param pw_auth: config setting from 'pw_auth'. Best given as True, False, or "unchanged". @param distro: an instance of the distro class for the target distribution @return: None""" service = distro.get_option("ssh_svcname", "ssh") restart_ssh = True try: distro.manage_service("status", service) except subp.ProcessExecutionError as e: uses_systemd = distro.uses_systemd() if not uses_systemd: LOG.debug( "Writing config 'ssh_pwauth: %s'. SSH service '%s'" " will not be restarted because it is not running or not" " available.", pw_auth, service, ) restart_ssh = False elif e.exit_code == 3: # Service is not running. Write ssh config. LOG.debug( "Writing config 'ssh_pwauth: %s'. SSH service '%s'" " will not be restarted because it is stopped.", pw_auth, service, ) restart_ssh = False elif e.exit_code == 4: # Service status is unknown LOG.warning( "Ignoring config 'ssh_pwauth: %s'." " SSH service '%s' is not installed.", pw_auth, service, ) return else: LOG.warning( "Ignoring config 'ssh_pwauth: %s'." " SSH service '%s' is not available. Error: %s.", pw_auth, service, e, ) return cfg_name = "PasswordAuthentication" if isinstance(pw_auth, str): LOG.warning( "DEPRECATION: The 'ssh_pwauth' config key should be set to " "a boolean value. The string format is deprecated and will be " "removed in a future version of cloud-init.") if util.is_true(pw_auth): cfg_val = "yes" elif util.is_false(pw_auth): cfg_val = "no" else: bmsg = "Leaving SSH config '%s' unchanged." % cfg_name if pw_auth is None or pw_auth.lower() == "unchanged": LOG.debug("%s ssh_pwauth=%s", bmsg, pw_auth) else: LOG.warning("%s Unrecognized value: ssh_pwauth=%s", bmsg, pw_auth) return updated = update_ssh_config({cfg_name: cfg_val}) if not updated: LOG.debug("No need to restart SSH service, %s not updated.", cfg_name) return if restart_ssh: distro.manage_service("restart", service) LOG.debug("Restarted the SSH daemon.") else: LOG.debug("Not restarting SSH service: service is stopped.")