summaryrefslogtreecommitdiff
path: root/cloudinit/ssh_util.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/ssh_util.py')
-rw-r--r--cloudinit/ssh_util.py172
1 files changed, 109 insertions, 63 deletions
diff --git a/cloudinit/ssh_util.py b/cloudinit/ssh_util.py
index 33679dcc..ab4c63aa 100644
--- a/cloudinit/ssh_util.py
+++ b/cloudinit/ssh_util.py
@@ -60,14 +60,16 @@ _DISABLE_USER_SSH_EXIT = 142
DISABLE_USER_OPTS = (
"no-port-forwarding,no-agent-forwarding,"
- "no-X11-forwarding,command=\"echo \'Please login as the user \\\"$USER\\\""
- " rather than the user \\\"$DISABLE_USER\\\".\';echo;sleep 10;"
- "exit " + str(_DISABLE_USER_SSH_EXIT) + "\"")
+ 'no-X11-forwarding,command="echo \'Please login as the user \\"$USER\\"'
+ ' rather than the user \\"$DISABLE_USER\\".\';echo;sleep 10;'
+ "exit " + str(_DISABLE_USER_SSH_EXIT) + '"'
+)
class AuthKeyLine(object):
- def __init__(self, source, keytype=None, base64=None,
- comment=None, options=None):
+ def __init__(
+ self, source, keytype=None, base64=None, comment=None, options=None
+ ):
self.base64 = base64
self.comment = comment
self.options = options
@@ -75,7 +77,7 @@ class AuthKeyLine(object):
self.source = source
def valid(self):
- return (self.base64 and self.keytype)
+ return self.base64 and self.keytype
def __str__(self):
toks = []
@@ -90,7 +92,7 @@ class AuthKeyLine(object):
if not toks:
return self.source
else:
- return ' '.join(toks)
+ return " ".join(toks)
class AuthKeyLineParser(object):
@@ -121,8 +123,7 @@ class AuthKeyLineParser(object):
"""
quoted = False
i = 0
- while (i < len(ent) and
- ((quoted) or (ent[i] not in (" ", "\t")))):
+ while i < len(ent) and ((quoted) or (ent[i] not in (" ", "\t"))):
curc = ent[i]
if i + 1 >= len(ent):
i = i + 1
@@ -143,7 +144,7 @@ class AuthKeyLineParser(object):
def parse(self, src_line, options=None):
# modeled after opensshes auth2-pubkey.c:user_key_allowed2
line = src_line.rstrip("\r\n")
- if line.startswith("#") or line.strip() == '':
+ if line.startswith("#") or line.strip() == "":
return AuthKeyLine(src_line)
def parse_ssh_key(ent):
@@ -174,8 +175,13 @@ class AuthKeyLineParser(object):
except TypeError:
return AuthKeyLine(src_line)
- return AuthKeyLine(src_line, keytype=keytype, base64=base64,
- comment=comment, options=options)
+ return AuthKeyLine(
+ src_line,
+ keytype=keytype,
+ base64=base64,
+ comment=comment,
+ options=options,
+ )
def parse_authorized_keys(fnames):
@@ -218,15 +224,15 @@ def update_authorized_keys(old_entries, keys):
lines = [str(b) for b in old_entries]
# Ensure it ends with a newline
- lines.append('')
- return '\n'.join(lines)
+ lines.append("")
+ return "\n".join(lines)
def users_ssh_info(username):
pw_ent = pwd.getpwnam(username)
if not pw_ent or not pw_ent.pw_dir:
raise RuntimeError("Unable to get SSH info for user %r" % (username))
- return (os.path.join(pw_ent.pw_dir, '.ssh'), pw_ent)
+ return (os.path.join(pw_ent.pw_dir, ".ssh"), pw_ent)
def render_authorizedkeysfile_paths(value, homedir, username):
@@ -269,9 +275,14 @@ def check_permissions(username, current_path, full_path, is_file, strictmodes):
# 1. owner must be either root or the user itself
owner = util.get_owner(current_path)
if strictmodes and owner != username and owner != "root":
- LOG.debug("Path %s in %s must be own by user %s or"
- " by root, but instead is own by %s. Ignoring key.",
- current_path, full_path, username, owner)
+ LOG.debug(
+ "Path %s in %s must be own by user %s or"
+ " by root, but instead is own by %s. Ignoring key.",
+ current_path,
+ full_path,
+ username,
+ owner,
+ )
return False
parent_permission = util.get_permissions(current_path)
@@ -291,17 +302,24 @@ def check_permissions(username, current_path, full_path, is_file, strictmodes):
minimal_permissions &= 0o007
if parent_permission & minimal_permissions == 0:
- LOG.debug("Path %s in %s must be accessible by user %s,"
- " check its permissions",
- current_path, full_path, username)
+ LOG.debug(
+ "Path %s in %s must be accessible by user %s,"
+ " check its permissions",
+ current_path,
+ full_path,
+ username,
+ )
return False
# 3. no write permission (w) is given to group and world users (022)
# Group and world user can still have +rx.
if strictmodes and parent_permission & 0o022 != 0:
- LOG.debug("Path %s in %s must not give write"
- "permission to group or world users. Ignoring key.",
- current_path, full_path)
+ LOG.debug(
+ "Path %s in %s must not give write"
+ "permission to group or world users. Ignoring key.",
+ current_path,
+ full_path,
+ )
return False
return True
@@ -326,17 +344,20 @@ def check_create_path(username, filename, strictmodes):
if os.path.islink(parent_folder):
LOG.debug(
"Invalid directory. Symlink exists in path: %s",
- parent_folder)
+ parent_folder,
+ )
return False
if os.path.isfile(parent_folder):
LOG.debug(
- "Invalid directory. File exists in path: %s",
- parent_folder)
+ "Invalid directory. File exists in path: %s", parent_folder
+ )
return False
- if (home_folder.startswith(parent_folder) or
- parent_folder == user_pwent.pw_dir):
+ if (
+ home_folder.startswith(parent_folder)
+ or parent_folder == user_pwent.pw_dir
+ ):
continue
if not os.path.exists(parent_folder):
@@ -354,8 +375,9 @@ def check_create_path(username, filename, strictmodes):
os.makedirs(parent_folder, mode=mode, exist_ok=True)
util.chownbyid(parent_folder, uid, gid)
- permissions = check_permissions(username, parent_folder,
- filename, False, strictmodes)
+ permissions = check_permissions(
+ username, parent_folder, filename, False, strictmodes
+ )
if not permissions:
return False
@@ -367,11 +389,12 @@ def check_create_path(username, filename, strictmodes):
if not os.path.exists(filename):
# if file does not exist: we need to create it, since the
# folders at this point exist and have right permissions
- util.write_file(filename, '', mode=0o600, ensure_dir_exists=True)
+ util.write_file(filename, "", mode=0o600, ensure_dir_exists=True)
util.chownbyid(filename, user_pwent.pw_uid, user_pwent.pw_gid)
- permissions = check_permissions(username, filename,
- filename, True, strictmodes)
+ permissions = check_permissions(
+ username, filename, filename, True, strictmodes
+ )
if not permissions:
return False
except (IOError, OSError) as e:
@@ -383,34 +406,44 @@ def check_create_path(username, filename, strictmodes):
def extract_authorized_keys(username, sshd_cfg_file=DEF_SSHD_CFG):
(ssh_dir, pw_ent) = users_ssh_info(username)
- default_authorizedkeys_file = os.path.join(ssh_dir, 'authorized_keys')
+ default_authorizedkeys_file = os.path.join(ssh_dir, "authorized_keys")
user_authorizedkeys_file = default_authorizedkeys_file
auth_key_fns = []
with util.SeLinuxGuard(ssh_dir, recursive=True):
try:
ssh_cfg = parse_ssh_config_map(sshd_cfg_file)
- key_paths = ssh_cfg.get("authorizedkeysfile",
- "%h/.ssh/authorized_keys")
+ key_paths = ssh_cfg.get(
+ "authorizedkeysfile", "%h/.ssh/authorized_keys"
+ )
strictmodes = ssh_cfg.get("strictmodes", "yes")
auth_key_fns = render_authorizedkeysfile_paths(
- key_paths, pw_ent.pw_dir, username)
+ key_paths, pw_ent.pw_dir, username
+ )
except (IOError, OSError):
# Give up and use a default key filename
auth_key_fns[0] = default_authorizedkeys_file
- util.logexc(LOG, "Failed extracting 'AuthorizedKeysFile' in SSH "
- "config from %r, using 'AuthorizedKeysFile' file "
- "%r instead", DEF_SSHD_CFG, auth_key_fns[0])
+ util.logexc(
+ LOG,
+ "Failed extracting 'AuthorizedKeysFile' in SSH "
+ "config from %r, using 'AuthorizedKeysFile' file "
+ "%r instead",
+ DEF_SSHD_CFG,
+ auth_key_fns[0],
+ )
# check if one of the keys is the user's one and has the right permissions
for key_path, auth_key_fn in zip(key_paths.split(), auth_key_fns):
- if any([
- '%u' in key_path,
- '%h' in key_path,
- auth_key_fn.startswith('{}/'.format(pw_ent.pw_dir))
- ]):
- permissions_ok = check_create_path(username, auth_key_fn,
- strictmodes == "yes")
+ if any(
+ [
+ "%u" in key_path,
+ "%h" in key_path,
+ auth_key_fn.startswith("{}/".format(pw_ent.pw_dir)),
+ ]
+ ):
+ permissions_ok = check_create_path(
+ username, auth_key_fn, strictmodes == "yes"
+ )
if permissions_ok:
user_authorizedkeys_file = auth_key_fn
break
@@ -418,11 +451,13 @@ def extract_authorized_keys(username, sshd_cfg_file=DEF_SSHD_CFG):
if user_authorizedkeys_file != default_authorizedkeys_file:
LOG.debug(
"AuthorizedKeysFile has an user-specific authorized_keys, "
- "using %s", user_authorizedkeys_file)
+ "using %s",
+ user_authorizedkeys_file,
+ )
return (
user_authorizedkeys_file,
- parse_authorized_keys([user_authorizedkeys_file])
+ parse_authorized_keys([user_authorizedkeys_file]),
)
@@ -485,11 +520,13 @@ def parse_ssh_config_lines(lines):
key, val = line.split(None, 1)
except ValueError:
try:
- key, val = line.split('=', 1)
+ key, val = line.split("=", 1)
except ValueError:
LOG.debug(
- "sshd_config: option \"%s\" has no key/value pair,"
- " skipping it", line)
+ 'sshd_config: option "%s" has no key/value pair,'
+ " skipping it",
+ line,
+ )
continue
ret.append(SshdConfigLine(line, key, val))
return ret
@@ -516,9 +553,10 @@ def update_ssh_config(updates, fname=DEF_SSHD_CFG):
changed = update_ssh_config_lines(lines=lines, updates=updates)
if changed:
util.write_file(
- fname, "\n".join(
- [str(line) for line in lines]
- ) + "\n", preserve_mode=True)
+ fname,
+ "\n".join([str(line) for line in lines]) + "\n",
+ preserve_mode=True,
+ )
return len(changed) != 0
@@ -542,12 +580,18 @@ def update_ssh_config_lines(lines, updates):
value = updates[key]
found.add(key)
if line.value == value:
- LOG.debug("line %d: option %s already set to %s",
- i, key, value)
+ LOG.debug(
+ "line %d: option %s already set to %s", i, key, value
+ )
else:
changed.append(key)
- LOG.debug("line %d: option %s updated %s -> %s", i,
- key, line.value, value)
+ LOG.debug(
+ "line %d: option %s updated %s -> %s",
+ i,
+ key,
+ line.value,
+ value,
+ )
line.value = value
if len(found) != len(updates):
@@ -555,9 +599,11 @@ def update_ssh_config_lines(lines, updates):
if key in found:
continue
changed.append(key)
- lines.append(SshdConfigLine('', key, value))
- LOG.debug("line %d: option %s added with %s",
- len(lines), key, value)
+ lines.append(SshdConfigLine("", key, value))
+ LOG.debug(
+ "line %d: option %s added with %s", len(lines), key, value
+ )
return changed
+
# vi: ts=4 expandtab