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