652 lines
32 KiB
Diff
652 lines
32 KiB
Diff
|
From 857009723f14e9ad2f5f4c8614d72982b00ec27d Mon Sep 17 00:00:00 2001
|
||
|
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||
|
Date: Mon, 12 Jul 2021 21:47:37 +0200
|
||
|
Subject: [PATCH 2/2] ssh-util: allow cloudinit to merge all ssh keys into a
|
||
|
custom user file, defined in AuthorizedKeysFile (#937)
|
||
|
|
||
|
RH-Author: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||
|
RH-MergeRequest: 5: ssh-util: allow cloudinit to merge all ssh keys into a custom user file, defined in AuthorizedKeysFile (#937)
|
||
|
RH-Commit: [1/1] 3ed352e47c34e2ed2a1f9f5d68bc8b8f9a1365a6 (eesposit/cloud-init-centos-)
|
||
|
RH-Bugzilla: 1979099
|
||
|
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||
|
RH-Acked-by: Mohamed Gamal Morsy <mmorsy@redhat.com>
|
||
|
|
||
|
Conflicts: upstream patch modifies tests/integration_tests/util.py, that is
|
||
|
not present in RHEL.
|
||
|
|
||
|
commit 9b52405c6f0de5e00d5ee9c1d13540425d8f6bf5
|
||
|
Author: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||
|
Date: Mon Jul 12 20:21:02 2021 +0200
|
||
|
|
||
|
ssh-util: allow cloudinit to merge all ssh keys into a custom user file, defined in AuthorizedKeysFile (#937)
|
||
|
|
||
|
This patch aims to fix LP1911680, by analyzing the files provided
|
||
|
in sshd_config and merge all keys into an user-specific file. Also
|
||
|
introduces additional tests to cover this specific case.
|
||
|
|
||
|
The file is picked by analyzing the path given in AuthorizedKeysFile.
|
||
|
|
||
|
If it points inside the current user folder (path is /home/user/*), it
|
||
|
means it is an user-specific file, so we can copy all user-keys there.
|
||
|
If it contains a %u or %h, it means that there will be a specific
|
||
|
authorized_keys file for each user, so we can copy all user-keys there.
|
||
|
If no path points to an user-specific file, for example when only
|
||
|
/etc/ssh/authorized_keys is given, default to ~/.ssh/authorized_keys.
|
||
|
Note that if there are more than a single user-specific file, the last
|
||
|
one will be picked.
|
||
|
|
||
|
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||
|
Co-authored-by: James Falcon <therealfalcon@gmail.com>
|
||
|
|
||
|
LP: #1911680
|
||
|
RHBZ:1862967
|
||
|
|
||
|
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||
|
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
||
|
---
|
||
|
cloudinit/ssh_util.py | 22 +-
|
||
|
.../assets/keys/id_rsa.test1 | 38 +++
|
||
|
.../assets/keys/id_rsa.test1.pub | 1 +
|
||
|
.../assets/keys/id_rsa.test2 | 38 +++
|
||
|
.../assets/keys/id_rsa.test2.pub | 1 +
|
||
|
.../assets/keys/id_rsa.test3 | 38 +++
|
||
|
.../assets/keys/id_rsa.test3.pub | 1 +
|
||
|
.../modules/test_ssh_keysfile.py | 85 ++++++
|
||
|
tests/unittests/test_sshutil.py | 246 +++++++++++++++++-
|
||
|
9 files changed, 456 insertions(+), 14 deletions(-)
|
||
|
create mode 100644 tests/integration_tests/assets/keys/id_rsa.test1
|
||
|
create mode 100644 tests/integration_tests/assets/keys/id_rsa.test1.pub
|
||
|
create mode 100644 tests/integration_tests/assets/keys/id_rsa.test2
|
||
|
create mode 100644 tests/integration_tests/assets/keys/id_rsa.test2.pub
|
||
|
create mode 100644 tests/integration_tests/assets/keys/id_rsa.test3
|
||
|
create mode 100644 tests/integration_tests/assets/keys/id_rsa.test3.pub
|
||
|
create mode 100644 tests/integration_tests/modules/test_ssh_keysfile.py
|
||
|
|
||
|
diff --git a/cloudinit/ssh_util.py b/cloudinit/ssh_util.py
|
||
|
index c08042d6..89057262 100644
|
||
|
--- a/cloudinit/ssh_util.py
|
||
|
+++ b/cloudinit/ssh_util.py
|
||
|
@@ -252,13 +252,15 @@ def render_authorizedkeysfile_paths(value, homedir, username):
|
||
|
def extract_authorized_keys(username, sshd_cfg_file=DEF_SSHD_CFG):
|
||
|
(ssh_dir, pw_ent) = users_ssh_info(username)
|
||
|
default_authorizedkeys_file = os.path.join(ssh_dir, 'authorized_keys')
|
||
|
+ user_authorizedkeys_file = default_authorizedkeys_file
|
||
|
auth_key_fns = []
|
||
|
with util.SeLinuxGuard(ssh_dir, recursive=True):
|
||
|
try:
|
||
|
ssh_cfg = parse_ssh_config_map(sshd_cfg_file)
|
||
|
+ key_paths = ssh_cfg.get("authorizedkeysfile",
|
||
|
+ "%h/.ssh/authorized_keys")
|
||
|
auth_key_fns = render_authorizedkeysfile_paths(
|
||
|
- ssh_cfg.get("authorizedkeysfile", "%h/.ssh/authorized_keys"),
|
||
|
- pw_ent.pw_dir, username)
|
||
|
+ key_paths, pw_ent.pw_dir, username)
|
||
|
|
||
|
except (IOError, OSError):
|
||
|
# Give up and use a default key filename
|
||
|
@@ -267,8 +269,22 @@ def extract_authorized_keys(username, sshd_cfg_file=DEF_SSHD_CFG):
|
||
|
"config from %r, using 'AuthorizedKeysFile' file "
|
||
|
"%r instead", DEF_SSHD_CFG, auth_key_fns[0])
|
||
|
|
||
|
+ # check if one of the keys is the user's one
|
||
|
+ for key_path, auth_key_fn in zip(key_paths.split(), auth_key_fns):
|
||
|
+ if any([
|
||
|
+ '%u' in key_path,
|
||
|
+ '%h' in key_path,
|
||
|
+ auth_key_fn.startswith('{}/'.format(pw_ent.pw_dir))
|
||
|
+ ]):
|
||
|
+ user_authorizedkeys_file = auth_key_fn
|
||
|
+
|
||
|
+ if user_authorizedkeys_file != default_authorizedkeys_file:
|
||
|
+ LOG.debug(
|
||
|
+ "AuthorizedKeysFile has an user-specific authorized_keys, "
|
||
|
+ "using %s", user_authorizedkeys_file)
|
||
|
+
|
||
|
# always store all the keys in the user's private file
|
||
|
- return (default_authorizedkeys_file, parse_authorized_keys(auth_key_fns))
|
||
|
+ return (user_authorizedkeys_file, parse_authorized_keys(auth_key_fns))
|
||
|
|
||
|
|
||
|
def setup_user_keys(keys, username, options=None):
|
||
|
diff --git a/tests/integration_tests/assets/keys/id_rsa.test1 b/tests/integration_tests/assets/keys/id_rsa.test1
|
||
|
new file mode 100644
|
||
|
index 00000000..bd4c822e
|
||
|
--- /dev/null
|
||
|
+++ b/tests/integration_tests/assets/keys/id_rsa.test1
|
||
|
@@ -0,0 +1,38 @@
|
||
|
+-----BEGIN OPENSSH PRIVATE KEY-----
|
||
|
+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
|
||
|
+CHbTWbsFrHR1qFAAAACmphbWVzQG5ld3Q=
|
||
|
+-----END OPENSSH PRIVATE KEY-----
|
||
|
diff --git a/tests/integration_tests/assets/keys/id_rsa.test1.pub b/tests/integration_tests/assets/keys/id_rsa.test1.pub
|
||
|
new file mode 100644
|
||
|
index 00000000..3d2e26e1
|
||
|
--- /dev/null
|
||
|
+++ b/tests/integration_tests/assets/keys/id_rsa.test1.pub
|
||
|
@@ -0,0 +1 @@
|
||
|
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC1GUb3ponbdRG8CA79sGy4uX6Wq5zfnRpLBX3z+Ly+W86fkZVwfJ63+tdCDiVnizSjAbeRbJAVjye9OT4b5Q1Tx83RqcDMEOL7gzOx6OQX2xyNMwWZdWsikgKpXdthA2M+/qv65tB2QPTV27d4H23n/OXziC2yGnSEF2h/LPy7X8Dbmzt9VTrsbIjs90q9pL7K8lIidmdvZ71VDN8kL6oCc3uBRlq0EqN2BS0l6JRR4NpWdpLXtvRqhP2IE7SwhxC05MGViUKMW3VMAwtnrgePdJ8ZkHtiPRn4vMUHYhZfXPOyMjrxiRg8o5Iv81iFBT17nmQlDtaeeA0dj/af8Dt00pIUOaAzlz0haB5nt+Omx8hgCgn8IDEUg0P48Qd3WAJLrGgSFpRXKHlYf9eTAq/WfHgCJnYEUIT7vmTJJEtlQn7nLSvcNTmS0xd5RHNUg/um7RHwMK6X4ZT4z/5MfJa0AQ6B5o0np9imjaKkOFuZNi3eSTaUwhqlHEYEWz7fux0= test1@host
|
||
|
diff --git a/tests/integration_tests/assets/keys/id_rsa.test2 b/tests/integration_tests/assets/keys/id_rsa.test2
|
||
|
new file mode 100644
|
||
|
index 00000000..5854d901
|
||
|
--- /dev/null
|
||
|
+++ b/tests/integration_tests/assets/keys/id_rsa.test2
|
||
|
@@ -0,0 +1,38 @@
|
||
|
+-----BEGIN OPENSSH PRIVATE KEY-----
|
||
|
+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
|
||
|
+tZRSm2GEgnDGAvAAAACmphbWVzQG5ld3Q=
|
||
|
+-----END OPENSSH PRIVATE KEY-----
|
||
|
diff --git a/tests/integration_tests/assets/keys/id_rsa.test2.pub b/tests/integration_tests/assets/keys/id_rsa.test2.pub
|
||
|
new file mode 100644
|
||
|
index 00000000..f3831a57
|
||
|
--- /dev/null
|
||
|
+++ b/tests/integration_tests/assets/keys/id_rsa.test2.pub
|
||
|
@@ -0,0 +1 @@
|
||
|
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC8rnQPY9Y5ziKTIdVElLq0OGrOMvlwqKK8gPinVfwFgJXDzdcAQY4uci1TJVcc0AOWHp+lWrU1joDYlW3KCg8XpkXHymHshYyaeEN2fEsvIaxuF3UzW2JckP9H7dacYdEng8qtBq8wuCodGuJ5XdBVV+MVJ6jqNf/hOu4/pma8hMxlYmtdoamHEn+k/KQR2Q6LwCYpT0U2+KiPI9Lac22P0H/rfkh2Ba+t3tV/lhyzD0xHnktZKJ3BrznpEfrZiXoukQZMebWUmIyTW7zwk9Kq+iGFpSQsqQWlxDBwHSbvpbUo7KWUmyZfxs1euVmwj5aKJgjteXm9CbbXsMS415q+08C0MzG7weZODZQnnk1rF6Ft97aXHaTmRgYbDeLU7U5U3alnb84HvUu5xh3/mrE9rPTfCzBwY4lgY+Q1zjS90RL9Jxzti3weydm7OqTI6DOfQeJQLOhhRgthOkt/7IabDFLIARjTnpo5+QKxugoc4qI6aUnE1plkVCRfV696ngM= test2@host
|
||
|
diff --git a/tests/integration_tests/assets/keys/id_rsa.test3 b/tests/integration_tests/assets/keys/id_rsa.test3
|
||
|
new file mode 100644
|
||
|
index 00000000..2596c762
|
||
|
--- /dev/null
|
||
|
+++ b/tests/integration_tests/assets/keys/id_rsa.test3
|
||
|
@@ -0,0 +1,38 @@
|
||
|
+-----BEGIN OPENSSH PRIVATE KEY-----
|
||
|
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
|
||
|
+NhAAAAAwEAAQAAAYEApPG4MdkYQKD57/qreFrh9GRC22y66qZOWZWRjC887rrbvBzO69hV
|
||
|
+yJpTIXleJEvpWiHYcjMR5G6NNFsnNtZ4fxDqmSc4vcFj53JsE/XNqLKq6psXadCb5vkNpG
|
||
|
+bxA+Z5bJlzJ969PgJIIEbgc86sei4kgR2MuPWqtZbY5GkpNCTqWuLYeFK+14oFruA2nyWH
|
||
|
+9MOIRDHK/d597psHy+LTMtymO7ZPhO571abKw6jvvwiSeDxVE9kV7KAQIuM9/S3gftvgQQ
|
||
|
+ron3GL34pgmIabdSGdbfHqGDooryJhlbquJZELBN236KgRNTCAjVvUzjjQr1eRP3xssGwV
|
||
|
+O6ECBGCQLl/aYogAgtwnwj9iXqtfiLK3EwlgjquU4+JQ0CVtLhG3gIZB+qoMThco0pmHTr
|
||
|
+jtfQCwrztsBBFunSa2/CstuV1mQ5O5ZrZ6ACo9yPRBNkns6+CiKdtMtCtzi3k2RDz9jpYm
|
||
|
+Pcak03Lr7IkdC1Tp6+jA+//yPHSO1o4CqW89IQzNAAAFgEUd7lZFHe5WAAAAB3NzaC1yc2
|
||
|
+EAAAGBAKTxuDHZGECg+e/6q3ha4fRkQttsuuqmTlmVkYwvPO6627wczuvYVciaUyF5XiRL
|
||
|
+6Voh2HIzEeRujTRbJzbWeH8Q6pknOL3BY+dybBP1zaiyquqbF2nQm+b5DaRm8QPmeWyZcy
|
||
|
+fevT4CSCBG4HPOrHouJIEdjLj1qrWW2ORpKTQk6lri2HhSvteKBa7gNp8lh/TDiEQxyv3e
|
||
|
+fe6bB8vi0zLcpju2T4Tue9WmysOo778Ikng8VRPZFeygECLjPf0t4H7b4EEK6J9xi9+KYJ
|
||
|
+iGm3UhnW3x6hg6KK8iYZW6riWRCwTdt+ioETUwgI1b1M440K9XkT98bLBsFTuhAgRgkC5f
|
||
|
+2mKIAILcJ8I/Yl6rX4iytxMJYI6rlOPiUNAlbS4Rt4CGQfqqDE4XKNKZh0647X0AsK87bA
|
||
|
+QRbp0mtvwrLbldZkOTuWa2egAqPcj0QTZJ7OvgoinbTLQrc4t5NkQ8/Y6WJj3GpNNy6+yJ
|
||
|
+HQtU6evowPv/8jx0jtaOAqlvPSEMzQAAAAMBAAEAAAGAGaqbdPZJNdVWzyb8g6/wtSzc0n
|
||
|
+Qq6dSTIJGLonq/So69HpqFAGIbhymsger24UMGvsXBfpO/1wH06w68HWZmPa+OMeLOi4iK
|
||
|
+WTuO4dQ/+l5DBlq32/lgKSLcIpb6LhcxEdsW9j9Mx1dnjc45owun/yMq/wRwH1/q/nLIsV
|
||
|
+JD3R9ZcGcYNDD8DWIm3D17gmw+qbG7hJES+0oh4n0xS2KyZpm7LFOEMDVEA8z+hE/HbryQ
|
||
|
+vjD1NC91n+qQWD1wKfN3WZDRwip3z1I5VHMpvXrA/spHpa9gzHK5qXNmZSz3/dfA1zHjCR
|
||
|
+2dHjJnrIUH8nyPfw8t+COC+sQBL3Nr0KUWEFPRM08cOcQm4ctzg17aDIZBONjlZGKlReR8
|
||
|
+1zfAw84Q70q2spLWLBLXSFblHkaOfijEbejIbaz2UUEQT27WD7RHAORdQlkx7eitk66T9d
|
||
|
+DzIq/cpYhm5Fs8KZsh3PLldp9nsHbD2Oa9J9LJyI4ryuIW0mVwRdvPSiiYi3K+mDCpAAAA
|
||
|
+wBe+ugEEJ+V7orb1f4Zez0Bd4FNkEc52WZL4CWbaCtM+ZBg5KnQ6xW14JdC8IS9cNi/I5P
|
||
|
+yLsBvG4bWPLGgQruuKY6oLueD6BFnKjqF6ACUCiSQldh4BAW1nYc2U48+FFvo3ZQyudFSy
|
||
|
+QEFlhHmcaNMDo0AIJY5Xnq2BG3nEX7AqdtZ8hhenHwLCRQJatDwSYBHDpSDdh9vpTnGp/2
|
||
|
+0jBz25Ko4UANzvSAc3sA4yN3jfpoM366TgdNf8x3g1v7yljQAAAMEA0HSQjzH5nhEwB58k
|
||
|
+mYYxnBYp1wb86zIuVhAyjZaeinvBQSTmLow8sXIHcCVuD3CgBezlU2SX5d9YuvRU9rcthi
|
||
|
+uzn4wWnbnzYy4SwzkMJXchUAkumFVD8Hq5TNPh2Z+033rLLE08EhYypSeVpuzdpFoStaS9
|
||
|
+3DUZA2bR/zLZI9MOVZRUcYImNegqIjOYHY8Sbj3/0QPV6+WpUJFMPvvedWhfaOsRMTA6nr
|
||
|
+VLG4pxkrieVl0UtuRGbzD/exXhXVi7AAAAwQDKkJj4ez/+KZFYlZQKiV0BrfUFcgS6ElFM
|
||
|
+2CZIEagCtu8eedrwkNqx2FUX33uxdvUTr4c9I3NvWeEEGTB9pgD4lh1x/nxfuhyGXtimFM
|
||
|
+GnznGV9oyz0DmKlKiKSEGwWf5G+/NiiCwwVJ7wsQQm7TqNtkQ9b8MhWWXC7xlXKUs7dmTa
|
||
|
+e8AqAndCCMEnbS1UQFO/R5PNcZXkFWDggLQ/eWRYKlrXgdnUgH6h0saOcViKpNJBUXb3+x
|
||
|
+eauhOY52PS/BcAAAAKamFtZXNAbmV3dAE=
|
||
|
+-----END OPENSSH PRIVATE KEY-----
|
||
|
diff --git a/tests/integration_tests/assets/keys/id_rsa.test3.pub b/tests/integration_tests/assets/keys/id_rsa.test3.pub
|
||
|
new file mode 100644
|
||
|
index 00000000..057db632
|
||
|
--- /dev/null
|
||
|
+++ b/tests/integration_tests/assets/keys/id_rsa.test3.pub
|
||
|
@@ -0,0 +1 @@
|
||
|
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCk8bgx2RhAoPnv+qt4WuH0ZELbbLrqpk5ZlZGMLzzuutu8HM7r2FXImlMheV4kS+laIdhyMxHkbo00Wyc21nh/EOqZJzi9wWPncmwT9c2osqrqmxdp0Jvm+Q2kZvED5nlsmXMn3r0+AkggRuBzzqx6LiSBHYy49aq1ltjkaSk0JOpa4th4Ur7XigWu4DafJYf0w4hEMcr93n3umwfL4tMy3KY7tk+E7nvVpsrDqO+/CJJ4PFUT2RXsoBAi4z39LeB+2+BBCuifcYvfimCYhpt1IZ1t8eoYOiivImGVuq4lkQsE3bfoqBE1MICNW9TOONCvV5E/fGywbBU7oQIEYJAuX9piiACC3CfCP2Jeq1+IsrcTCWCOq5Tj4lDQJW0uEbeAhkH6qgxOFyjSmYdOuO19ALCvO2wEEW6dJrb8Ky25XWZDk7lmtnoAKj3I9EE2Sezr4KIp20y0K3OLeTZEPP2OliY9xqTTcuvsiR0LVOnr6MD7//I8dI7WjgKpbz0hDM0= test3@host
|
||
|
diff --git a/tests/integration_tests/modules/test_ssh_keysfile.py b/tests/integration_tests/modules/test_ssh_keysfile.py
|
||
|
new file mode 100644
|
||
|
index 00000000..f82d7649
|
||
|
--- /dev/null
|
||
|
+++ b/tests/integration_tests/modules/test_ssh_keysfile.py
|
||
|
@@ -0,0 +1,85 @@
|
||
|
+import paramiko
|
||
|
+import pytest
|
||
|
+from io import StringIO
|
||
|
+from paramiko.ssh_exception import SSHException
|
||
|
+
|
||
|
+from tests.integration_tests.instances import IntegrationInstance
|
||
|
+from tests.integration_tests.util import get_test_rsa_keypair
|
||
|
+
|
||
|
+TEST_USER1_KEYS = get_test_rsa_keypair('test1')
|
||
|
+TEST_USER2_KEYS = get_test_rsa_keypair('test2')
|
||
|
+TEST_DEFAULT_KEYS = get_test_rsa_keypair('test3')
|
||
|
+
|
||
|
+USERDATA = """\
|
||
|
+#cloud-config
|
||
|
+bootcmd:
|
||
|
+ - sed -i 's;#AuthorizedKeysFile.*;AuthorizedKeysFile /etc/ssh/authorized_keys %h/.ssh/authorized_keys2;' /etc/ssh/sshd_config
|
||
|
+ssh_authorized_keys:
|
||
|
+ - {default}
|
||
|
+users:
|
||
|
+- default
|
||
|
+- name: test_user1
|
||
|
+ ssh_authorized_keys:
|
||
|
+ - {user1}
|
||
|
+- name: test_user2
|
||
|
+ ssh_authorized_keys:
|
||
|
+ - {user2}
|
||
|
+""".format( # noqa: E501
|
||
|
+ default=TEST_DEFAULT_KEYS.public_key,
|
||
|
+ user1=TEST_USER1_KEYS.public_key,
|
||
|
+ user2=TEST_USER2_KEYS.public_key,
|
||
|
+)
|
||
|
+
|
||
|
+
|
||
|
+@pytest.mark.ubuntu
|
||
|
+@pytest.mark.user_data(USERDATA)
|
||
|
+def test_authorized_keys(client: IntegrationInstance):
|
||
|
+ expected_keys = [
|
||
|
+ ('test_user1', '/home/test_user1/.ssh/authorized_keys2',
|
||
|
+ TEST_USER1_KEYS),
|
||
|
+ ('test_user2', '/home/test_user2/.ssh/authorized_keys2',
|
||
|
+ TEST_USER2_KEYS),
|
||
|
+ ('ubuntu', '/home/ubuntu/.ssh/authorized_keys2',
|
||
|
+ TEST_DEFAULT_KEYS),
|
||
|
+ ('root', '/root/.ssh/authorized_keys2', TEST_DEFAULT_KEYS),
|
||
|
+ ]
|
||
|
+
|
||
|
+ for user, filename, keys in expected_keys:
|
||
|
+ contents = client.read_from_file(filename)
|
||
|
+ if user in ['ubuntu', 'root']:
|
||
|
+ # Our personal public key gets added by pycloudlib
|
||
|
+ lines = contents.split('\n')
|
||
|
+ assert len(lines) == 2
|
||
|
+ assert keys.public_key.strip() in contents
|
||
|
+ else:
|
||
|
+ assert contents.strip() == keys.public_key.strip()
|
||
|
+
|
||
|
+ # Ensure we can actually connect
|
||
|
+ ssh = paramiko.SSHClient()
|
||
|
+ ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||
|
+ paramiko_key = paramiko.RSAKey.from_private_key(StringIO(
|
||
|
+ keys.private_key))
|
||
|
+
|
||
|
+ # Will fail with AuthenticationException if
|
||
|
+ # we cannot connect
|
||
|
+ ssh.connect(
|
||
|
+ client.instance.ip,
|
||
|
+ username=user,
|
||
|
+ pkey=paramiko_key,
|
||
|
+ look_for_keys=False,
|
||
|
+ allow_agent=False,
|
||
|
+ )
|
||
|
+
|
||
|
+ # Ensure other uses can't connect using our key
|
||
|
+ other_users = [u[0] for u in expected_keys if u[2] != keys]
|
||
|
+ for other_user in other_users:
|
||
|
+ with pytest.raises(SSHException):
|
||
|
+ print('trying to connect as {} with key from {}'.format(
|
||
|
+ other_user, user))
|
||
|
+ ssh.connect(
|
||
|
+ client.instance.ip,
|
||
|
+ username=other_user,
|
||
|
+ pkey=paramiko_key,
|
||
|
+ look_for_keys=False,
|
||
|
+ allow_agent=False,
|
||
|
+ )
|
||
|
diff --git a/tests/unittests/test_sshutil.py b/tests/unittests/test_sshutil.py
|
||
|
index fd1d1bac..bcb8044f 100644
|
||
|
--- a/tests/unittests/test_sshutil.py
|
||
|
+++ b/tests/unittests/test_sshutil.py
|
||
|
@@ -570,20 +570,33 @@ class TestBasicAuthorizedKeyParse(test_helpers.CiTestCase):
|
||
|
ssh_util.render_authorizedkeysfile_paths(
|
||
|
"%h/.keys", "/homedirs/bobby", "bobby"))
|
||
|
|
||
|
+ def test_all(self):
|
||
|
+ self.assertEqual(
|
||
|
+ ["/homedirs/bobby/.keys", "/homedirs/bobby/.secret/keys",
|
||
|
+ "/keys/path1", "/opt/bobby/keys"],
|
||
|
+ ssh_util.render_authorizedkeysfile_paths(
|
||
|
+ "%h/.keys .secret/keys /keys/path1 /opt/%u/keys",
|
||
|
+ "/homedirs/bobby", "bobby"))
|
||
|
+
|
||
|
|
||
|
class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
|
||
|
|
||
|
@patch("cloudinit.ssh_util.pwd.getpwnam")
|
||
|
def test_multiple_authorizedkeys_file_order1(self, m_getpwnam):
|
||
|
- fpw = FakePwEnt(pw_name='bobby', pw_dir='/home2/bobby')
|
||
|
+ fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
||
|
m_getpwnam.return_value = fpw
|
||
|
- authorized_keys = self.tmp_path('authorized_keys')
|
||
|
+ user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
||
|
+
|
||
|
+ # /tmp/home2/bobby/.ssh/authorized_keys = rsa
|
||
|
+ authorized_keys = self.tmp_path('authorized_keys', dir=user_ssh_folder)
|
||
|
util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
||
|
|
||
|
- user_keys = self.tmp_path('user_keys')
|
||
|
+ # /tmp/home2/bobby/.ssh/user_keys = dsa
|
||
|
+ user_keys = self.tmp_path('user_keys', dir=user_ssh_folder)
|
||
|
util.write_file(user_keys, VALID_CONTENT['dsa'])
|
||
|
|
||
|
- sshd_config = self.tmp_path('sshd_config')
|
||
|
+ # /tmp/sshd_config
|
||
|
+ sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
||
|
util.write_file(
|
||
|
sshd_config,
|
||
|
"AuthorizedKeysFile %s %s" % (authorized_keys, user_keys)
|
||
|
@@ -593,33 +606,244 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
|
||
|
fpw.pw_name, sshd_config)
|
||
|
content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
||
|
|
||
|
- self.assertEqual("%s/.ssh/authorized_keys" % fpw.pw_dir, auth_key_fn)
|
||
|
+ self.assertEqual(user_keys, auth_key_fn)
|
||
|
self.assertTrue(VALID_CONTENT['rsa'] in content)
|
||
|
self.assertTrue(VALID_CONTENT['dsa'] in content)
|
||
|
|
||
|
@patch("cloudinit.ssh_util.pwd.getpwnam")
|
||
|
def test_multiple_authorizedkeys_file_order2(self, m_getpwnam):
|
||
|
- fpw = FakePwEnt(pw_name='suzie', pw_dir='/home/suzie')
|
||
|
+ fpw = FakePwEnt(pw_name='suzie', pw_dir='/tmp/home/suzie')
|
||
|
m_getpwnam.return_value = fpw
|
||
|
- authorized_keys = self.tmp_path('authorized_keys')
|
||
|
+ user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
||
|
+
|
||
|
+ # /tmp/home/suzie/.ssh/authorized_keys = rsa
|
||
|
+ authorized_keys = self.tmp_path('authorized_keys', dir=user_ssh_folder)
|
||
|
util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
||
|
|
||
|
- user_keys = self.tmp_path('user_keys')
|
||
|
+ # /tmp/home/suzie/.ssh/user_keys = dsa
|
||
|
+ user_keys = self.tmp_path('user_keys', dir=user_ssh_folder)
|
||
|
util.write_file(user_keys, VALID_CONTENT['dsa'])
|
||
|
|
||
|
- sshd_config = self.tmp_path('sshd_config')
|
||
|
+ # /tmp/sshd_config
|
||
|
+ sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
||
|
util.write_file(
|
||
|
sshd_config,
|
||
|
- "AuthorizedKeysFile %s %s" % (authorized_keys, user_keys)
|
||
|
+ "AuthorizedKeysFile %s %s" % (user_keys, authorized_keys)
|
||
|
)
|
||
|
|
||
|
(auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
||
|
- fpw.pw_name, sshd_config
|
||
|
+ fpw.pw_name, sshd_config)
|
||
|
+ content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
||
|
+
|
||
|
+ self.assertEqual(authorized_keys, auth_key_fn)
|
||
|
+ self.assertTrue(VALID_CONTENT['rsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['dsa'] in content)
|
||
|
+
|
||
|
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
||
|
+ def test_multiple_authorizedkeys_file_local_global(self, m_getpwnam):
|
||
|
+ fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
||
|
+ m_getpwnam.return_value = fpw
|
||
|
+ user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
||
|
+
|
||
|
+ # /tmp/home2/bobby/.ssh/authorized_keys = rsa
|
||
|
+ authorized_keys = self.tmp_path('authorized_keys', dir=user_ssh_folder)
|
||
|
+ util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
||
|
+
|
||
|
+ # /tmp/home2/bobby/.ssh/user_keys = dsa
|
||
|
+ user_keys = self.tmp_path('user_keys', dir=user_ssh_folder)
|
||
|
+ util.write_file(user_keys, VALID_CONTENT['dsa'])
|
||
|
+
|
||
|
+ # /tmp/etc/ssh/authorized_keys = ecdsa
|
||
|
+ authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys',
|
||
|
+ dir="/tmp")
|
||
|
+ util.write_file(authorized_keys_global, VALID_CONTENT['ecdsa'])
|
||
|
+
|
||
|
+ # /tmp/sshd_config
|
||
|
+ sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
||
|
+ util.write_file(
|
||
|
+ sshd_config,
|
||
|
+ "AuthorizedKeysFile %s %s %s" % (authorized_keys_global,
|
||
|
+ user_keys, authorized_keys)
|
||
|
+ )
|
||
|
+
|
||
|
+ (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
||
|
+ fpw.pw_name, sshd_config)
|
||
|
+ content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
||
|
+
|
||
|
+ self.assertEqual(authorized_keys, auth_key_fn)
|
||
|
+ self.assertTrue(VALID_CONTENT['rsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['dsa'] in content)
|
||
|
+
|
||
|
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
||
|
+ def test_multiple_authorizedkeys_file_local_global2(self, m_getpwnam):
|
||
|
+ fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
||
|
+ m_getpwnam.return_value = fpw
|
||
|
+ user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
||
|
+
|
||
|
+ # /tmp/home2/bobby/.ssh/authorized_keys2 = rsa
|
||
|
+ authorized_keys = self.tmp_path('authorized_keys2',
|
||
|
+ dir=user_ssh_folder)
|
||
|
+ util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
||
|
+
|
||
|
+ # /tmp/home2/bobby/.ssh/user_keys3 = dsa
|
||
|
+ user_keys = self.tmp_path('user_keys3', dir=user_ssh_folder)
|
||
|
+ util.write_file(user_keys, VALID_CONTENT['dsa'])
|
||
|
+
|
||
|
+ # /tmp/etc/ssh/authorized_keys = ecdsa
|
||
|
+ authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys',
|
||
|
+ dir="/tmp")
|
||
|
+ util.write_file(authorized_keys_global, VALID_CONTENT['ecdsa'])
|
||
|
+
|
||
|
+ # /tmp/sshd_config
|
||
|
+ sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
||
|
+ util.write_file(
|
||
|
+ sshd_config,
|
||
|
+ "AuthorizedKeysFile %s %s %s" % (authorized_keys_global,
|
||
|
+ authorized_keys, user_keys)
|
||
|
+ )
|
||
|
+
|
||
|
+ (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
||
|
+ fpw.pw_name, sshd_config)
|
||
|
+ content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
||
|
+
|
||
|
+ self.assertEqual(user_keys, auth_key_fn)
|
||
|
+ self.assertTrue(VALID_CONTENT['rsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['dsa'] in content)
|
||
|
+
|
||
|
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
||
|
+ def test_multiple_authorizedkeys_file_global(self, m_getpwnam):
|
||
|
+ fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
||
|
+ m_getpwnam.return_value = fpw
|
||
|
+
|
||
|
+ # /tmp/etc/ssh/authorized_keys = rsa
|
||
|
+ authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys',
|
||
|
+ dir="/tmp")
|
||
|
+ util.write_file(authorized_keys_global, VALID_CONTENT['rsa'])
|
||
|
+
|
||
|
+ # /tmp/sshd_config
|
||
|
+ sshd_config = self.tmp_path('sshd_config')
|
||
|
+ util.write_file(
|
||
|
+ sshd_config,
|
||
|
+ "AuthorizedKeysFile %s" % (authorized_keys_global)
|
||
|
)
|
||
|
+
|
||
|
+ (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
||
|
+ fpw.pw_name, sshd_config)
|
||
|
content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
||
|
|
||
|
self.assertEqual("%s/.ssh/authorized_keys" % fpw.pw_dir, auth_key_fn)
|
||
|
self.assertTrue(VALID_CONTENT['rsa'] in content)
|
||
|
+
|
||
|
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
||
|
+ def test_multiple_authorizedkeys_file_multiuser(self, m_getpwnam):
|
||
|
+ fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home2/bobby')
|
||
|
+ m_getpwnam.return_value = fpw
|
||
|
+ user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
||
|
+ # /tmp/home2/bobby/.ssh/authorized_keys2 = rsa
|
||
|
+ authorized_keys = self.tmp_path('authorized_keys2',
|
||
|
+ dir=user_ssh_folder)
|
||
|
+ util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
||
|
+ # /tmp/home2/bobby/.ssh/user_keys3 = dsa
|
||
|
+ user_keys = self.tmp_path('user_keys3', dir=user_ssh_folder)
|
||
|
+ util.write_file(user_keys, VALID_CONTENT['dsa'])
|
||
|
+
|
||
|
+ fpw2 = FakePwEnt(pw_name='suzie', pw_dir='/tmp/home/suzie')
|
||
|
+ user_ssh_folder = "%s/.ssh" % fpw2.pw_dir
|
||
|
+ # /tmp/home/suzie/.ssh/authorized_keys2 = ssh-xmss@openssh.com
|
||
|
+ authorized_keys2 = self.tmp_path('authorized_keys2',
|
||
|
+ dir=user_ssh_folder)
|
||
|
+ util.write_file(authorized_keys2,
|
||
|
+ VALID_CONTENT['ssh-xmss@openssh.com'])
|
||
|
+
|
||
|
+ # /tmp/etc/ssh/authorized_keys = ecdsa
|
||
|
+ authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys2',
|
||
|
+ dir="/tmp")
|
||
|
+ util.write_file(authorized_keys_global, VALID_CONTENT['ecdsa'])
|
||
|
+
|
||
|
+ # /tmp/sshd_config
|
||
|
+ sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
||
|
+ util.write_file(
|
||
|
+ sshd_config,
|
||
|
+ "AuthorizedKeysFile %s %%h/.ssh/authorized_keys2 %s" %
|
||
|
+ (authorized_keys_global, user_keys)
|
||
|
+ )
|
||
|
+
|
||
|
+ # process first user
|
||
|
+ (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
||
|
+ fpw.pw_name, sshd_config)
|
||
|
+ content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
||
|
+
|
||
|
+ self.assertEqual(user_keys, auth_key_fn)
|
||
|
+ self.assertTrue(VALID_CONTENT['rsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['dsa'] in content)
|
||
|
+ self.assertFalse(VALID_CONTENT['ssh-xmss@openssh.com'] in content)
|
||
|
+
|
||
|
+ m_getpwnam.return_value = fpw2
|
||
|
+ # process second user
|
||
|
+ (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
||
|
+ fpw2.pw_name, sshd_config)
|
||
|
+ content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
||
|
+
|
||
|
+ self.assertEqual(authorized_keys2, auth_key_fn)
|
||
|
+ self.assertTrue(VALID_CONTENT['ssh-xmss@openssh.com'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['dsa'] in content)
|
||
|
+ self.assertFalse(VALID_CONTENT['rsa'] in content)
|
||
|
+
|
||
|
+ @patch("cloudinit.ssh_util.pwd.getpwnam")
|
||
|
+ def test_multiple_authorizedkeys_file_multiuser2(self, m_getpwnam):
|
||
|
+ fpw = FakePwEnt(pw_name='bobby', pw_dir='/tmp/home/bobby')
|
||
|
+ m_getpwnam.return_value = fpw
|
||
|
+ user_ssh_folder = "%s/.ssh" % fpw.pw_dir
|
||
|
+ # /tmp/home/bobby/.ssh/authorized_keys2 = rsa
|
||
|
+ authorized_keys = self.tmp_path('authorized_keys2',
|
||
|
+ dir=user_ssh_folder)
|
||
|
+ util.write_file(authorized_keys, VALID_CONTENT['rsa'])
|
||
|
+ # /tmp/home/bobby/.ssh/user_keys3 = dsa
|
||
|
+ user_keys = self.tmp_path('user_keys3', dir=user_ssh_folder)
|
||
|
+ util.write_file(user_keys, VALID_CONTENT['dsa'])
|
||
|
+
|
||
|
+ fpw2 = FakePwEnt(pw_name='badguy', pw_dir='/tmp/home/badguy')
|
||
|
+ user_ssh_folder = "%s/.ssh" % fpw2.pw_dir
|
||
|
+ # /tmp/home/badguy/home/bobby = ""
|
||
|
+ authorized_keys2 = self.tmp_path('home/bobby', dir="/tmp/home/badguy")
|
||
|
+
|
||
|
+ # /tmp/etc/ssh/authorized_keys = ecdsa
|
||
|
+ authorized_keys_global = self.tmp_path('etc/ssh/authorized_keys2',
|
||
|
+ dir="/tmp")
|
||
|
+ util.write_file(authorized_keys_global, VALID_CONTENT['ecdsa'])
|
||
|
+
|
||
|
+ # /tmp/sshd_config
|
||
|
+ sshd_config = self.tmp_path('sshd_config', dir="/tmp")
|
||
|
+ util.write_file(
|
||
|
+ sshd_config,
|
||
|
+ "AuthorizedKeysFile %s %%h/.ssh/authorized_keys2 %s %s" %
|
||
|
+ (authorized_keys_global, user_keys, authorized_keys2)
|
||
|
+ )
|
||
|
+
|
||
|
+ # process first user
|
||
|
+ (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
||
|
+ fpw.pw_name, sshd_config)
|
||
|
+ content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
||
|
+
|
||
|
+ self.assertEqual(user_keys, auth_key_fn)
|
||
|
+ self.assertTrue(VALID_CONTENT['rsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
||
|
+ self.assertTrue(VALID_CONTENT['dsa'] in content)
|
||
|
+
|
||
|
+ m_getpwnam.return_value = fpw2
|
||
|
+ # process second user
|
||
|
+ (auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
|
||
|
+ fpw2.pw_name, sshd_config)
|
||
|
+ content = ssh_util.update_authorized_keys(auth_key_entries, [])
|
||
|
+
|
||
|
+ # badguy should not take the key from the other user!
|
||
|
+ self.assertEqual(authorized_keys2, auth_key_fn)
|
||
|
+ self.assertTrue(VALID_CONTENT['ecdsa'] in content)
|
||
|
self.assertTrue(VALID_CONTENT['dsa'] in content)
|
||
|
+ self.assertFalse(VALID_CONTENT['rsa'] in content)
|
||
|
|
||
|
# vi: ts=4 expandtab
|
||
|
--
|
||
|
2.27.0
|
||
|
|