|
|
c36ff1 |
From 56a18921655fa829b7b76d6d515a45dd7c733620 Mon Sep 17 00:00:00 2001
|
|
|
c36ff1 |
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
|
|
c36ff1 |
Date: Tue, 10 Aug 2021 00:05:25 +0200
|
|
|
c36ff1 |
Subject: [PATCH 1/2] Stop copying ssh system keys and check folder permissions
|
|
|
c36ff1 |
(#956)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
RH-Author: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
|
|
c36ff1 |
RH-MergeRequest: 7: Stop copying ssh system keys and check folder permissions (#956)
|
|
|
c36ff1 |
RH-Commit: [1/1] e475216a77e3ae6ba9b699fb3ea50bb9c0a88dae (eesposit/cloud-init-centos-)
|
|
|
c36ff1 |
RH-Bugzilla: 1979099
|
|
|
c36ff1 |
RH-Acked-by: Mohamed Gamal Morsy <mmorsy@redhat.com>
|
|
|
c36ff1 |
RH-Acked-by: Eduardo Otubo <otubo@redhat.com>
|
|
|
c36ff1 |
|
|
|
c36ff1 |
This is a continuation of previous MR 25 and upstream PR #937.
|
|
|
c36ff1 |
There were still issues when using non-standard file paths like
|
|
|
c36ff1 |
/etc/ssh/userkeys/%u or /etc/ssh/authorized_keys, and the choice
|
|
|
c36ff1 |
of storing the keys of all authorized_keys files into a single
|
|
|
c36ff1 |
one was not ideal. This fix modifies cloudinit to support
|
|
|
c36ff1 |
all different cases of authorized_keys file locations, and
|
|
|
c36ff1 |
picks a user-specific file where to copy the new keys that
|
|
|
c36ff1 |
complies with ssh permissions.
|
|
|
c36ff1 |
|
|
|
c36ff1 |
commit 00dbaf1e9ab0e59d81662f0f3561897bef499a3f
|
|
|
c36ff1 |
Author: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
|
|
c36ff1 |
Date: Mon Aug 9 16:49:56 2021 +0200
|
|
|
c36ff1 |
|
|
|
c36ff1 |
Stop copying ssh system keys and check folder permissions (#956)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
In /etc/ssh/sshd_config, it is possible to define a custom
|
|
|
c36ff1 |
authorized_keys file that will contain the keys allowed to access the
|
|
|
c36ff1 |
machine via the AuthorizedKeysFile option. Cloudinit is able to add
|
|
|
c36ff1 |
user-specific keys to the existing ones, but we need to be careful on
|
|
|
c36ff1 |
which of the authorized_keys files listed to pick.
|
|
|
c36ff1 |
Chosing a file that is shared by all user will cause security
|
|
|
c36ff1 |
issues, because the owner of that key can then access also other users.
|
|
|
c36ff1 |
|
|
|
c36ff1 |
We therefore pick an authorized_keys file only if it satisfies the
|
|
|
c36ff1 |
following conditions:
|
|
|
c36ff1 |
1. it is not a "global" file, ie it must be defined in
|
|
|
c36ff1 |
AuthorizedKeysFile with %u, %h or be in /home/<user>. This avoids
|
|
|
c36ff1 |
security issues.
|
|
|
c36ff1 |
2. it must comply with ssh permission requirements, otherwise the ssh
|
|
|
c36ff1 |
agent won't use that file.
|
|
|
c36ff1 |
|
|
|
c36ff1 |
If it doesn't meet either of those conditions, write to
|
|
|
c36ff1 |
~/.ssh/authorized_keys
|
|
|
c36ff1 |
|
|
|
c36ff1 |
We also need to consider the case when the chosen authorized_keys file
|
|
|
c36ff1 |
does not exist. In this case, the existing behavior of cloud-init is
|
|
|
c36ff1 |
to create the new file. We therefore need to be sure that the file
|
|
|
c36ff1 |
complies with ssh permissions too, by setting:
|
|
|
c36ff1 |
- the actual file to permission 600, and owned by the user
|
|
|
c36ff1 |
- the directories in the path that do not exist must be root owned and
|
|
|
c36ff1 |
with permission 755.
|
|
|
c36ff1 |
|
|
|
c36ff1 |
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
|
|
c36ff1 |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
c36ff1 |
---
|
|
|
c36ff1 |
cloudinit/ssh_util.py | 133 ++++-
|
|
|
c36ff1 |
cloudinit/util.py | 51 +-
|
|
|
c36ff1 |
tests/unittests/test_sshutil.py | 952 +++++++++++++++++++++++++-------
|
|
|
c36ff1 |
3 files changed, 920 insertions(+), 216 deletions(-)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
diff --git a/cloudinit/ssh_util.py b/cloudinit/ssh_util.py
|
|
|
c36ff1 |
index 89057262..b8a3c8f7 100644
|
|
|
c36ff1 |
--- a/cloudinit/ssh_util.py
|
|
|
c36ff1 |
+++ b/cloudinit/ssh_util.py
|
|
|
c36ff1 |
@@ -249,6 +249,113 @@ def render_authorizedkeysfile_paths(value, homedir, username):
|
|
|
c36ff1 |
return rendered
|
|
|
c36ff1 |
|
|
|
c36ff1 |
|
|
|
c36ff1 |
+# Inspired from safe_path() in openssh source code (misc.c).
|
|
|
c36ff1 |
+def check_permissions(username, current_path, full_path, is_file, strictmodes):
|
|
|
c36ff1 |
+ """Check if the file/folder in @current_path has the right permissions.
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ We need to check that:
|
|
|
c36ff1 |
+ 1. If StrictMode is enabled, the owner is either root or the user
|
|
|
c36ff1 |
+ 2. the user can access the file/folder, otherwise ssh won't use it
|
|
|
c36ff1 |
+ 3. If StrictMode is enabled, no write permission is given to group
|
|
|
c36ff1 |
+ and world users (022)
|
|
|
c36ff1 |
+ """
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # group/world can only execute the folder (access)
|
|
|
c36ff1 |
+ minimal_permissions = 0o711
|
|
|
c36ff1 |
+ if is_file:
|
|
|
c36ff1 |
+ # group/world can only read the file
|
|
|
c36ff1 |
+ minimal_permissions = 0o644
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # 1. owner must be either root or the user itself
|
|
|
c36ff1 |
+ owner = util.get_owner(current_path)
|
|
|
c36ff1 |
+ if strictmodes and owner != username and owner != "root":
|
|
|
c36ff1 |
+ LOG.debug("Path %s in %s must be own by user %s or"
|
|
|
c36ff1 |
+ " by root, but instead is own by %s. Ignoring key.",
|
|
|
c36ff1 |
+ current_path, full_path, username, owner)
|
|
|
c36ff1 |
+ return False
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ parent_permission = util.get_permissions(current_path)
|
|
|
c36ff1 |
+ # 2. the user can access the file/folder, otherwise ssh won't use it
|
|
|
c36ff1 |
+ if owner == username:
|
|
|
c36ff1 |
+ # need only the owner permissions
|
|
|
c36ff1 |
+ minimal_permissions &= 0o700
|
|
|
c36ff1 |
+ else:
|
|
|
c36ff1 |
+ group_owner = util.get_group(current_path)
|
|
|
c36ff1 |
+ user_groups = util.get_user_groups(username)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ if group_owner in user_groups:
|
|
|
c36ff1 |
+ # need only the group permissions
|
|
|
c36ff1 |
+ minimal_permissions &= 0o070
|
|
|
c36ff1 |
+ else:
|
|
|
c36ff1 |
+ # need only the world permissions
|
|
|
c36ff1 |
+ minimal_permissions &= 0o007
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ if parent_permission & minimal_permissions == 0:
|
|
|
c36ff1 |
+ LOG.debug("Path %s in %s must be accessible by user %s,"
|
|
|
c36ff1 |
+ " check its permissions",
|
|
|
c36ff1 |
+ current_path, full_path, username)
|
|
|
c36ff1 |
+ return False
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # 3. no write permission (w) is given to group and world users (022)
|
|
|
c36ff1 |
+ # Group and world user can still have +rx.
|
|
|
c36ff1 |
+ if strictmodes and parent_permission & 0o022 != 0:
|
|
|
c36ff1 |
+ LOG.debug("Path %s in %s must not give write"
|
|
|
c36ff1 |
+ "permission to group or world users. Ignoring key.",
|
|
|
c36ff1 |
+ current_path, full_path)
|
|
|
c36ff1 |
+ return False
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ return True
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+def check_create_path(username, filename, strictmodes):
|
|
|
c36ff1 |
+ user_pwent = users_ssh_info(username)[1]
|
|
|
c36ff1 |
+ root_pwent = users_ssh_info("root")[1]
|
|
|
c36ff1 |
+ try:
|
|
|
c36ff1 |
+ # check the directories first
|
|
|
c36ff1 |
+ directories = filename.split("/")[1:-1]
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # scan in order, from root to file name
|
|
|
c36ff1 |
+ parent_folder = ""
|
|
|
c36ff1 |
+ # this is to comply also with unit tests, and
|
|
|
c36ff1 |
+ # strange home directories
|
|
|
c36ff1 |
+ home_folder = os.path.dirname(user_pwent.pw_dir)
|
|
|
c36ff1 |
+ for directory in directories:
|
|
|
c36ff1 |
+ parent_folder += "/" + directory
|
|
|
c36ff1 |
+ if home_folder.startswith(parent_folder):
|
|
|
c36ff1 |
+ continue
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ if not os.path.isdir(parent_folder):
|
|
|
c36ff1 |
+ # directory does not exist, and permission so far are good:
|
|
|
c36ff1 |
+ # create the directory, and make it accessible by everyone
|
|
|
c36ff1 |
+ # but owned by root, as it might be used by many users.
|
|
|
c36ff1 |
+ with util.SeLinuxGuard(parent_folder):
|
|
|
c36ff1 |
+ os.makedirs(parent_folder, mode=0o755, exist_ok=True)
|
|
|
c36ff1 |
+ util.chownbyid(parent_folder, root_pwent.pw_uid,
|
|
|
c36ff1 |
+ root_pwent.pw_gid)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ permissions = check_permissions(username, parent_folder,
|
|
|
c36ff1 |
+ filename, False, strictmodes)
|
|
|
c36ff1 |
+ if not permissions:
|
|
|
c36ff1 |
+ return False
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # check the file
|
|
|
c36ff1 |
+ if not os.path.exists(filename):
|
|
|
c36ff1 |
+ # if file does not exist: we need to create it, since the
|
|
|
c36ff1 |
+ # folders at this point exist and have right permissions
|
|
|
c36ff1 |
+ util.write_file(filename, '', mode=0o600, ensure_dir_exists=True)
|
|
|
c36ff1 |
+ util.chownbyid(filename, user_pwent.pw_uid, user_pwent.pw_gid)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ permissions = check_permissions(username, filename,
|
|
|
c36ff1 |
+ filename, True, strictmodes)
|
|
|
c36ff1 |
+ if not permissions:
|
|
|
c36ff1 |
+ return False
|
|
|
c36ff1 |
+ except (IOError, OSError) as e:
|
|
|
c36ff1 |
+ util.logexc(LOG, str(e))
|
|
|
c36ff1 |
+ return False
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ return True
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
def extract_authorized_keys(username, sshd_cfg_file=DEF_SSHD_CFG):
|
|
|
c36ff1 |
(ssh_dir, pw_ent) = users_ssh_info(username)
|
|
|
c36ff1 |
default_authorizedkeys_file = os.path.join(ssh_dir, 'authorized_keys')
|
|
|
c36ff1 |
@@ -259,6 +366,7 @@ def extract_authorized_keys(username, sshd_cfg_file=DEF_SSHD_CFG):
|
|
|
c36ff1 |
ssh_cfg = parse_ssh_config_map(sshd_cfg_file)
|
|
|
c36ff1 |
key_paths = ssh_cfg.get("authorizedkeysfile",
|
|
|
c36ff1 |
"%h/.ssh/authorized_keys")
|
|
|
c36ff1 |
+ strictmodes = ssh_cfg.get("strictmodes", "yes")
|
|
|
c36ff1 |
auth_key_fns = render_authorizedkeysfile_paths(
|
|
|
c36ff1 |
key_paths, pw_ent.pw_dir, username)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
@@ -269,31 +377,31 @@ def extract_authorized_keys(username, sshd_cfg_file=DEF_SSHD_CFG):
|
|
|
c36ff1 |
"config from %r, using 'AuthorizedKeysFile' file "
|
|
|
c36ff1 |
"%r instead", DEF_SSHD_CFG, auth_key_fns[0])
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # check if one of the keys is the user's one
|
|
|
c36ff1 |
+ # check if one of the keys is the user's one and has the right permissions
|
|
|
c36ff1 |
for key_path, auth_key_fn in zip(key_paths.split(), auth_key_fns):
|
|
|
c36ff1 |
if any([
|
|
|
c36ff1 |
'%u' in key_path,
|
|
|
c36ff1 |
'%h' in key_path,
|
|
|
c36ff1 |
auth_key_fn.startswith('{}/'.format(pw_ent.pw_dir))
|
|
|
c36ff1 |
]):
|
|
|
c36ff1 |
- user_authorizedkeys_file = auth_key_fn
|
|
|
c36ff1 |
+ permissions_ok = check_create_path(username, auth_key_fn,
|
|
|
c36ff1 |
+ strictmodes == "yes")
|
|
|
c36ff1 |
+ if permissions_ok:
|
|
|
c36ff1 |
+ user_authorizedkeys_file = auth_key_fn
|
|
|
c36ff1 |
+ break
|
|
|
c36ff1 |
|
|
|
c36ff1 |
if user_authorizedkeys_file != default_authorizedkeys_file:
|
|
|
c36ff1 |
LOG.debug(
|
|
|
c36ff1 |
"AuthorizedKeysFile has an user-specific authorized_keys, "
|
|
|
c36ff1 |
"using %s", user_authorizedkeys_file)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # always store all the keys in the user's private file
|
|
|
c36ff1 |
- return (user_authorizedkeys_file, parse_authorized_keys(auth_key_fns))
|
|
|
c36ff1 |
+ return (
|
|
|
c36ff1 |
+ user_authorizedkeys_file,
|
|
|
c36ff1 |
+ parse_authorized_keys([user_authorizedkeys_file])
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
|
|
|
c36ff1 |
def setup_user_keys(keys, username, options=None):
|
|
|
c36ff1 |
- # Make sure the users .ssh dir is setup accordingly
|
|
|
c36ff1 |
- (ssh_dir, pwent) = users_ssh_info(username)
|
|
|
c36ff1 |
- if not os.path.isdir(ssh_dir):
|
|
|
c36ff1 |
- util.ensure_dir(ssh_dir, mode=0o700)
|
|
|
c36ff1 |
- util.chownbyid(ssh_dir, pwent.pw_uid, pwent.pw_gid)
|
|
|
c36ff1 |
-
|
|
|
c36ff1 |
# Turn the 'update' keys given into actual entries
|
|
|
c36ff1 |
parser = AuthKeyLineParser()
|
|
|
c36ff1 |
key_entries = []
|
|
|
c36ff1 |
@@ -302,11 +410,10 @@ def setup_user_keys(keys, username, options=None):
|
|
|
c36ff1 |
|
|
|
c36ff1 |
# Extract the old and make the new
|
|
|
c36ff1 |
(auth_key_fn, auth_key_entries) = extract_authorized_keys(username)
|
|
|
c36ff1 |
+ ssh_dir = os.path.dirname(auth_key_fn)
|
|
|
c36ff1 |
with util.SeLinuxGuard(ssh_dir, recursive=True):
|
|
|
c36ff1 |
content = update_authorized_keys(auth_key_entries, key_entries)
|
|
|
c36ff1 |
- util.ensure_dir(os.path.dirname(auth_key_fn), mode=0o700)
|
|
|
c36ff1 |
- util.write_file(auth_key_fn, content, mode=0o600)
|
|
|
c36ff1 |
- util.chownbyid(auth_key_fn, pwent.pw_uid, pwent.pw_gid)
|
|
|
c36ff1 |
+ util.write_file(auth_key_fn, content, preserve_mode=True)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
|
|
|
c36ff1 |
class SshdConfigLine(object):
|
|
|
c36ff1 |
diff --git a/cloudinit/util.py b/cloudinit/util.py
|
|
|
c36ff1 |
index 4e0a72db..343976ad 100644
|
|
|
c36ff1 |
--- a/cloudinit/util.py
|
|
|
c36ff1 |
+++ b/cloudinit/util.py
|
|
|
c36ff1 |
@@ -35,6 +35,7 @@ from base64 import b64decode, b64encode
|
|
|
c36ff1 |
from errno import ENOENT
|
|
|
c36ff1 |
from functools import lru_cache
|
|
|
c36ff1 |
from urllib import parse
|
|
|
c36ff1 |
+from typing import List
|
|
|
c36ff1 |
|
|
|
c36ff1 |
from cloudinit import importer
|
|
|
c36ff1 |
from cloudinit import log as logging
|
|
|
c36ff1 |
@@ -1830,6 +1831,53 @@ def chmod(path, mode):
|
|
|
c36ff1 |
os.chmod(path, real_mode)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
|
|
|
c36ff1 |
+def get_permissions(path: str) -> int:
|
|
|
c36ff1 |
+ """
|
|
|
c36ff1 |
+ Returns the octal permissions of the file/folder pointed by the path,
|
|
|
c36ff1 |
+ encoded as an int.
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @param path: The full path of the file/folder.
|
|
|
c36ff1 |
+ """
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ return stat.S_IMODE(os.stat(path).st_mode)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+def get_owner(path: str) -> str:
|
|
|
c36ff1 |
+ """
|
|
|
c36ff1 |
+ Returns the owner of the file/folder pointed by the path.
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @param path: The full path of the file/folder.
|
|
|
c36ff1 |
+ """
|
|
|
c36ff1 |
+ st = os.stat(path)
|
|
|
c36ff1 |
+ return pwd.getpwuid(st.st_uid).pw_name
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+def get_group(path: str) -> str:
|
|
|
c36ff1 |
+ """
|
|
|
c36ff1 |
+ Returns the group of the file/folder pointed by the path.
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @param path: The full path of the file/folder.
|
|
|
c36ff1 |
+ """
|
|
|
c36ff1 |
+ st = os.stat(path)
|
|
|
c36ff1 |
+ return grp.getgrgid(st.st_gid).gr_name
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+def get_user_groups(username: str) -> List[str]:
|
|
|
c36ff1 |
+ """
|
|
|
c36ff1 |
+ Returns a list of all groups to which the user belongs
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @param username: the user we want to check
|
|
|
c36ff1 |
+ """
|
|
|
c36ff1 |
+ groups = []
|
|
|
c36ff1 |
+ for group in grp.getgrall():
|
|
|
c36ff1 |
+ if username in group.gr_mem:
|
|
|
c36ff1 |
+ groups.append(group.gr_name)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ gid = pwd.getpwnam(username).pw_gid
|
|
|
c36ff1 |
+ groups.append(grp.getgrgid(gid).gr_name)
|
|
|
c36ff1 |
+ return groups
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
def write_file(
|
|
|
c36ff1 |
filename,
|
|
|
c36ff1 |
content,
|
|
|
c36ff1 |
@@ -1856,8 +1904,7 @@ def write_file(
|
|
|
c36ff1 |
|
|
|
c36ff1 |
if preserve_mode:
|
|
|
c36ff1 |
try:
|
|
|
c36ff1 |
- file_stat = os.stat(filename)
|
|
|
c36ff1 |
- mode = stat.S_IMODE(file_stat.st_mode)
|
|
|
c36ff1 |
+ mode = get_permissions(filename)
|
|
|
c36ff1 |
except OSError:
|
|
|
c36ff1 |
pass
|
|
|
c36ff1 |
|
|
|
c36ff1 |
diff --git a/tests/unittests/test_sshutil.py b/tests/unittests/test_sshutil.py
|
|
|
c36ff1 |
index bcb8044f..a66788bf 100644
|
|
|
c36ff1 |
--- a/tests/unittests/test_sshutil.py
|
|
|
c36ff1 |
+++ b/tests/unittests/test_sshutil.py
|
|
|
c36ff1 |
@@ -1,6 +1,9 @@
|
|
|
c36ff1 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
|
c36ff1 |
|
|
|
c36ff1 |
+import os
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
from collections import namedtuple
|
|
|
c36ff1 |
+from functools import partial
|
|
|
c36ff1 |
from unittest.mock import patch
|
|
|
c36ff1 |
|
|
|
c36ff1 |
from cloudinit import ssh_util
|
|
|
c36ff1 |
@@ -8,13 +11,48 @@ from cloudinit.tests import helpers as test_helpers
|
|
|
c36ff1 |
from cloudinit import util
|
|
|
c36ff1 |
|
|
|
c36ff1 |
# https://stackoverflow.com/questions/11351032/
|
|
|
c36ff1 |
-FakePwEnt = namedtuple(
|
|
|
c36ff1 |
- 'FakePwEnt',
|
|
|
c36ff1 |
- ['pw_dir', 'pw_gecos', 'pw_name', 'pw_passwd', 'pw_shell', 'pwd_uid'])
|
|
|
c36ff1 |
+FakePwEnt = namedtuple('FakePwEnt', [
|
|
|
c36ff1 |
+ 'pw_name',
|
|
|
c36ff1 |
+ 'pw_passwd',
|
|
|
c36ff1 |
+ 'pw_uid',
|
|
|
c36ff1 |
+ 'pw_gid',
|
|
|
c36ff1 |
+ 'pw_gecos',
|
|
|
c36ff1 |
+ 'pw_dir',
|
|
|
c36ff1 |
+ 'pw_shell',
|
|
|
c36ff1 |
+])
|
|
|
c36ff1 |
FakePwEnt.__new__.__defaults__ = tuple(
|
|
|
c36ff1 |
"UNSET_%s" % n for n in FakePwEnt._fields)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
|
|
|
c36ff1 |
+def mock_get_owner(updated_permissions, value):
|
|
|
c36ff1 |
+ try:
|
|
|
c36ff1 |
+ return updated_permissions[value][0]
|
|
|
c36ff1 |
+ except ValueError:
|
|
|
c36ff1 |
+ return util.get_owner(value)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+def mock_get_group(updated_permissions, value):
|
|
|
c36ff1 |
+ try:
|
|
|
c36ff1 |
+ return updated_permissions[value][1]
|
|
|
c36ff1 |
+ except ValueError:
|
|
|
c36ff1 |
+ return util.get_group(value)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+def mock_get_user_groups(username):
|
|
|
c36ff1 |
+ return username
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+def mock_get_permissions(updated_permissions, value):
|
|
|
c36ff1 |
+ try:
|
|
|
c36ff1 |
+ return updated_permissions[value][2]
|
|
|
c36ff1 |
+ except ValueError:
|
|
|
c36ff1 |
+ return util.get_permissions(value)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+def mock_getpwnam(users, username):
|
|
|
c36ff1 |
+ return users[username]
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
# Do not use these public keys, most of them are fetched from
|
|
|
c36ff1 |
# the testdata for OpenSSH, and their private keys are available
|
|
|
c36ff1 |
# https://github.com/openssh/openssh-portable/tree/master/regress/unittests/sshkey/testdata
|
|
|
c36ff1 |
@@ -552,12 +590,30 @@ class TestBasicAuthorizedKeyParse(test_helpers.CiTestCase):
|
|
|
c36ff1 |
ssh_util.render_authorizedkeysfile_paths(
|
|
|
c36ff1 |
"/opt/%u/keys", "/home/bobby", "bobby"))
|
|
|
c36ff1 |
|
|
|
c36ff1 |
+ def test_user_file(self):
|
|
|
c36ff1 |
+ self.assertEqual(
|
|
|
c36ff1 |
+ ["/opt/bobby"],
|
|
|
c36ff1 |
+ ssh_util.render_authorizedkeysfile_paths(
|
|
|
c36ff1 |
+ "/opt/%u", "/home/bobby", "bobby"))
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ def test_user_file2(self):
|
|
|
c36ff1 |
+ self.assertEqual(
|
|
|
c36ff1 |
+ ["/opt/bobby/bobby"],
|
|
|
c36ff1 |
+ ssh_util.render_authorizedkeysfile_paths(
|
|
|
c36ff1 |
+ "/opt/%u/%u", "/home/bobby", "bobby"))
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
def test_multiple(self):
|
|
|
c36ff1 |
self.assertEqual(
|
|
|
c36ff1 |
["/keys/path1", "/keys/path2"],
|
|
|
c36ff1 |
ssh_util.render_authorizedkeysfile_paths(
|
|
|
c36ff1 |
"/keys/path1 /keys/path2", "/home/bobby", "bobby"))
|
|
|
c36ff1 |
|
|
|
c36ff1 |
+ def test_multiple2(self):
|
|
|
c36ff1 |
+ self.assertEqual(
|
|
|
c36ff1 |
+ ["/keys/path1", "/keys/bobby"],
|
|
|
c36ff1 |
+ ssh_util.render_authorizedkeysfile_paths(
|
|
|
c36ff1 |
+ "/keys/path1 /keys/%u", "/home/bobby", "bobby"))
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
def test_relative(self):
|
|
|
c36ff1 |
self.assertEqual(
|
|
|
c36ff1 |
["/home/bobby/.secret/keys"],
|
|
|
c36ff1 |
@@ -581,269 +637,763 @@ class TestBasicAuthorizedKeyParse(test_helpers.CiTestCase):
|
|
|
c36ff1 |
|
|
|
c36ff1 |
class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
- def test_multiple_authorizedkeys_file_order1(self, m_getpwnam):
|
|
|
c36ff1 |
- fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
|
|
c36ff1 |
- m_getpwnam.return_value = fpw
|
|
|
c36ff1 |
- user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
|
|
c36ff1 |
-
|
|
|
c36ff1 |
- # /tmp/home2/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
- authorized_keys = self.tmp_path('authorized_keys', dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
|
|
c36ff1 |
-
|
|
|
c36ff1 |
- # /tmp/home2/bobby/.ssh/user_keys = dsa
|
|
|
c36ff1 |
- user_keys = self.tmp_path('user_keys', dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(user_keys, VALID_CONTENT['dsa'])
|
|
|
c36ff1 |
-
|
|
|
c36ff1 |
- # /tmp/sshd_config
|
|
|
c36ff1 |
+ def create_fake_users(self, names, mock_permissions,
|
|
|
c36ff1 |
+ m_get_group, m_get_owner, m_get_permissions,
|
|
|
c36ff1 |
+ m_getpwnam, users):
|
|
|
c36ff1 |
+ homes = []
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ root = '/tmp/root'
|
|
|
c36ff1 |
+ fpw = FakePwEnt(pw_name="root", pw_dir=root)
|
|
|
c36ff1 |
+ users["root"] = fpw
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ for name in names:
|
|
|
c36ff1 |
+ home = '/tmp/home/' + name
|
|
|
c36ff1 |
+ fpw = FakePwEnt(pw_name=name, pw_dir=home)
|
|
|
c36ff1 |
+ users[name] = fpw
|
|
|
c36ff1 |
+ homes.append(home)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ m_get_permissions.side_effect = partial(
|
|
|
c36ff1 |
+ mock_get_permissions, mock_permissions)
|
|
|
c36ff1 |
+ m_get_owner.side_effect = partial(mock_get_owner, mock_permissions)
|
|
|
c36ff1 |
+ m_get_group.side_effect = partial(mock_get_group, mock_permissions)
|
|
|
c36ff1 |
+ m_getpwnam.side_effect = partial(mock_getpwnam, users)
|
|
|
c36ff1 |
+ return homes
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ def create_user_authorized_file(self, home, filename, content_key, keys):
|
|
|
c36ff1 |
+ user_ssh_folder = "%s/.ssh" % home
|
|
|
c36ff1 |
+ # /tmp/home/<user>/.ssh/authorized_keys = content_key
|
|
|
c36ff1 |
+ authorized_keys = self.tmp_path(filename, dir=user_ssh_folder)
|
|
|
c36ff1 |
+ util.write_file(authorized_keys, VALID_CONTENT[content_key])
|
|
|
c36ff1 |
+ keys[authorized_keys] = content_key
|
|
|
c36ff1 |
+ return authorized_keys
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ def create_global_authorized_file(self, filename, content_key, keys):
|
|
|
c36ff1 |
+ authorized_keys = self.tmp_path(filename, dir='/tmp')
|
|
|
c36ff1 |
+ util.write_file(authorized_keys, VALID_CONTENT[content_key])
|
|
|
c36ff1 |
+ keys[authorized_keys] = content_key
|
|
|
c36ff1 |
+ return authorized_keys
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ def create_sshd_config(self, authorized_keys_files):
|
|
|
c36ff1 |
sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
|
|
c36ff1 |
util.write_file(
|
|
|
c36ff1 |
sshd_config,
|
|
|
c36ff1 |
- "AuthorizedKeysFile %s %s" % (authorized_keys, user_keys)
|
|
|
c36ff1 |
+ "AuthorizedKeysFile " + authorized_keys_files
|
|
|
c36ff1 |
)
|
|
|
c36ff1 |
+ return sshd_config
|
|
|
c36ff1 |
|
|
|
c36ff1 |
+ def execute_and_check(self, user, sshd_config, solution, keys,
|
|
|
c36ff1 |
+ delete_keys=True):
|
|
|
c36ff1 |
(auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
|
|
c36ff1 |
- fpw.pw_name, sshd_config)
|
|
|
c36ff1 |
+ user, sshd_config)
|
|
|
c36ff1 |
content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- self.assertEqual(user_keys, auth_key_fn)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['rsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['dsa'] in content)
|
|
|
c36ff1 |
+ self.assertEqual(auth_key_fn, solution)
|
|
|
c36ff1 |
+ for path, key in keys.items():
|
|
|
c36ff1 |
+ if path == solution:
|
|
|
c36ff1 |
+ self.assertTrue(VALID_CONTENT[key] in content)
|
|
|
c36ff1 |
+ else:
|
|
|
c36ff1 |
+ self.assertFalse(VALID_CONTENT[key] in content)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ if delete_keys and os.path.isdir("/tmp/home/"):
|
|
|
c36ff1 |
+ util.delete_dir_contents("/tmp/home/")
|
|
|
c36ff1 |
|
|
|
c36ff1 |
@patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
- def test_multiple_authorizedkeys_file_order2(self, m_getpwnam):
|
|
|
c36ff1 |
- fpw = FakePwEnt(pw_name='suzie', pw_dir='/tmp/home/suzie')
|
|
|
c36ff1 |
- m_getpwnam.return_value = fpw
|
|
|
c36ff1 |
- user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_single_user_two_local_files(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/user_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby], mock_permissions, m_get_group, m_get_owner,
|
|
|
c36ff1 |
+ m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ home = homes[0]
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/home/suzie/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
- authorized_keys = self.tmp_path('authorized_keys', dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home, 'authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/home/suzie/.ssh/user_keys = dsa
|
|
|
c36ff1 |
- user_keys = self.tmp_path('user_keys', dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(user_keys, VALID_CONTENT['dsa'])
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/user_keys = dsa
|
|
|
c36ff1 |
+ user_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home, 'user_keys', 'dsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
# /tmp/sshd_config
|
|
|
c36ff1 |
- sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(
|
|
|
c36ff1 |
- sshd_config,
|
|
|
c36ff1 |
- "AuthorizedKeysFile %s %s" % (user_keys, authorized_keys)
|
|
|
c36ff1 |
+ options = "%s %s" % (authorized_keys, user_keys)
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ self.execute_and_check(user_bobby, sshd_config, authorized_keys, keys)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_single_user_two_local_files_inverted(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/user_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby], mock_permissions, m_get_group, m_get_owner,
|
|
|
c36ff1 |
+ m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
)
|
|
|
c36ff1 |
+ home = homes[0]
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
|
|
c36ff1 |
- fpw.pw_name, sshd_config)
|
|
|
c36ff1 |
- content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home, 'authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- self.assertEqual(authorized_keys, auth_key_fn)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['rsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['dsa'] in content)
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/user_keys = dsa
|
|
|
c36ff1 |
+ user_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home, 'user_keys', 'dsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
- def test_multiple_authorizedkeys_file_local_global(self, m_getpwnam):
|
|
|
c36ff1 |
- fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
|
|
c36ff1 |
- m_getpwnam.return_value = fpw
|
|
|
c36ff1 |
- user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
|
|
c36ff1 |
+ # /tmp/sshd_config
|
|
|
c36ff1 |
+ options = "%s %s" % (user_keys, authorized_keys)
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/home2/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
- authorized_keys = self.tmp_path('authorized_keys', dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
|
|
c36ff1 |
+ self.execute_and_check(user_bobby, sshd_config, user_keys, keys)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/home2/bobby/.ssh/user_keys = dsa
|
|
|
c36ff1 |
- user_keys = self.tmp_path('user_keys', dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(user_keys, VALID_CONTENT['dsa'])
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_single_user_local_global_files(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/user_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby], mock_permissions, m_get_group, m_get_owner,
|
|
|
c36ff1 |
+ m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ home = homes[0]
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/etc/ssh/authorized_keys = ecdsa
|
|
|
c36ff1 |
- authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys',
|
|
|
c36ff1 |
- dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(authorized_keys_global, VALID_CONTENT['ecdsa'])
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home, 'authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/sshd_config
|
|
|
c36ff1 |
- sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(
|
|
|
c36ff1 |
- sshd_config,
|
|
|
c36ff1 |
- "AuthorizedKeysFile %s %s %s" % (authorized_keys_global,
|
|
|
c36ff1 |
- user_keys, authorized_keys)
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/user_keys = dsa
|
|
|
c36ff1 |
+ user_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home, 'user_keys', 'dsa', keys
|
|
|
c36ff1 |
)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
|
|
c36ff1 |
- fpw.pw_name, sshd_config)
|
|
|
c36ff1 |
- content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
|
|
c36ff1 |
+ authorized_keys_global = self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/authorized_keys', 'ecdsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- self.assertEqual(authorized_keys, auth_key_fn)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['rsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['dsa'] in content)
|
|
|
c36ff1 |
+ options = "%s %s %s" % (authorized_keys_global, user_keys,
|
|
|
c36ff1 |
+ authorized_keys)
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
- def test_multiple_authorizedkeys_file_local_global2(self, m_getpwnam):
|
|
|
c36ff1 |
- fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
|
|
c36ff1 |
- m_getpwnam.return_value = fpw
|
|
|
c36ff1 |
- user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
|
|
c36ff1 |
+ self.execute_and_check(user_bobby, sshd_config, user_keys, keys)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/home2/bobby/.ssh/authorized_keys2 = rsa
|
|
|
c36ff1 |
- authorized_keys = self.tmp_path('authorized_keys2',
|
|
|
c36ff1 |
- dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_single_user_local_global_files_inverted(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/user_keys3': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys2': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby], mock_permissions, m_get_group, m_get_owner,
|
|
|
c36ff1 |
+ m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ home = homes[0]
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/home2/bobby/.ssh/user_keys3 = dsa
|
|
|
c36ff1 |
- user_keys = self.tmp_path('user_keys3', dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(user_keys, VALID_CONTENT['dsa'])
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home, 'authorized_keys2', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/etc/ssh/authorized_keys = ecdsa
|
|
|
c36ff1 |
- authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys',
|
|
|
c36ff1 |
- dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(authorized_keys_global, VALID_CONTENT['ecdsa'])
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/user_keys = dsa
|
|
|
c36ff1 |
+ user_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home, 'user_keys3', 'dsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/sshd_config
|
|
|
c36ff1 |
- sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(
|
|
|
c36ff1 |
- sshd_config,
|
|
|
c36ff1 |
- "AuthorizedKeysFile %s %s %s" % (authorized_keys_global,
|
|
|
c36ff1 |
- authorized_keys, user_keys)
|
|
|
c36ff1 |
+ authorized_keys_global = self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/authorized_keys', 'ecdsa', keys
|
|
|
c36ff1 |
)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
|
|
c36ff1 |
- fpw.pw_name, sshd_config)
|
|
|
c36ff1 |
- content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
|
|
c36ff1 |
+ options = "%s %s %s" % (authorized_keys_global, authorized_keys,
|
|
|
c36ff1 |
+ user_keys)
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- self.assertEqual(user_keys, auth_key_fn)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['rsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['dsa'] in content)
|
|
|
c36ff1 |
+ self.execute_and_check(user_bobby, sshd_config, authorized_keys, keys)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
@patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
- def test_multiple_authorizedkeys_file_global(self, m_getpwnam):
|
|
|
c36ff1 |
- fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
|
|
c36ff1 |
- m_getpwnam.return_value = fpw
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_single_user_global_file(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby], mock_permissions, m_get_group, m_get_owner,
|
|
|
c36ff1 |
+ m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ home = homes[0]
|
|
|
c36ff1 |
|
|
|
c36ff1 |
# /tmp/etc/ssh/authorized_keys = rsa
|
|
|
c36ff1 |
- authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys',
|
|
|
c36ff1 |
- dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(authorized_keys_global, VALID_CONTENT['rsa'])
|
|
|
c36ff1 |
+ authorized_keys_global = self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/sshd_config
|
|
|
c36ff1 |
- sshd_config = self.tmp_path('sshd_config')
|
|
|
c36ff1 |
- util.write_file(
|
|
|
c36ff1 |
- sshd_config,
|
|
|
c36ff1 |
- "AuthorizedKeysFile %s" % (authorized_keys_global)
|
|
|
c36ff1 |
+ options = "%s" % authorized_keys_global
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ default = "%s/.ssh/authorized_keys" % home
|
|
|
c36ff1 |
+ self.execute_and_check(user_bobby, sshd_config, default, keys)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_two_users_local_file_standard(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh/authorized_keys': ('suzie', 'suzie', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ user_suzie = 'suzie'
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby, user_suzie], mock_permissions, m_get_group,
|
|
|
c36ff1 |
+ m_get_owner, m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
)
|
|
|
c36ff1 |
+ home_bobby = homes[0]
|
|
|
c36ff1 |
+ home_suzie = homes[1]
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
|
|
c36ff1 |
- fpw.pw_name, sshd_config)
|
|
|
c36ff1 |
- content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- self.assertEqual("%s/.ssh/authorized_keys" % fpw.pw_dir, auth_key_fn)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['rsa'] in content)
|
|
|
c36ff1 |
+ # /tmp/home/suzie/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ authorized_keys2 = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_suzie, 'authorized_keys', 'ssh-xmss@openssh.com', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ options = ".ssh/authorized_keys"
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_bobby, sshd_config, authorized_keys, keys, delete_keys=False
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ self.execute_and_check(user_suzie, sshd_config, authorized_keys2, keys)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
@patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
- def test_multiple_authorizedkeys_file_multiuser(self, m_getpwnam):
|
|
|
c36ff1 |
- fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
|
|
c36ff1 |
- m_getpwnam.return_value = fpw
|
|
|
c36ff1 |
- user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
|
|
c36ff1 |
- # /tmp/home2/bobby/.ssh/authorized_keys2 = rsa
|
|
|
c36ff1 |
- authorized_keys = self.tmp_path('authorized_keys2',
|
|
|
c36ff1 |
- dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
|
|
c36ff1 |
- # /tmp/home2/bobby/.ssh/user_keys3 = dsa
|
|
|
c36ff1 |
- user_keys = self.tmp_path('user_keys3', dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(user_keys, VALID_CONTENT['dsa'])
|
|
|
c36ff1 |
-
|
|
|
c36ff1 |
- fpw2 = FakePwEnt(pw_name='suzie', pw_dir='/tmp/home/suzie')
|
|
|
c36ff1 |
- user_ssh_folder = "%s/.ssh" % fpw2.pw_dir
|
|
|
c36ff1 |
- # /tmp/home/suzie/.ssh/authorized_keys2 = ssh-xmss@openssh.com
|
|
|
c36ff1 |
- authorized_keys2 = self.tmp_path('authorized_keys2',
|
|
|
c36ff1 |
- dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(authorized_keys2,
|
|
|
c36ff1 |
- VALID_CONTENT['ssh-xmss@openssh.com'])
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_two_users_local_file_custom(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys2': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh/authorized_keys2': ('suzie', 'suzie', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ user_suzie = 'suzie'
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby, user_suzie], mock_permissions, m_get_group,
|
|
|
c36ff1 |
+ m_get_owner, m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ home_bobby = homes[0]
|
|
|
c36ff1 |
+ home_suzie = homes[1]
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/etc/ssh/authorized_keys = ecdsa
|
|
|
c36ff1 |
- authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys2',
|
|
|
c36ff1 |
- dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(authorized_keys_global, VALID_CONTENT['ecdsa'])
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys2 = rsa
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'authorized_keys2', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # /tmp/sshd_config
|
|
|
c36ff1 |
- sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(
|
|
|
c36ff1 |
- sshd_config,
|
|
|
c36ff1 |
- "AuthorizedKeysFile %s %%h/.ssh/authorized_keys2 %s" %
|
|
|
c36ff1 |
- (authorized_keys_global, user_keys)
|
|
|
c36ff1 |
+ # /tmp/home/suzie/.ssh/authorized_keys2 = rsa
|
|
|
c36ff1 |
+ authorized_keys2 = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_suzie, 'authorized_keys2', 'ssh-xmss@openssh.com', keys
|
|
|
c36ff1 |
)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # process first user
|
|
|
c36ff1 |
- (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
|
|
c36ff1 |
- fpw.pw_name, sshd_config)
|
|
|
c36ff1 |
- content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
|
|
c36ff1 |
+ options = ".ssh/authorized_keys2"
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_bobby, sshd_config, authorized_keys, keys, delete_keys=False
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ self.execute_and_check(user_suzie, sshd_config, authorized_keys2, keys)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- self.assertEqual(user_keys, auth_key_fn)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['rsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['dsa'] in content)
|
|
|
c36ff1 |
- self.assertFalse(VALID_CONTENT['ssh-xmss@openssh.com'] in content)
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_two_users_local_global_files(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys2': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/user_keys3': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh/authorized_keys2': ('suzie', 'suzie', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh/user_keys3': ('suzie', 'suzie', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ user_suzie = 'suzie'
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby, user_suzie], mock_permissions, m_get_group,
|
|
|
c36ff1 |
+ m_get_owner, m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ home_bobby = homes[0]
|
|
|
c36ff1 |
+ home_suzie = homes[1]
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- m_getpwnam.return_value = fpw2
|
|
|
c36ff1 |
- # process second user
|
|
|
c36ff1 |
- (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
|
|
c36ff1 |
- fpw2.pw_name, sshd_config)
|
|
|
c36ff1 |
- content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys2 = rsa
|
|
|
c36ff1 |
+ self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'authorized_keys2', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/user_keys3 = dsa
|
|
|
c36ff1 |
+ user_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'user_keys3', 'dsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/home/suzie/.ssh/authorized_keys2 = rsa
|
|
|
c36ff1 |
+ authorized_keys2 = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_suzie, 'authorized_keys2', 'ssh-xmss@openssh.com', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/etc/ssh/authorized_keys = ecdsa
|
|
|
c36ff1 |
+ authorized_keys_global = self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/authorized_keys2', 'ecdsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ options = "%s %s %%h/.ssh/authorized_keys2" % \
|
|
|
c36ff1 |
+ (authorized_keys_global, user_keys)
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- self.assertEqual(authorized_keys2, auth_key_fn)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['ssh-xmss@openssh.com'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['dsa'] in content)
|
|
|
c36ff1 |
- self.assertFalse(VALID_CONTENT['rsa'] in content)
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_bobby, sshd_config, user_keys, keys, delete_keys=False
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ self.execute_and_check(user_suzie, sshd_config, authorized_keys2, keys)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_user_groups")
|
|
|
c36ff1 |
@patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
- def test_multiple_authorizedkeys_file_multiuser2(self, m_getpwnam):
|
|
|
c36ff1 |
- fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home/bobby')
|
|
|
c36ff1 |
- m_getpwnam.return_value = fpw
|
|
|
c36ff1 |
- user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_two_users_local_global_files_badguy(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
|
|
|
c36ff1 |
+ m_get_user_groups
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys2': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/user_keys3': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/home/badguy': ('root', 'root', 0o755),
|
|
|
c36ff1 |
+ '/tmp/home/badguy/home': ('root', 'root', 0o755),
|
|
|
c36ff1 |
+ '/tmp/home/badguy/home/bobby': ('root', 'root', 0o655),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ user_badguy = 'badguy'
|
|
|
c36ff1 |
+ home_bobby, *_ = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby, user_badguy], mock_permissions, m_get_group,
|
|
|
c36ff1 |
+ m_get_owner, m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ m_get_user_groups.side_effect = mock_get_user_groups
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
# /tmp/home/bobby/.ssh/authorized_keys2 = rsa
|
|
|
c36ff1 |
- authorized_keys = self.tmp_path('authorized_keys2',
|
|
|
c36ff1 |
- dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'authorized_keys2', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
# /tmp/home/bobby/.ssh/user_keys3 = dsa
|
|
|
c36ff1 |
- user_keys = self.tmp_path('user_keys3', dir=user_ssh_folder)
|
|
|
c36ff1 |
- util.write_file(user_keys, VALID_CONTENT['dsa'])
|
|
|
c36ff1 |
+ user_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'user_keys3', 'dsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- fpw2 = FakePwEnt(pw_name='badguy', pw_dir='/tmp/home/badguy')
|
|
|
c36ff1 |
- user_ssh_folder = "%s/.ssh" % fpw2.pw_dir
|
|
|
c36ff1 |
# /tmp/home/badguy/home/bobby = ""
|
|
|
c36ff1 |
authorized_keys2 = self.tmp_path('home/bobby', dir="/tmp/home/badguy")
|
|
|
c36ff1 |
+ util.write_file(authorized_keys2, '')
|
|
|
c36ff1 |
|
|
|
c36ff1 |
# /tmp/etc/ssh/authorized_keys = ecdsa
|
|
|
c36ff1 |
- authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys2',
|
|
|
c36ff1 |
- dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(authorized_keys_global, VALID_CONTENT['ecdsa'])
|
|
|
c36ff1 |
+ authorized_keys_global = self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/authorized_keys2', 'ecdsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
# /tmp/sshd_config
|
|
|
c36ff1 |
- sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
|
|
c36ff1 |
- util.write_file(
|
|
|
c36ff1 |
- sshd_config,
|
|
|
c36ff1 |
- "AuthorizedKeysFile %s %%h/.ssh/authorized_keys2 %s %s" %
|
|
|
c36ff1 |
- (authorized_keys_global, user_keys, authorized_keys2)
|
|
|
c36ff1 |
+ options = "%s %%h/.ssh/authorized_keys2 %s %s" % \
|
|
|
c36ff1 |
+ (authorized_keys2, authorized_keys_global, user_keys)
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_bobby, sshd_config, authorized_keys, keys, delete_keys=False
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_badguy, sshd_config, authorized_keys2, keys
|
|
|
c36ff1 |
)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # process first user
|
|
|
c36ff1 |
- (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
|
|
c36ff1 |
- fpw.pw_name, sshd_config)
|
|
|
c36ff1 |
- content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_user_groups")
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_two_users_unaccessible_file(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
|
|
|
c36ff1 |
+ m_get_user_groups
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ '/tmp/etc': ('root', 'root', 0o755),
|
|
|
c36ff1 |
+ '/tmp/etc/ssh': ('root', 'root', 0o755),
|
|
|
c36ff1 |
+ '/tmp/etc/ssh/userkeys': ('root', 'root', 0o700),
|
|
|
c36ff1 |
+ '/tmp/etc/ssh/userkeys/bobby': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/etc/ssh/userkeys/badguy': ('badguy', 'badguy', 0o600),
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ '/tmp/home/badguy': ('badguy', 'badguy', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/badguy/.ssh': ('badguy', 'badguy', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/badguy/.ssh/authorized_keys':
|
|
|
c36ff1 |
+ ('badguy', 'badguy', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ user_badguy = 'badguy'
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby, user_badguy], mock_permissions, m_get_group,
|
|
|
c36ff1 |
+ m_get_owner, m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ m_get_user_groups.side_effect = mock_get_user_groups
|
|
|
c36ff1 |
+ home_bobby = homes[0]
|
|
|
c36ff1 |
+ home_badguy = homes[1]
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- self.assertEqual(user_keys, auth_key_fn)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['rsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['dsa'] in content)
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ # /tmp/etc/ssh/userkeys/bobby = dsa
|
|
|
c36ff1 |
+ # assume here that we can bypass userkeys, despite permissions
|
|
|
c36ff1 |
+ self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/userkeys/bobby', 'dsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- m_getpwnam.return_value = fpw2
|
|
|
c36ff1 |
- # process second user
|
|
|
c36ff1 |
- (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
|
|
c36ff1 |
- fpw2.pw_name, sshd_config)
|
|
|
c36ff1 |
- content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
|
|
c36ff1 |
+ # /tmp/home/badguy/.ssh/authorized_keys = ssh-xmss@openssh.com
|
|
|
c36ff1 |
+ authorized_keys2 = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_badguy, 'authorized_keys', 'ssh-xmss@openssh.com', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
|
|
|
c36ff1 |
- # badguy should not take the key from the other user!
|
|
|
c36ff1 |
- self.assertEqual(authorized_keys2, auth_key_fn)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
|
|
c36ff1 |
- self.assertTrue(VALID_CONTENT['dsa'] in content)
|
|
|
c36ff1 |
- self.assertFalse(VALID_CONTENT['rsa'] in content)
|
|
|
c36ff1 |
+ # /tmp/etc/ssh/userkeys/badguy = ecdsa
|
|
|
c36ff1 |
+ self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/userkeys/badguy', 'ecdsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/sshd_config
|
|
|
c36ff1 |
+ options = "/tmp/etc/ssh/userkeys/%u .ssh/authorized_keys"
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_bobby, sshd_config, authorized_keys, keys, delete_keys=False
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_badguy, sshd_config, authorized_keys2, keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_user_groups")
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_two_users_accessible_file(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
|
|
|
c36ff1 |
+ m_get_user_groups
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ '/tmp/etc': ('root', 'root', 0o755),
|
|
|
c36ff1 |
+ '/tmp/etc/ssh': ('root', 'root', 0o755),
|
|
|
c36ff1 |
+ '/tmp/etc/ssh/userkeys': ('root', 'root', 0o755),
|
|
|
c36ff1 |
+ '/tmp/etc/ssh/userkeys/bobby': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+ '/tmp/etc/ssh/userkeys/badguy': ('badguy', 'badguy', 0o600),
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ '/tmp/home/badguy': ('badguy', 'badguy', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/badguy/.ssh': ('badguy', 'badguy', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/badguy/.ssh/authorized_keys':
|
|
|
c36ff1 |
+ ('badguy', 'badguy', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ user_badguy = 'badguy'
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby, user_badguy], mock_permissions, m_get_group,
|
|
|
c36ff1 |
+ m_get_owner, m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ m_get_user_groups.side_effect = mock_get_user_groups
|
|
|
c36ff1 |
+ home_bobby = homes[0]
|
|
|
c36ff1 |
+ home_badguy = homes[1]
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ # /tmp/etc/ssh/userkeys/bobby = dsa
|
|
|
c36ff1 |
+ # assume here that we can bypass userkeys, despite permissions
|
|
|
c36ff1 |
+ authorized_keys = self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/userkeys/bobby', 'dsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/home/badguy/.ssh/authorized_keys = ssh-xmss@openssh.com
|
|
|
c36ff1 |
+ self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_badguy, 'authorized_keys', 'ssh-xmss@openssh.com', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/etc/ssh/userkeys/badguy = ecdsa
|
|
|
c36ff1 |
+ authorized_keys2 = self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/userkeys/badguy', 'ecdsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/sshd_config
|
|
|
c36ff1 |
+ options = "/tmp/etc/ssh/userkeys/%u .ssh/authorized_keys"
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_bobby, sshd_config, authorized_keys, keys, delete_keys=False
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_badguy, sshd_config, authorized_keys2, keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_user_groups")
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_two_users_hardcoded_single_user_file(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
|
|
|
c36ff1 |
+ m_get_user_groups
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh/authorized_keys': ('suzie', 'suzie', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ user_suzie = 'suzie'
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby, user_suzie], mock_permissions, m_get_group,
|
|
|
c36ff1 |
+ m_get_owner, m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ home_bobby = homes[0]
|
|
|
c36ff1 |
+ home_suzie = homes[1]
|
|
|
c36ff1 |
+ m_get_user_groups.side_effect = mock_get_user_groups
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/home/suzie/.ssh/authorized_keys = ssh-xmss@openssh.com
|
|
|
c36ff1 |
+ self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_suzie, 'authorized_keys', 'ssh-xmss@openssh.com', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/sshd_config
|
|
|
c36ff1 |
+ options = "%s" % (authorized_keys)
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_bobby, sshd_config, authorized_keys, keys, delete_keys=False
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ default = "%s/.ssh/authorized_keys" % home_suzie
|
|
|
c36ff1 |
+ self.execute_and_check(user_suzie, sshd_config, default, keys)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_user_groups")
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_two_users_hardcoded_single_user_file_inverted(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
|
|
|
c36ff1 |
+ m_get_user_groups
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh/authorized_keys': ('suzie', 'suzie', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ user_suzie = 'suzie'
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby, user_suzie], mock_permissions, m_get_group,
|
|
|
c36ff1 |
+ m_get_owner, m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ home_bobby = homes[0]
|
|
|
c36ff1 |
+ home_suzie = homes[1]
|
|
|
c36ff1 |
+ m_get_user_groups.side_effect = mock_get_user_groups
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/home/suzie/.ssh/authorized_keys = ssh-xmss@openssh.com
|
|
|
c36ff1 |
+ authorized_keys2 = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_suzie, 'authorized_keys', 'ssh-xmss@openssh.com', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/sshd_config
|
|
|
c36ff1 |
+ options = "%s" % (authorized_keys2)
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ default = "%s/.ssh/authorized_keys" % home_bobby
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_bobby, sshd_config, default, keys, delete_keys=False
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ self.execute_and_check(user_suzie, sshd_config, authorized_keys2, keys)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_user_groups")
|
|
|
c36ff1 |
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_permissions")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_owner")
|
|
|
c36ff1 |
+ @patch("cloudinit.util.get_group")
|
|
|
c36ff1 |
+ def test_two_users_hardcoded_user_files(
|
|
|
c36ff1 |
+ self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
|
|
|
c36ff1 |
+ m_get_user_groups
|
|
|
c36ff1 |
+ ):
|
|
|
c36ff1 |
+ keys = {}
|
|
|
c36ff1 |
+ users = {}
|
|
|
c36ff1 |
+ mock_permissions = {
|
|
|
c36ff1 |
+ '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
|
|
|
c36ff1 |
+ '/tmp/home/suzie/.ssh/authorized_keys': ('suzie', 'suzie', 0o600),
|
|
|
c36ff1 |
+ }
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ user_bobby = 'bobby'
|
|
|
c36ff1 |
+ user_suzie = 'suzie'
|
|
|
c36ff1 |
+ homes = self.create_fake_users(
|
|
|
c36ff1 |
+ [user_bobby, user_suzie], mock_permissions, m_get_group,
|
|
|
c36ff1 |
+ m_get_owner, m_get_permissions, m_getpwnam, users
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ home_bobby = homes[0]
|
|
|
c36ff1 |
+ home_suzie = homes[1]
|
|
|
c36ff1 |
+ m_get_user_groups.side_effect = mock_get_user_groups
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/home/bobby/.ssh/authorized_keys = rsa
|
|
|
c36ff1 |
+ authorized_keys = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_bobby, 'authorized_keys', 'rsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/home/suzie/.ssh/authorized_keys = ssh-xmss@openssh.com
|
|
|
c36ff1 |
+ authorized_keys2 = self.create_user_authorized_file(
|
|
|
c36ff1 |
+ home_suzie, 'authorized_keys', 'ssh-xmss@openssh.com', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/etc/ssh/authorized_keys = ecdsa
|
|
|
c36ff1 |
+ authorized_keys_global = self.create_global_authorized_file(
|
|
|
c36ff1 |
+ 'etc/ssh/authorized_keys', 'ecdsa', keys
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ # /tmp/sshd_config
|
|
|
c36ff1 |
+ options = "%s %s %s" % \
|
|
|
c36ff1 |
+ (authorized_keys_global, authorized_keys, authorized_keys2)
|
|
|
c36ff1 |
+ sshd_config = self.create_sshd_config(options)
|
|
|
c36ff1 |
+
|
|
|
c36ff1 |
+ self.execute_and_check(
|
|
|
c36ff1 |
+ user_bobby, sshd_config, authorized_keys, keys, delete_keys=False
|
|
|
c36ff1 |
+ )
|
|
|
c36ff1 |
+ self.execute_and_check(user_suzie, sshd_config, authorized_keys2, keys)
|
|
|
c36ff1 |
|
|
|
c36ff1 |
# vi: ts=4 expandtab
|
|
|
c36ff1 |
--
|
|
|
c36ff1 |
2.27.0
|
|
|
c36ff1 |
|