summaryrefslogtreecommitdiff
path: root/tests/unittests/test_sshutil.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unittests/test_sshutil.py')
-rw-r--r--tests/unittests/test_sshutil.py817
1 files changed, 490 insertions, 327 deletions
diff --git a/tests/unittests/test_sshutil.py b/tests/unittests/test_sshutil.py
index b210bd3b..d614350e 100644
--- a/tests/unittests/test_sshutil.py
+++ b/tests/unittests/test_sshutil.py
@@ -1,27 +1,29 @@
# This file is part of cloud-init. See LICENSE file for license information.
import os
-
from collections import namedtuple
from functools import partial
from unittest.mock import patch
-from cloudinit import ssh_util
+from cloudinit import ssh_util, util
from tests.unittests import helpers as test_helpers
-from cloudinit import util
# https://stackoverflow.com/questions/11351032/
-FakePwEnt = namedtuple('FakePwEnt', [
- 'pw_name',
- 'pw_passwd',
- 'pw_uid',
- 'pw_gid',
- 'pw_gecos',
- 'pw_dir',
- 'pw_shell',
-])
+FakePwEnt = namedtuple(
+ "FakePwEnt",
+ [
+ "pw_name",
+ "pw_passwd",
+ "pw_uid",
+ "pw_gid",
+ "pw_gecos",
+ "pw_dir",
+ "pw_shell",
+ ],
+)
FakePwEnt.__new__.__defaults__ = tuple(
- "UNSET_%s" % n for n in FakePwEnt._fields)
+ "UNSET_%s" % n for n in FakePwEnt._fields
+)
def mock_get_owner(updated_permissions, value):
@@ -57,7 +59,7 @@ def mock_getpwnam(users, username):
# the testdata for OpenSSH, and their private keys are available
# https://github.com/openssh/openssh-portable/tree/master/regress/unittests/sshkey/testdata
VALID_CONTENT = {
- 'dsa': (
+ "dsa": (
"AAAAB3NzaC1kc3MAAACBAIrjOQSlSea19bExXBMBKBvcLhBoVvNBjCppNzllipF"
"W4jgIOMcNanULRrZGjkOKat6MWJNetSbV1E6IOFDQ16rQgsh/OvYU9XhzM8seLa"
"A21VszZuhIV7/2DE3vxu7B54zVzueG1O1Deq6goQCRGWBUnqO2yluJiG4HzrnDa"
@@ -69,12 +71,12 @@ VALID_CONTENT = {
"JNDnIqDHxTkc6LY2vu8Y2pQ3/bVnllZZOda2oD5HQ7ovygQa6CH+fbaZHbdDUX/"
"5z7u2rVAlDw=="
),
- 'ecdsa': (
+ "ecdsa": (
"AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBITrGBB3cgJ"
"J7fPxvtMW9H3oRisNpJ3OAslxZeyP7I0A9BPAW0RQIwHVtVnM7zrp4nI+JLZov/"
"Ql7lc2leWL7CY="
),
- 'rsa': (
+ "rsa": (
"AAAAB3NzaC1yc2EAAAABIwAAAQEA3I7VUf2l5gSn5uavROsc5HRDpZdQueUq5oz"
"emNSj8T7enqKHOEaFoU2VoPgGEWC9RyzSQVeyD6s7APMcE82EtmW4skVEgEGSbD"
"c1pvxzxtchBj78hJP6Cf5TCMFSXw+Fz5rF1dR23QDbN1mkHs7adr8GW4kSWqU7Q"
@@ -82,11 +84,10 @@ VALID_CONTENT = {
"YWpMfYdPUnE7u536WqzFmsaqJctz3gBxH9Ex7dFtrxR4qiqEr9Qtlu3xGn7Bw07"
"/+i1D+ey3ONkZLN+LQ714cgj8fRS4Hj29SCmXp5Kt5/82cD/VN3NtHw=="
),
- 'ed25519': (
- "AAAAC3NzaC1lZDI1NTE5AAAAIA1J77+CrJ8p6/vWCEzuylqJNMHUP/XmeYyGVWb"
- "8lnDd"
+ "ed25519": (
+ "AAAAC3NzaC1lZDI1NTE5AAAAIA1J77+CrJ8p6/vWCEzuylqJNMHUP/XmeYyGVWb8lnDd"
),
- 'ecdsa-sha2-nistp256-cert-v01@openssh.com': (
+ "ecdsa-sha2-nistp256-cert-v01@openssh.com": (
"AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAA"
"gQIfwT/+UX68/hlKsdKuaOuAVB6ftTg03SlP/uH4OBEwAAAAIbmlzdHAyNTYAAA"
"BBBEjA0gjJmPM6La3sXyfNlnjilvvGY6I2M8SvJj4o3X/46wcUbPWTaj4RF3EXw"
@@ -101,12 +102,12 @@ VALID_CONTENT = {
"2tM3QXkDcwdP0SxSEW5yy4XV5oAAAAhANNMm1cdVlAt3hmycQgdD82zPlg5YvVO"
"iN0SQTbgVD8i"
),
- 'ecdsa-sha2-nistp256': (
+ "ecdsa-sha2-nistp256": (
"AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEjA0gjJmPM"
"6La3sXyfNlnjilvvGY6I2M8SvJj4o3X/46wcUbPWTaj4RF3EXwHvNxplYBwdPlk"
"2zEecvf9Cs2BM="
),
- 'ecdsa-sha2-nistp384-cert-v01@openssh.com': (
+ "ecdsa-sha2-nistp384-cert-v01@openssh.com": (
"AAAAKGVjZHNhLXNoYTItbmlzdHAzODQtY2VydC12MDFAb3BlbnNzaC5jb20AAAA"
"grnSvDsK1EnCZndO1IyGWcGkVgVSkPWi/XO2ybPFyLVUAAAAIbmlzdHAzODQAAA"
"BhBAaYSQs+8TT0Tzciy0dorwhur6yzOGUrYQ6ueUQYWbE7eNdHmhsVrlpGPgSaY"
@@ -123,12 +124,12 @@ VALID_CONTENT = {
"RVYqYQgAAADAiit0UCMDAUbjD+R2x4LvU3x/t8G3sdqDLRNfMRpjZpvcS8AwC+Y"
"VFVSQNn0AyzW0="
),
- 'ecdsa-sha2-nistp384': (
+ "ecdsa-sha2-nistp384": (
"AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBAaYSQs+8TT"
"0Tzciy0dorwhur6yzOGUrYQ6ueUQYWbE7eNdHmhsVrlpGPgSaYByhXtAJiPOMqL"
"U5h0eb3sCtM3ek4NvjXFTGTqPrrxJI6q0OsgrtkGE7UM9ZsfMm7q6BOA=="
),
- 'ecdsa-sha2-nistp521-cert-v01@openssh.com': (
+ "ecdsa-sha2-nistp521-cert-v01@openssh.com": (
"AAAAKGVjZHNhLXNoYTItbmlzdHA1MjEtY2VydC12MDFAb3BlbnNzaC5jb20AAAA"
"gGmRzkkMvRFk1V5U3m3mQ2nfW20SJVXk1NKnT5iZGDcEAAAAIbmlzdHA1MjEAAA"
"CFBAHosAOHAI1ZkerbKYQ72S6uit1u77PCj/OalZtXgsxv0TTAZB273puG2X94C"
@@ -147,13 +148,13 @@ VALID_CONTENT = {
"AAAQgEzkIpX3yKXPaPcK17mNx40ujEDitm4ARmbhAge0sFhZtf7YIgI55b6vkI8"
"JvMJkzQCBF1cpNOaIpVh1nFZNBphMQ=="
),
- 'ecdsa-sha2-nistp521': (
+ "ecdsa-sha2-nistp521": (
"AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHosAOHAI1"
"ZkerbKYQ72S6uit1u77PCj/OalZtXgsxv0TTAZB273puG2X94CQ8yyNHcby87zF"
"ZHdv5BSKyZ/cyREAAeiAcSakop9VS3+bUfZpEIqwBZXarwUjnRnxprkcQ0rfCCd"
"agkGZr/OA7DemK2D8tKLTHsKoEEWNImo6/pXDkFxA=="
),
- 'sk-ecdsa-sha2-nistp256-cert-v01@openssh.com': (
+ "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com": (
"AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIxzuxl4z3u"
"wAIslne8Huft+1n1IhHAlNbWZkQyyECCGAAAAIFOG6kY7Rf4UtCFvPwKgo/BztX"
"ck2xC4a2WyA34XtIwZAAAAAAAAAAgAAAACAAAABmp1bGl1cwAAABIAAAAFaG9zd"
@@ -162,12 +163,12 @@ VALID_CONTENT = {
"AAFMAAAALc3NoLWVkMjU1MTkAAABABGTn+Bmz86Ajk+iqKCSdP5NClsYzn4alJd"
"0V5bizhP0Kumc/HbqQfSt684J1WdSzih+EjvnTgBhK9jTBKb90AQ=="
),
- 'sk-ecdsa-sha2-nistp256@openssh.com': (
+ "sk-ecdsa-sha2-nistp256@openssh.com": (
"AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHA"
"yNTYAAABBBIELQJ2DgvaX1yQlKFokfWM2suuaCFI2qp0eJodHyg6O4ifxc3XpRK"
"d1OS8dNYQtE/YjdXSrA+AOnMF5ns2Nkx4AAAAEc3NoOg=="
),
- 'sk-ssh-ed25519-cert-v01@openssh.com': (
+ "sk-ssh-ed25519-cert-v01@openssh.com": (
"AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIxzuxl4z3u"
"wAIslne8Huft+1n1IhHAlNbWZkQyyECCGAAAAIFOG6kY7Rf4UtCFvPwKgo/BztX"
"ck2xC4a2WyA34XtIwZAAAAAAAAAAgAAAACAAAABmp1bGl1cwAAABIAAAAFaG9zd"
@@ -176,11 +177,11 @@ VALID_CONTENT = {
"AAFMAAAALc3NoLWVkMjU1MTkAAABABGTn+Bmz86Ajk+iqKCSdP5NClsYzn4alJd"
"0V5bizhP0Kumc/HbqQfSt684J1WdSzih+EjvnTgBhK9jTBKb90AQ=="
),
- 'sk-ssh-ed25519@openssh.com': (
+ "sk-ssh-ed25519@openssh.com": (
"AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAICFo/k5LU8863u66YC9"
"eUO2170QduohPURkQnbLa/dczAAAABHNzaDo="
),
- 'ssh-dss-cert-v01@openssh.com': (
+ "ssh-dss-cert-v01@openssh.com": (
"AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgdTlbNU9Hn9Qng3F"
"HxwH971bxCIoq1ern/QWFFDWXgmYAAACBAPqS600VGwdPAQC/p3f0uGyrLVql0c"
"Fn1zYd/JGvtabKnIYjLaYprje/NcjwI3CZFJiz4Dp3S8kLs+X5/1DMn/Tg1Y4D4"
@@ -197,7 +198,7 @@ VALID_CONTENT = {
"+F7SMGQAAAFMAAAALc3NoLWVkMjU1MTkAAABAh/z1LIdNL1b66tQ8t9DY9BTB3B"
"QKpTKmc7ezyFKLwl96yaIniZwD9Ticdbe/8i/Li3uCFE3EAt8NAIv9zff8Bg=="
),
- 'ssh-dss': (
+ "ssh-dss": (
"AAAAB3NzaC1kc3MAAACBAPqS600VGwdPAQC/p3f0uGyrLVql0cFn1zYd/JGvtab"
"KnIYjLaYprje/NcjwI3CZFJiz4Dp3S8kLs+X5/1DMn/Tg1Y4D4yLB+6vCtHcJF7"
"rVBFhvw/KZwc7G54ez3khyOtsg82fzpyOc8/mq+/+C5TMKO7DDjMF0k5emWKCsa"
@@ -209,7 +210,7 @@ VALID_CONTENT = {
"GIf95LiLSgaXMjko7joot+LK84ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9"
"Ycnyhp2mSFsQt"
),
- 'ssh-ed25519-cert-v01@openssh.com': (
+ "ssh-ed25519-cert-v01@openssh.com": (
"AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIxzuxl4z3u"
"wAIslne8Huft+1n1IhHAlNbWZkQyyECCGAAAAIFOG6kY7Rf4UtCFvPwKgo/BztX"
"ck2xC4a2WyA34XtIwZAAAAAAAAAAgAAAACAAAABmp1bGl1cwAAABIAAAAFaG9zd"
@@ -218,11 +219,10 @@ VALID_CONTENT = {
"AAFMAAAALc3NoLWVkMjU1MTkAAABABGTn+Bmz86Ajk+iqKCSdP5NClsYzn4alJd"
"0V5bizhP0Kumc/HbqQfSt684J1WdSzih+EjvnTgBhK9jTBKb90AQ=="
),
- 'ssh-ed25519': (
- "AAAAC3NzaC1lZDI1NTE5AAAAIFOG6kY7Rf4UtCFvPwKgo/BztXck2xC4a2WyA34"
- "XtIwZ"
+ "ssh-ed25519": (
+ "AAAAC3NzaC1lZDI1NTE5AAAAIFOG6kY7Rf4UtCFvPwKgo/BztXck2xC4a2WyA34XtIwZ"
),
- 'ssh-rsa-cert-v01@openssh.com': (
+ "ssh-rsa-cert-v01@openssh.com": (
"AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg98LhS2EHxLOWCLo"
"pZPwHdg/RJXusnkOqQXSc9R7aITkAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGh"
"EZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yM"
@@ -233,13 +233,13 @@ VALID_CONTENT = {
"he0jBkAAABTAAAAC3NzaC1lZDI1NTE5AAAAQI3QGlUCzC07KorupxpDkkGy6tni"
"aZ8EvBflzvv+itXWNchGvfUeHmVT6aX0sRqehdz/lR+GmXRoZBhofwh0qAM="
),
- 'ssh-rsa': (
+ "ssh-rsa": (
"AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8"
"+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C3"
"1Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u"
"0rU1967pDJx+w=="
),
- 'ssh-xmss-cert-v01@openssh.com': (
+ "ssh-xmss-cert-v01@openssh.com": (
"AAAAHXNzaC14bXNzLWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIM2UD0IH+Igsekq"
"xjTO5f36exX4WGRMCtDGPjwfbXblxAAAAFVhNU1NfU0hBMi0yNTZfVzE2X0gxMA"
"AAAEDI83/K5JMOy0BMJgQypRdz35ApAnoQinMJ8ZMoZPaEJF8Z4rANQlfzaAXum"
@@ -305,7 +305,7 @@ VALID_CONTENT = {
"rNYClh8fQEQ8XuOCDpomMWu58YOTfbZNMDWs/Ou7RfCjX+VNwjPShDK9joMwWKc"
"Jy3QalZbaoWtcyyvXxR2sqhVR9F7Cmasq4="
),
- 'ssh-xmss@openssh.com': (
+ "ssh-xmss@openssh.com": (
"AAAAFHNzaC14bXNzQG9wZW5zc2guY29tAAAAFVhNU1NfU0hBMi0yNTZfVzE2X0g"
"xMAAAAECqptWnK94d+Sj2xcdTu8gz+75lawZoLSZFqC5IhbYuT/Z3oBZCim6yt+"
"HAmk6MKldl3Fg+74v4sR/SII0I0Jv/"
@@ -316,19 +316,25 @@ KEY_TYPES = list(VALID_CONTENT.keys())
TEST_OPTIONS = (
"no-port-forwarding,no-agent-forwarding,no-X11-forwarding,"
- 'command="echo \'Please login as the user \"ubuntu\" rather than the'
- 'user \"root\".\';echo;sleep 10"')
+ 'command="echo \'Please login as the user "ubuntu" rather than the'
+ 'user "root".\';echo;sleep 10"'
+)
class TestAuthKeyLineParser(test_helpers.CiTestCase):
-
def test_simple_parse(self):
# test key line with common 3 fields (keytype, base64, comment)
parser = ssh_util.AuthKeyLineParser()
for ktype in KEY_TYPES:
content = VALID_CONTENT[ktype]
- comment = 'user-%s@host' % ktype
- line = ' '.join((ktype, content, comment,))
+ comment = "user-%s@host" % ktype
+ line = " ".join(
+ (
+ ktype,
+ content,
+ comment,
+ )
+ )
key = parser.parse(line)
self.assertEqual(key.base64, content)
@@ -341,7 +347,12 @@ class TestAuthKeyLineParser(test_helpers.CiTestCase):
parser = ssh_util.AuthKeyLineParser()
for ktype in KEY_TYPES:
content = VALID_CONTENT[ktype]
- line = ' '.join((ktype, content,))
+ line = " ".join(
+ (
+ ktype,
+ content,
+ )
+ )
key = parser.parse(line)
self.assertEqual(key.base64, content)
@@ -355,8 +366,15 @@ class TestAuthKeyLineParser(test_helpers.CiTestCase):
options = TEST_OPTIONS
for ktype in KEY_TYPES:
content = VALID_CONTENT[ktype]
- comment = 'user-%s@host' % ktype
- line = ' '.join((options, ktype, content, comment,))
+ comment = "user-%s@host" % ktype
+ line = " ".join(
+ (
+ options,
+ ktype,
+ content,
+ comment,
+ )
+ )
key = parser.parse(line)
self.assertEqual(key.base64, content)
@@ -368,7 +386,7 @@ class TestAuthKeyLineParser(test_helpers.CiTestCase):
# test key line with key type and base64 only
parser = ssh_util.AuthKeyLineParser()
- baseline = ' '.join(("rsa", VALID_CONTENT['rsa'], "user@host"))
+ baseline = " ".join(("rsa", VALID_CONTENT["rsa"], "user@host"))
myopts = "no-port-forwarding,no-agent-forwarding"
key = parser.parse("allowedopt" + " " + baseline)
@@ -379,59 +397,62 @@ class TestAuthKeyLineParser(test_helpers.CiTestCase):
def test_parse_invalid_keytype(self):
parser = ssh_util.AuthKeyLineParser()
- key = parser.parse(' '.join(["badkeytype", VALID_CONTENT['rsa']]))
+ key = parser.parse(" ".join(["badkeytype", VALID_CONTENT["rsa"]]))
self.assertFalse(key.valid())
class TestUpdateAuthorizedKeys(test_helpers.CiTestCase):
-
def test_new_keys_replace(self):
"""new entries with the same base64 should replace old."""
orig_entries = [
- ' '.join(('rsa', VALID_CONTENT['rsa'], 'orig_comment1')),
- ' '.join(('dsa', VALID_CONTENT['dsa'], 'orig_comment2'))]
+ " ".join(("rsa", VALID_CONTENT["rsa"], "orig_comment1")),
+ " ".join(("dsa", VALID_CONTENT["dsa"], "orig_comment2")),
+ ]
new_entries = [
- ' '.join(('rsa', VALID_CONTENT['rsa'], 'new_comment1')), ]
+ " ".join(("rsa", VALID_CONTENT["rsa"], "new_comment1")),
+ ]
- expected = '\n'.join([new_entries[0], orig_entries[1]]) + '\n'
+ expected = "\n".join([new_entries[0], orig_entries[1]]) + "\n"
parser = ssh_util.AuthKeyLineParser()
found = ssh_util.update_authorized_keys(
[parser.parse(p) for p in orig_entries],
- [parser.parse(p) for p in new_entries])
+ [parser.parse(p) for p in new_entries],
+ )
self.assertEqual(expected, found)
def test_new_invalid_keys_are_ignored(self):
"""new entries that are invalid should be skipped."""
orig_entries = [
- ' '.join(('rsa', VALID_CONTENT['rsa'], 'orig_comment1')),
- ' '.join(('dsa', VALID_CONTENT['dsa'], 'orig_comment2'))]
+ " ".join(("rsa", VALID_CONTENT["rsa"], "orig_comment1")),
+ " ".join(("dsa", VALID_CONTENT["dsa"], "orig_comment2")),
+ ]
new_entries = [
- ' '.join(('rsa', VALID_CONTENT['rsa'], 'new_comment1')),
- 'xxx-invalid-thing1',
- 'xxx-invalid-blob2'
+ " ".join(("rsa", VALID_CONTENT["rsa"], "new_comment1")),
+ "xxx-invalid-thing1",
+ "xxx-invalid-blob2",
]
- expected = '\n'.join([new_entries[0], orig_entries[1]]) + '\n'
+ expected = "\n".join([new_entries[0], orig_entries[1]]) + "\n"
parser = ssh_util.AuthKeyLineParser()
found = ssh_util.update_authorized_keys(
[parser.parse(p) for p in orig_entries],
- [parser.parse(p) for p in new_entries])
+ [parser.parse(p) for p in new_entries],
+ )
self.assertEqual(expected, found)
class TestParseSSHConfig(test_helpers.CiTestCase):
-
def setUp(self):
- self.load_file_patch = patch('cloudinit.ssh_util.util.load_file')
+ self.load_file_patch = patch("cloudinit.ssh_util.util.load_file")
self.load_file = self.load_file_patch.start()
- self.isfile_patch = patch('cloudinit.ssh_util.os.path.isfile')
+ self.isfile_patch = patch("cloudinit.ssh_util.os.path.isfile")
self.isfile = self.isfile_patch.start()
self.isfile.return_value = True
@@ -442,60 +463,61 @@ class TestParseSSHConfig(test_helpers.CiTestCase):
def test_not_a_file(self):
self.isfile.return_value = False
self.load_file.side_effect = IOError
- ret = ssh_util.parse_ssh_config('not a real file')
+ ret = ssh_util.parse_ssh_config("not a real file")
self.assertEqual([], ret)
def test_empty_file(self):
- self.load_file.return_value = ''
- ret = ssh_util.parse_ssh_config('some real file')
+ self.load_file.return_value = ""
+ ret = ssh_util.parse_ssh_config("some real file")
self.assertEqual([], ret)
def test_comment_line(self):
- comment_line = '# This is a comment'
+ comment_line = "# This is a comment"
self.load_file.return_value = comment_line
- ret = ssh_util.parse_ssh_config('some real file')
+ ret = ssh_util.parse_ssh_config("some real file")
self.assertEqual(1, len(ret))
self.assertEqual(comment_line, ret[0].line)
def test_blank_lines(self):
- lines = ['', '\t', ' ']
- self.load_file.return_value = '\n'.join(lines)
- ret = ssh_util.parse_ssh_config('some real file')
+ lines = ["", "\t", " "]
+ self.load_file.return_value = "\n".join(lines)
+ ret = ssh_util.parse_ssh_config("some real file")
self.assertEqual(len(lines), len(ret))
for line in ret:
- self.assertEqual('', line.line)
+ self.assertEqual("", line.line)
def test_lower_case_config(self):
- self.load_file.return_value = 'foo bar'
- ret = ssh_util.parse_ssh_config('some real file')
+ self.load_file.return_value = "foo bar"
+ ret = ssh_util.parse_ssh_config("some real file")
self.assertEqual(1, len(ret))
- self.assertEqual('foo', ret[0].key)
- self.assertEqual('bar', ret[0].value)
+ self.assertEqual("foo", ret[0].key)
+ self.assertEqual("bar", ret[0].value)
def test_upper_case_config(self):
- self.load_file.return_value = 'Foo Bar'
- ret = ssh_util.parse_ssh_config('some real file')
+ self.load_file.return_value = "Foo Bar"
+ ret = ssh_util.parse_ssh_config("some real file")
self.assertEqual(1, len(ret))
- self.assertEqual('foo', ret[0].key)
- self.assertEqual('Bar', ret[0].value)
+ self.assertEqual("foo", ret[0].key)
+ self.assertEqual("Bar", ret[0].value)
def test_lower_case_with_equals(self):
- self.load_file.return_value = 'foo=bar'
- ret = ssh_util.parse_ssh_config('some real file')
+ self.load_file.return_value = "foo=bar"
+ ret = ssh_util.parse_ssh_config("some real file")
self.assertEqual(1, len(ret))
- self.assertEqual('foo', ret[0].key)
- self.assertEqual('bar', ret[0].value)
+ self.assertEqual("foo", ret[0].key)
+ self.assertEqual("bar", ret[0].value)
def test_upper_case_with_equals(self):
- self.load_file.return_value = 'Foo=bar'
- ret = ssh_util.parse_ssh_config('some real file')
+ self.load_file.return_value = "Foo=bar"
+ ret = ssh_util.parse_ssh_config("some real file")
self.assertEqual(1, len(ret))
- self.assertEqual('foo', ret[0].key)
- self.assertEqual('bar', ret[0].value)
+ self.assertEqual("foo", ret[0].key)
+ self.assertEqual("bar", ret[0].value)
class TestUpdateSshConfigLines(test_helpers.CiTestCase):
"""Test the update_ssh_config_lines method."""
+
exlines = [
"#PasswordAuthentication yes",
"UsePAM yes",
@@ -514,8 +536,8 @@ class TestUpdateSshConfigLines(test_helpers.CiTestCase):
def test_new_option_added(self):
"""A single update of non-existing option."""
lines = ssh_util.parse_ssh_config_lines(list(self.exlines))
- result = ssh_util.update_ssh_config_lines(lines, {'MyKey': 'MyVal'})
- self.assertEqual(['MyKey'], result)
+ result = ssh_util.update_ssh_config_lines(lines, {"MyKey": "MyVal"})
+ self.assertEqual(["MyKey"], result)
self.check_line(lines[-1], "MyKey", "MyVal")
def test_commented_out_not_updated_but_appended(self):
@@ -543,8 +565,12 @@ class TestUpdateSshConfigLines(test_helpers.CiTestCase):
def test_multiple_updates_with_add(self):
"""Verify multiple updates some added some changed, some not."""
- updates = {"UsePAM": "no", "X11Forwarding": "no", "NewOpt": "newval",
- "AcceptEnv": "LANG ADD LC_*"}
+ updates = {
+ "UsePAM": "no",
+ "X11Forwarding": "no",
+ "NewOpt": "newval",
+ "AcceptEnv": "LANG ADD LC_*",
+ }
lines = ssh_util.parse_ssh_config_lines(list(self.exlines))
result = ssh_util.update_ssh_config_lines(lines, updates)
self.assertEqual(set(["UsePAM", "NewOpt", "AcceptEnv"]), set(result))
@@ -569,7 +595,7 @@ class TestUpdateSshConfigLines(test_helpers.CiTestCase):
class TestUpdateSshConfig(test_helpers.CiTestCase):
- cfgdata = '\n'.join(["#Option val", "MyKey ORIG_VAL", ""])
+ cfgdata = "\n".join(["#Option val", "MyKey ORIG_VAL", ""])
def test_modified(self):
mycfg = self.tmp_path("ssh_config_1")
@@ -579,7 +605,7 @@ class TestUpdateSshConfig(test_helpers.CiTestCase):
found = util.load_file(mycfg)
self.assertEqual(self.cfgdata.replace("ORIG_VAL", "NEW_VAL"), found)
# assert there is a newline at end of file (LP: #1677205)
- self.assertEqual('\n', found[-1])
+ self.assertEqual("\n", found[-1])
def test_not_modified(self):
mycfg = self.tmp_path("ssh_config_2")
@@ -596,72 +622,100 @@ class TestBasicAuthorizedKeyParse(test_helpers.CiTestCase):
self.assertEqual(
["/opt/bobby/keys"],
ssh_util.render_authorizedkeysfile_paths(
- "/opt/%u/keys", "/home/bobby", "bobby"))
+ "/opt/%u/keys", "/home/bobby", "bobby"
+ ),
+ )
def test_user_file(self):
self.assertEqual(
["/opt/bobby"],
ssh_util.render_authorizedkeysfile_paths(
- "/opt/%u", "/home/bobby", "bobby"))
+ "/opt/%u", "/home/bobby", "bobby"
+ ),
+ )
def test_user_file2(self):
self.assertEqual(
["/opt/bobby/bobby"],
ssh_util.render_authorizedkeysfile_paths(
- "/opt/%u/%u", "/home/bobby", "bobby"))
+ "/opt/%u/%u", "/home/bobby", "bobby"
+ ),
+ )
def test_multiple(self):
self.assertEqual(
["/keys/path1", "/keys/path2"],
ssh_util.render_authorizedkeysfile_paths(
- "/keys/path1 /keys/path2", "/home/bobby", "bobby"))
+ "/keys/path1 /keys/path2", "/home/bobby", "bobby"
+ ),
+ )
def test_multiple2(self):
self.assertEqual(
["/keys/path1", "/keys/bobby"],
ssh_util.render_authorizedkeysfile_paths(
- "/keys/path1 /keys/%u", "/home/bobby", "bobby"))
+ "/keys/path1 /keys/%u", "/home/bobby", "bobby"
+ ),
+ )
def test_relative(self):
self.assertEqual(
["/home/bobby/.secret/keys"],
ssh_util.render_authorizedkeysfile_paths(
- ".secret/keys", "/home/bobby", "bobby"))
+ ".secret/keys", "/home/bobby", "bobby"
+ ),
+ )
def test_home(self):
self.assertEqual(
["/homedirs/bobby/.keys"],
ssh_util.render_authorizedkeysfile_paths(
- "%h/.keys", "/homedirs/bobby", "bobby"))
+ "%h/.keys", "/homedirs/bobby", "bobby"
+ ),
+ )
def test_all(self):
self.assertEqual(
- ["/homedirs/bobby/.keys", "/homedirs/bobby/.secret/keys",
- "/keys/path1", "/opt/bobby/keys"],
+ [
+ "/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"))
+ "/homedirs/bobby",
+ "bobby",
+ ),
+ )
class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
-
- def create_fake_users(self, names, mock_permissions,
- m_get_group, m_get_owner, m_get_permissions,
- m_getpwnam, users):
+ def create_fake_users(
+ self,
+ names,
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
+ ):
homes = []
- root = '/tmp/root'
+ root = "/tmp/root"
fpw = FakePwEnt(pw_name="root", pw_dir=root)
users["root"] = fpw
for name in names:
- home = '/tmp/home/' + name
+ home = "/tmp/home/" + name
fpw = FakePwEnt(pw_name=name, pw_dir=home)
users[name] = fpw
homes.append(home)
m_get_permissions.side_effect = partial(
- mock_get_permissions, mock_permissions)
+ mock_get_permissions, mock_permissions
+ )
m_get_owner.side_effect = partial(mock_get_owner, mock_permissions)
m_get_group.side_effect = partial(mock_get_group, mock_permissions)
m_getpwnam.side_effect = partial(mock_getpwnam, users)
@@ -676,23 +730,24 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
return authorized_keys
def create_global_authorized_file(self, filename, content_key, keys):
- authorized_keys = self.tmp_path(filename, dir='/tmp')
+ authorized_keys = self.tmp_path(filename, dir="/tmp")
util.write_file(authorized_keys, VALID_CONTENT[content_key])
keys[authorized_keys] = content_key
return authorized_keys
def create_sshd_config(self, authorized_keys_files):
- sshd_config = self.tmp_path('sshd_config', dir="/tmp")
+ sshd_config = self.tmp_path("sshd_config", dir="/tmp")
util.write_file(
- sshd_config,
- "AuthorizedKeysFile " + authorized_keys_files
+ sshd_config, "AuthorizedKeysFile " + authorized_keys_files
)
return sshd_config
- def execute_and_check(self, user, sshd_config, solution, keys,
- delete_keys=True):
+ def execute_and_check(
+ self, user, sshd_config, solution, keys, delete_keys=True
+ ):
(auth_key_fn, auth_key_entries) = ssh_util.extract_authorized_keys(
- user, sshd_config)
+ user, sshd_config
+ )
content = ssh_util.update_authorized_keys(auth_key_entries, [])
self.assertEqual(auth_key_fn, solution)
@@ -712,30 +767,35 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
def test_single_user_two_local_files(
self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
):
- user_bobby = 'bobby'
+ user_bobby = "bobby"
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/user_keys': ('bobby', 'bobby', 0o600),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/user_keys": ("bobby", "bobby", 0o600),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
}
homes = self.create_fake_users(
- [user_bobby], mock_permissions, m_get_group, m_get_owner,
- m_get_permissions, m_getpwnam, users
+ [user_bobby],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home = homes[0]
# /tmp/home/bobby/.ssh/authorized_keys = rsa
authorized_keys = self.create_user_authorized_file(
- home, 'authorized_keys', 'rsa', keys
+ home, "authorized_keys", "rsa", keys
)
# /tmp/home/bobby/.ssh/user_keys = dsa
user_keys = self.create_user_authorized_file(
- home, 'user_keys', 'dsa', keys
+ home, "user_keys", "dsa", keys
)
# /tmp/sshd_config
@@ -751,30 +811,35 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
def test_single_user_two_local_files_inverted(
self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
):
- user_bobby = 'bobby'
+ user_bobby = "bobby"
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/user_keys': ('bobby', 'bobby', 0o600),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/user_keys": ("bobby", "bobby", 0o600),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
}
homes = self.create_fake_users(
- [user_bobby], mock_permissions, m_get_group, m_get_owner,
- m_get_permissions, m_getpwnam, users
+ [user_bobby],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home = homes[0]
# /tmp/home/bobby/.ssh/authorized_keys = rsa
authorized_keys = self.create_user_authorized_file(
- home, 'authorized_keys', 'rsa', keys
+ home, "authorized_keys", "rsa", keys
)
# /tmp/home/bobby/.ssh/user_keys = dsa
user_keys = self.create_user_authorized_file(
- home, 'user_keys', 'dsa', keys
+ home, "user_keys", "dsa", keys
)
# /tmp/sshd_config
@@ -790,38 +855,46 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
def test_single_user_local_global_files(
self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
):
- user_bobby = 'bobby'
+ user_bobby = "bobby"
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/user_keys': ('bobby', 'bobby', 0o600),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/user_keys": ("bobby", "bobby", 0o600),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
}
homes = self.create_fake_users(
- [user_bobby], mock_permissions, m_get_group, m_get_owner,
- m_get_permissions, m_getpwnam, users
+ [user_bobby],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home = homes[0]
# /tmp/home/bobby/.ssh/authorized_keys = rsa
authorized_keys = self.create_user_authorized_file(
- home, 'authorized_keys', 'rsa', keys
+ home, "authorized_keys", "rsa", keys
)
# /tmp/home/bobby/.ssh/user_keys = dsa
user_keys = self.create_user_authorized_file(
- home, 'user_keys', 'dsa', keys
+ home, "user_keys", "dsa", keys
)
authorized_keys_global = self.create_global_authorized_file(
- 'etc/ssh/authorized_keys', 'ecdsa', keys
+ "etc/ssh/authorized_keys", "ecdsa", keys
)
- options = "%s %s %s" % (authorized_keys_global, user_keys,
- authorized_keys)
+ options = "%s %s %s" % (
+ authorized_keys_global,
+ user_keys,
+ authorized_keys,
+ )
sshd_config = self.create_sshd_config(options)
self.execute_and_check(user_bobby, sshd_config, user_keys, keys)
@@ -833,38 +906,46 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
def test_single_user_local_global_files_inverted(
self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
):
- user_bobby = 'bobby'
+ user_bobby = "bobby"
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/user_keys3': ('bobby', 'bobby', 0o600),
- '/tmp/home/bobby/.ssh/authorized_keys2': ('bobby', 'bobby', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/user_keys3": ("bobby", "bobby", 0o600),
+ "/tmp/home/bobby/.ssh/authorized_keys2": ("bobby", "bobby", 0o600),
}
homes = self.create_fake_users(
- [user_bobby], mock_permissions, m_get_group, m_get_owner,
- m_get_permissions, m_getpwnam, users
+ [user_bobby],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home = homes[0]
# /tmp/home/bobby/.ssh/authorized_keys = rsa
authorized_keys = self.create_user_authorized_file(
- home, 'authorized_keys2', 'rsa', keys
+ home, "authorized_keys2", "rsa", keys
)
# /tmp/home/bobby/.ssh/user_keys = dsa
user_keys = self.create_user_authorized_file(
- home, 'user_keys3', 'dsa', keys
+ home, "user_keys3", "dsa", keys
)
authorized_keys_global = self.create_global_authorized_file(
- 'etc/ssh/authorized_keys', 'ecdsa', keys
+ "etc/ssh/authorized_keys", "ecdsa", keys
)
- options = "%s %s %s" % (authorized_keys_global, authorized_keys,
- user_keys)
+ options = "%s %s %s" % (
+ authorized_keys_global,
+ authorized_keys,
+ user_keys,
+ )
sshd_config = self.create_sshd_config(options)
self.execute_and_check(user_bobby, sshd_config, authorized_keys, keys)
@@ -876,24 +957,29 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
def test_single_user_global_file(
self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam
):
- user_bobby = 'bobby'
+ user_bobby = "bobby"
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
}
homes = self.create_fake_users(
- [user_bobby], mock_permissions, m_get_group, m_get_owner,
- m_get_permissions, m_getpwnam, users
+ [user_bobby],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home = homes[0]
# /tmp/etc/ssh/authorized_keys = rsa
authorized_keys_global = self.create_global_authorized_file(
- 'etc/ssh/authorized_keys', 'rsa', keys
+ "etc/ssh/authorized_keys", "rsa", keys
)
options = "%s" % authorized_keys_global
@@ -912,31 +998,36 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
- '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh/authorized_keys': ('suzie', 'suzie', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
+ "/tmp/home/suzie": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh/authorized_keys": ("suzie", "suzie", 0o600),
}
- user_bobby = 'bobby'
- user_suzie = 'suzie'
+ user_bobby = "bobby"
+ user_suzie = "suzie"
homes = self.create_fake_users(
- [user_bobby, user_suzie], mock_permissions, m_get_group,
- m_get_owner, m_get_permissions, m_getpwnam, users
+ [user_bobby, user_suzie],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home_bobby = homes[0]
home_suzie = homes[1]
# /tmp/home/bobby/.ssh/authorized_keys = rsa
authorized_keys = self.create_user_authorized_file(
- home_bobby, 'authorized_keys', 'rsa', keys
+ home_bobby, "authorized_keys", "rsa", keys
)
# /tmp/home/suzie/.ssh/authorized_keys = rsa
authorized_keys2 = self.create_user_authorized_file(
- home_suzie, 'authorized_keys', 'ssh-xmss@openssh.com', keys
+ home_suzie, "authorized_keys", "ssh-xmss@openssh.com", keys
)
options = ".ssh/authorized_keys"
@@ -957,31 +1048,36 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys2': ('bobby', 'bobby', 0o600),
- '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh/authorized_keys2': ('suzie', 'suzie', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys2": ("bobby", "bobby", 0o600),
+ "/tmp/home/suzie": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh/authorized_keys2": ("suzie", "suzie", 0o600),
}
- user_bobby = 'bobby'
- user_suzie = 'suzie'
+ user_bobby = "bobby"
+ user_suzie = "suzie"
homes = self.create_fake_users(
- [user_bobby, user_suzie], mock_permissions, m_get_group,
- m_get_owner, m_get_permissions, m_getpwnam, users
+ [user_bobby, user_suzie],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home_bobby = homes[0]
home_suzie = homes[1]
# /tmp/home/bobby/.ssh/authorized_keys2 = rsa
authorized_keys = self.create_user_authorized_file(
- home_bobby, 'authorized_keys2', 'rsa', keys
+ home_bobby, "authorized_keys2", "rsa", keys
)
# /tmp/home/suzie/.ssh/authorized_keys2 = rsa
authorized_keys2 = self.create_user_authorized_file(
- home_suzie, 'authorized_keys2', 'ssh-xmss@openssh.com', keys
+ home_suzie, "authorized_keys2", "ssh-xmss@openssh.com", keys
)
options = ".ssh/authorized_keys2"
@@ -1002,46 +1098,53 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys2': ('bobby', 'bobby', 0o600),
- '/tmp/home/bobby/.ssh/user_keys3': ('bobby', 'bobby', 0o600),
- '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh/authorized_keys2': ('suzie', 'suzie', 0o600),
- '/tmp/home/suzie/.ssh/user_keys3': ('suzie', 'suzie', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys2": ("bobby", "bobby", 0o600),
+ "/tmp/home/bobby/.ssh/user_keys3": ("bobby", "bobby", 0o600),
+ "/tmp/home/suzie": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh/authorized_keys2": ("suzie", "suzie", 0o600),
+ "/tmp/home/suzie/.ssh/user_keys3": ("suzie", "suzie", 0o600),
}
- user_bobby = 'bobby'
- user_suzie = 'suzie'
+ user_bobby = "bobby"
+ user_suzie = "suzie"
homes = self.create_fake_users(
- [user_bobby, user_suzie], mock_permissions, m_get_group,
- m_get_owner, m_get_permissions, m_getpwnam, users
+ [user_bobby, user_suzie],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home_bobby = homes[0]
home_suzie = homes[1]
# /tmp/home/bobby/.ssh/authorized_keys2 = rsa
self.create_user_authorized_file(
- home_bobby, 'authorized_keys2', 'rsa', keys
+ home_bobby, "authorized_keys2", "rsa", keys
)
# /tmp/home/bobby/.ssh/user_keys3 = dsa
user_keys = self.create_user_authorized_file(
- home_bobby, 'user_keys3', 'dsa', keys
+ home_bobby, "user_keys3", "dsa", keys
)
# /tmp/home/suzie/.ssh/authorized_keys2 = rsa
authorized_keys2 = self.create_user_authorized_file(
- home_suzie, 'authorized_keys2', 'ssh-xmss@openssh.com', keys
+ home_suzie, "authorized_keys2", "ssh-xmss@openssh.com", keys
)
# /tmp/etc/ssh/authorized_keys = ecdsa
authorized_keys_global = self.create_global_authorized_file(
- 'etc/ssh/authorized_keys2', 'ecdsa', keys
+ "etc/ssh/authorized_keys2", "ecdsa", keys
)
- options = "%s %s %%h/.ssh/authorized_keys2" % \
- (authorized_keys_global, user_keys)
+ options = "%s %s %%h/.ssh/authorized_keys2" % (
+ authorized_keys_global,
+ user_keys,
+ )
sshd_config = self.create_sshd_config(options)
self.execute_and_check(
@@ -1055,50 +1158,62 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
@patch("cloudinit.util.get_owner")
@patch("cloudinit.util.get_group")
def test_two_users_local_global_files_badguy(
- self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
- m_get_user_groups
+ self,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ m_get_user_groups,
):
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys2': ('bobby', 'bobby', 0o600),
- '/tmp/home/bobby/.ssh/user_keys3': ('bobby', 'bobby', 0o600),
- '/tmp/home/badguy': ('root', 'root', 0o755),
- '/tmp/home/badguy/home': ('root', 'root', 0o755),
- '/tmp/home/badguy/home/bobby': ('root', 'root', 0o655),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys2": ("bobby", "bobby", 0o600),
+ "/tmp/home/bobby/.ssh/user_keys3": ("bobby", "bobby", 0o600),
+ "/tmp/home/badguy": ("root", "root", 0o755),
+ "/tmp/home/badguy/home": ("root", "root", 0o755),
+ "/tmp/home/badguy/home/bobby": ("root", "root", 0o655),
}
- user_bobby = 'bobby'
- user_badguy = 'badguy'
+ user_bobby = "bobby"
+ user_badguy = "badguy"
home_bobby, *_ = self.create_fake_users(
- [user_bobby, user_badguy], mock_permissions, m_get_group,
- m_get_owner, m_get_permissions, m_getpwnam, users
+ [user_bobby, user_badguy],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
m_get_user_groups.side_effect = mock_get_user_groups
# /tmp/home/bobby/.ssh/authorized_keys2 = rsa
authorized_keys = self.create_user_authorized_file(
- home_bobby, 'authorized_keys2', 'rsa', keys
+ home_bobby, "authorized_keys2", "rsa", keys
)
# /tmp/home/bobby/.ssh/user_keys3 = dsa
user_keys = self.create_user_authorized_file(
- home_bobby, 'user_keys3', 'dsa', keys
+ home_bobby, "user_keys3", "dsa", keys
)
# /tmp/home/badguy/home/bobby = ""
- authorized_keys2 = self.tmp_path('home/bobby', dir="/tmp/home/badguy")
- util.write_file(authorized_keys2, '')
+ authorized_keys2 = self.tmp_path("home/bobby", dir="/tmp/home/badguy")
+ util.write_file(authorized_keys2, "")
# /tmp/etc/ssh/authorized_keys = ecdsa
authorized_keys_global = self.create_global_authorized_file(
- 'etc/ssh/authorized_keys2', 'ecdsa', keys
+ "etc/ssh/authorized_keys2", "ecdsa", keys
)
# /tmp/sshd_config
- options = "%s %%h/.ssh/authorized_keys2 %s %s" % \
- (authorized_keys2, authorized_keys_global, user_keys)
+ options = "%s %%h/.ssh/authorized_keys2 %s %s" % (
+ authorized_keys2,
+ authorized_keys_global,
+ user_keys,
+ )
sshd_config = self.create_sshd_config(options)
self.execute_and_check(
@@ -1114,33 +1229,43 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
@patch("cloudinit.util.get_owner")
@patch("cloudinit.util.get_group")
def test_two_users_unaccessible_file(
- self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
- m_get_user_groups
+ self,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ m_get_user_groups,
):
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
-
- '/tmp/etc': ('root', 'root', 0o755),
- '/tmp/etc/ssh': ('root', 'root', 0o755),
- '/tmp/etc/ssh/userkeys': ('root', 'root', 0o700),
- '/tmp/etc/ssh/userkeys/bobby': ('bobby', 'bobby', 0o600),
- '/tmp/etc/ssh/userkeys/badguy': ('badguy', 'badguy', 0o600),
-
- '/tmp/home/badguy': ('badguy', 'badguy', 0o700),
- '/tmp/home/badguy/.ssh': ('badguy', 'badguy', 0o700),
- '/tmp/home/badguy/.ssh/authorized_keys':
- ('badguy', 'badguy', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
+ "/tmp/etc": ("root", "root", 0o755),
+ "/tmp/etc/ssh": ("root", "root", 0o755),
+ "/tmp/etc/ssh/userkeys": ("root", "root", 0o700),
+ "/tmp/etc/ssh/userkeys/bobby": ("bobby", "bobby", 0o600),
+ "/tmp/etc/ssh/userkeys/badguy": ("badguy", "badguy", 0o600),
+ "/tmp/home/badguy": ("badguy", "badguy", 0o700),
+ "/tmp/home/badguy/.ssh": ("badguy", "badguy", 0o700),
+ "/tmp/home/badguy/.ssh/authorized_keys": (
+ "badguy",
+ "badguy",
+ 0o600,
+ ),
}
- user_bobby = 'bobby'
- user_badguy = 'badguy'
+ user_bobby = "bobby"
+ user_badguy = "badguy"
homes = self.create_fake_users(
- [user_bobby, user_badguy], mock_permissions, m_get_group,
- m_get_owner, m_get_permissions, m_getpwnam, users
+ [user_bobby, user_badguy],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
m_get_user_groups.side_effect = mock_get_user_groups
home_bobby = homes[0]
@@ -1148,22 +1273,22 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
# /tmp/home/bobby/.ssh/authorized_keys = rsa
authorized_keys = self.create_user_authorized_file(
- home_bobby, 'authorized_keys', 'rsa', keys
+ home_bobby, "authorized_keys", "rsa", keys
)
# /tmp/etc/ssh/userkeys/bobby = dsa
# assume here that we can bypass userkeys, despite permissions
self.create_global_authorized_file(
- 'etc/ssh/userkeys/bobby', 'dsa', keys
+ "etc/ssh/userkeys/bobby", "dsa", keys
)
# /tmp/home/badguy/.ssh/authorized_keys = ssh-xmss@openssh.com
authorized_keys2 = self.create_user_authorized_file(
- home_badguy, 'authorized_keys', 'ssh-xmss@openssh.com', keys
+ home_badguy, "authorized_keys", "ssh-xmss@openssh.com", keys
)
# /tmp/etc/ssh/userkeys/badguy = ecdsa
self.create_global_authorized_file(
- 'etc/ssh/userkeys/badguy', 'ecdsa', keys
+ "etc/ssh/userkeys/badguy", "ecdsa", keys
)
# /tmp/sshd_config
@@ -1183,33 +1308,43 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
@patch("cloudinit.util.get_owner")
@patch("cloudinit.util.get_group")
def test_two_users_accessible_file(
- self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
- m_get_user_groups
+ self,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ m_get_user_groups,
):
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
-
- '/tmp/etc': ('root', 'root', 0o755),
- '/tmp/etc/ssh': ('root', 'root', 0o755),
- '/tmp/etc/ssh/userkeys': ('root', 'root', 0o755),
- '/tmp/etc/ssh/userkeys/bobby': ('bobby', 'bobby', 0o600),
- '/tmp/etc/ssh/userkeys/badguy': ('badguy', 'badguy', 0o600),
-
- '/tmp/home/badguy': ('badguy', 'badguy', 0o700),
- '/tmp/home/badguy/.ssh': ('badguy', 'badguy', 0o700),
- '/tmp/home/badguy/.ssh/authorized_keys':
- ('badguy', 'badguy', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
+ "/tmp/etc": ("root", "root", 0o755),
+ "/tmp/etc/ssh": ("root", "root", 0o755),
+ "/tmp/etc/ssh/userkeys": ("root", "root", 0o755),
+ "/tmp/etc/ssh/userkeys/bobby": ("bobby", "bobby", 0o600),
+ "/tmp/etc/ssh/userkeys/badguy": ("badguy", "badguy", 0o600),
+ "/tmp/home/badguy": ("badguy", "badguy", 0o700),
+ "/tmp/home/badguy/.ssh": ("badguy", "badguy", 0o700),
+ "/tmp/home/badguy/.ssh/authorized_keys": (
+ "badguy",
+ "badguy",
+ 0o600,
+ ),
}
- user_bobby = 'bobby'
- user_badguy = 'badguy'
+ user_bobby = "bobby"
+ user_badguy = "badguy"
homes = self.create_fake_users(
- [user_bobby, user_badguy], mock_permissions, m_get_group,
- m_get_owner, m_get_permissions, m_getpwnam, users
+ [user_bobby, user_badguy],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
m_get_user_groups.side_effect = mock_get_user_groups
home_bobby = homes[0]
@@ -1217,22 +1352,22 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
# /tmp/home/bobby/.ssh/authorized_keys = rsa
self.create_user_authorized_file(
- home_bobby, 'authorized_keys', 'rsa', keys
+ home_bobby, "authorized_keys", "rsa", keys
)
# /tmp/etc/ssh/userkeys/bobby = dsa
# assume here that we can bypass userkeys, despite permissions
authorized_keys = self.create_global_authorized_file(
- 'etc/ssh/userkeys/bobby', 'dsa', keys
+ "etc/ssh/userkeys/bobby", "dsa", keys
)
# /tmp/home/badguy/.ssh/authorized_keys = ssh-xmss@openssh.com
self.create_user_authorized_file(
- home_badguy, 'authorized_keys', 'ssh-xmss@openssh.com', keys
+ home_badguy, "authorized_keys", "ssh-xmss@openssh.com", keys
)
# /tmp/etc/ssh/userkeys/badguy = ecdsa
authorized_keys2 = self.create_global_authorized_file(
- 'etc/ssh/userkeys/badguy', 'ecdsa', keys
+ "etc/ssh/userkeys/badguy", "ecdsa", keys
)
# /tmp/sshd_config
@@ -1252,26 +1387,34 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
@patch("cloudinit.util.get_owner")
@patch("cloudinit.util.get_group")
def test_two_users_hardcoded_single_user_file(
- self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
- m_get_user_groups
+ self,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ m_get_user_groups,
):
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
-
- '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh/authorized_keys': ('suzie', 'suzie', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
+ "/tmp/home/suzie": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh/authorized_keys": ("suzie", "suzie", 0o600),
}
- user_bobby = 'bobby'
- user_suzie = 'suzie'
+ user_bobby = "bobby"
+ user_suzie = "suzie"
homes = self.create_fake_users(
- [user_bobby, user_suzie], mock_permissions, m_get_group,
- m_get_owner, m_get_permissions, m_getpwnam, users
+ [user_bobby, user_suzie],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home_bobby = homes[0]
home_suzie = homes[1]
@@ -1279,12 +1422,12 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
# /tmp/home/bobby/.ssh/authorized_keys = rsa
authorized_keys = self.create_user_authorized_file(
- home_bobby, 'authorized_keys', 'rsa', keys
+ home_bobby, "authorized_keys", "rsa", keys
)
# /tmp/home/suzie/.ssh/authorized_keys = ssh-xmss@openssh.com
self.create_user_authorized_file(
- home_suzie, 'authorized_keys', 'ssh-xmss@openssh.com', keys
+ home_suzie, "authorized_keys", "ssh-xmss@openssh.com", keys
)
# /tmp/sshd_config
@@ -1303,26 +1446,34 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
@patch("cloudinit.util.get_owner")
@patch("cloudinit.util.get_group")
def test_two_users_hardcoded_single_user_file_inverted(
- self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
- m_get_user_groups
+ self,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ m_get_user_groups,
):
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
-
- '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh/authorized_keys': ('suzie', 'suzie', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
+ "/tmp/home/suzie": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh/authorized_keys": ("suzie", "suzie", 0o600),
}
- user_bobby = 'bobby'
- user_suzie = 'suzie'
+ user_bobby = "bobby"
+ user_suzie = "suzie"
homes = self.create_fake_users(
- [user_bobby, user_suzie], mock_permissions, m_get_group,
- m_get_owner, m_get_permissions, m_getpwnam, users
+ [user_bobby, user_suzie],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home_bobby = homes[0]
home_suzie = homes[1]
@@ -1330,12 +1481,12 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
# /tmp/home/bobby/.ssh/authorized_keys = rsa
self.create_user_authorized_file(
- home_bobby, 'authorized_keys', 'rsa', keys
+ home_bobby, "authorized_keys", "rsa", keys
)
# /tmp/home/suzie/.ssh/authorized_keys = ssh-xmss@openssh.com
authorized_keys2 = self.create_user_authorized_file(
- home_suzie, 'authorized_keys', 'ssh-xmss@openssh.com', keys
+ home_suzie, "authorized_keys", "ssh-xmss@openssh.com", keys
)
# /tmp/sshd_config
@@ -1354,26 +1505,34 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
@patch("cloudinit.util.get_owner")
@patch("cloudinit.util.get_group")
def test_two_users_hardcoded_user_files(
- self, m_get_group, m_get_owner, m_get_permissions, m_getpwnam,
- m_get_user_groups
+ self,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ m_get_user_groups,
):
keys = {}
users = {}
mock_permissions = {
- '/tmp/home/bobby': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh': ('bobby', 'bobby', 0o700),
- '/tmp/home/bobby/.ssh/authorized_keys': ('bobby', 'bobby', 0o600),
-
- '/tmp/home/suzie': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh': ('suzie', 'suzie', 0o700),
- '/tmp/home/suzie/.ssh/authorized_keys': ('suzie', 'suzie', 0o600),
+ "/tmp/home/bobby": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh": ("bobby", "bobby", 0o700),
+ "/tmp/home/bobby/.ssh/authorized_keys": ("bobby", "bobby", 0o600),
+ "/tmp/home/suzie": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh": ("suzie", "suzie", 0o700),
+ "/tmp/home/suzie/.ssh/authorized_keys": ("suzie", "suzie", 0o600),
}
- user_bobby = 'bobby'
- user_suzie = 'suzie'
+ user_bobby = "bobby"
+ user_suzie = "suzie"
homes = self.create_fake_users(
- [user_bobby, user_suzie], mock_permissions, m_get_group,
- m_get_owner, m_get_permissions, m_getpwnam, users
+ [user_bobby, user_suzie],
+ mock_permissions,
+ m_get_group,
+ m_get_owner,
+ m_get_permissions,
+ m_getpwnam,
+ users,
)
home_bobby = homes[0]
home_suzie = homes[1]
@@ -1381,22 +1540,25 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
# /tmp/home/bobby/.ssh/authorized_keys = rsa
authorized_keys = self.create_user_authorized_file(
- home_bobby, 'authorized_keys', 'rsa', keys
+ home_bobby, "authorized_keys", "rsa", keys
)
# /tmp/home/suzie/.ssh/authorized_keys = ssh-xmss@openssh.com
authorized_keys2 = self.create_user_authorized_file(
- home_suzie, 'authorized_keys', 'ssh-xmss@openssh.com', keys
+ home_suzie, "authorized_keys", "ssh-xmss@openssh.com", keys
)
# /tmp/etc/ssh/authorized_keys = ecdsa
authorized_keys_global = self.create_global_authorized_file(
- 'etc/ssh/authorized_keys', 'ecdsa', keys
+ "etc/ssh/authorized_keys", "ecdsa", keys
)
# /tmp/sshd_config
- options = "%s %s %s" % \
- (authorized_keys_global, authorized_keys, authorized_keys2)
+ options = "%s %s %s" % (
+ authorized_keys_global,
+ authorized_keys,
+ authorized_keys2,
+ )
sshd_config = self.create_sshd_config(options)
self.execute_and_check(
@@ -1404,4 +1566,5 @@ class TestMultipleSshAuthorizedKeysFile(test_helpers.CiTestCase):
)
self.execute_and_check(user_suzie, sshd_config, authorized_keys2, keys)
+
# vi: ts=4 expandtab