sailesh1993 / rpms / cloud-init

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