salt/tests/pytests/integration/cli/test_salt_key.py

318 lines
9.1 KiB
Python

import ast
import os
import re
import textwrap
import pytest
import salt.utils.files
import salt.utils.platform
import salt.utils.pycrypto
import salt.utils.yaml
from saltfactories.utils import random_string
pytestmark = [
pytest.mark.slow_test,
pytest.mark.windows_whitelisted,
]
USERA = "saltdev-key"
USERA_PWD = "saltdev"
PUB_KEY = textwrap.dedent(
"""\
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoqIZDtcQtqUNs0wC7qQz
JwFhXAVNT5C8M8zhI+pFtF/63KoN5k1WwAqP2j3LquTG68WpxcBwLtKfd7FVA/Kr
OF3kXDWFnDi+HDchW2lJObgfzLckWNRFaF8SBvFM2dys3CGSgCV0S/qxnRAjrJQb
B3uQwtZ64ncJAlkYpArv3GwsfRJ5UUQnYPDEJwGzMskZ0pHd60WwM1gMlfYmNX5O
RBEjybyNpYDzpda6e6Ypsn6ePGLkP/tuwUf+q9wpbRE3ZwqERC2XRPux+HX2rGP+
mkzpmuHkyi2wV33A9pDfMgRHdln2CLX0KgfRGixUQhW1o+Kmfv2rq4sGwpCgLbTh
NwIDAQAB
-----END PUBLIC KEY-----
"""
)
@pytest.fixture(scope="module")
def saltdev_account(sminion):
try:
assert sminion.functions.user.add(USERA, createhome=False)
assert sminion.functions.shadow.set_password(
USERA,
USERA_PWD
if salt.utils.platform.is_darwin()
else salt.utils.pycrypto.gen_hash(password=USERA_PWD),
)
assert USERA in sminion.functions.user.list_users()
# Run tests
yield
finally:
sminion.functions.user.delete(USERA, remove=True)
def test_remove_key(salt_master, salt_key_cli):
"""
test salt-key -d usage
"""
min_name = random_string("minibar-")
pki_dir = salt_master.config["pki_dir"]
key = os.path.join(pki_dir, "minions", min_name)
with salt.utils.files.fopen(key, "w") as fp:
fp.write(PUB_KEY)
try:
# Check Key
ret = salt_key_cli.run("-p", min_name)
assert ret.exitcode == 0
assert "minions" in ret.json
assert min_name in ret.json["minions"]
assert "-----BEGIN PUBLIC KEY-----" in ret.json["minions"][min_name]
# Remove Key
ret = salt_key_cli.run("-d", min_name, "-y")
assert ret.exitcode == 0
# We can't load JSON because we print to stdout!
# >>>>> STDOUT >>>>>
# The following keys are going to be deleted:
# {
# "minions": [
# "minibar"
# ]
# }
# Key for minion minibar deleted.
# <<<<< STDOUT <<<<<
assert "minions" in ret.stdout
assert min_name in ret.stdout
# Check Key
ret = salt_key_cli.run("-p", min_name)
assert ret.exitcode == 0
assert ret.json == {}
finally:
if os.path.exists(key):
os.unlink(key)
@pytest.mark.skip_if_not_root
@pytest.mark.destructive_test
@pytest.mark.skip_on_windows(reason="PAM is not supported on Windows")
def test_remove_key_eauth(salt_key_cli, salt_master, saltdev_account):
"""
test salt-key -d usage
"""
min_name = random_string("minibar-")
pki_dir = salt_master.config["pki_dir"]
key = os.path.join(pki_dir, "minions", min_name)
with salt.utils.files.fopen(key, "w") as fp:
fp.write(PUB_KEY)
try:
# Check Key
ret = salt_key_cli.run("-p", min_name)
assert ret.exitcode == 0
assert "minions" in ret.json
assert min_name in ret.json["minions"]
assert "-----BEGIN PUBLIC KEY-----" in ret.json["minions"][min_name]
# Remove Key
ret = salt_key_cli.run(
"-d",
min_name,
"-y",
"--eauth",
"pam",
"--username",
USERA,
"--password",
USERA_PWD,
)
assert ret.exitcode == 0
# We can't load JSON because we print to stdout!
# >>>>> STDOUT >>>>>
# The following keys are going to be deleted:
# {
# "minions": [
# "minibar"
# ]
# }
# Key for minion minibar deleted.
# <<<<< STDOUT <<<<<
assert "minions" in ret.stdout
assert min_name in ret.stdout
# Check Key
ret = salt_key_cli.run("-p", min_name)
assert ret.exitcode == 0
assert ret.json == {}
finally:
if os.path.exists(key):
os.unlink(key)
@pytest.mark.parametrize("key_type", ("acc", "pre", "den", "un", "rej"))
def test_list_accepted_args(salt_key_cli, key_type):
"""
test salt-key -l for accepted arguments
"""
# Should not trigger any error
ret = salt_key_cli.run("-l", key_type)
assert ret.exitcode == 0
assert "error:" not in ret.stdout
# Should throw an error now
ret = salt_key_cli.run("-l", "foo-{}".format(key_type))
assert ret.exitcode != 0
assert "error:" in ret.stderr
def test_list_all(salt_key_cli, salt_minion, salt_sub_minion):
"""
test salt-key -L
"""
ret = salt_key_cli.run("-L")
assert ret.exitcode == 0
expected = {
"minions_rejected": [],
"minions_denied": [],
"minions_pre": [],
"minions": [salt_minion.id, salt_sub_minion.id],
}
assert ret.json == expected
def test_list_all_yaml_out(salt_key_cli, salt_minion, salt_sub_minion):
"""
test salt-key -L --out=yaml
"""
ret = salt_key_cli.run("-L", "--out=yaml")
assert ret.exitcode == 0
output = salt.utils.yaml.safe_load(ret.stdout)
expected = {
"minions_rejected": [],
"minions_denied": [],
"minions_pre": [],
"minions": [salt_minion.id, salt_sub_minion.id],
}
assert output == expected
def test_list_all_raw_out(salt_key_cli, salt_minion, salt_sub_minion):
"""
test salt-key -L --out=raw
"""
ret = salt_key_cli.run("-L", "--out=raw")
assert ret.exitcode == 0
output = ast.literal_eval(ret.stdout)
expected = {
"minions_rejected": [],
"minions_denied": [],
"minions_pre": [],
"minions": [salt_minion.id, salt_sub_minion.id],
}
assert output == expected
def test_list_acc(salt_key_cli, salt_minion, salt_sub_minion):
"""
test salt-key -l acc
"""
ret = salt_key_cli.run("-l", "acc")
assert ret.exitcode == 0
expected = {"minions": [salt_minion.id, salt_sub_minion.id]}
assert ret.json == expected
@pytest.mark.skip_if_not_root
@pytest.mark.destructive_test
@pytest.mark.skip_on_windows(reason="PAM is not supported on Windows")
def test_list_acc_eauth(salt_key_cli, saltdev_account, salt_minion, salt_sub_minion):
"""
test salt-key -l with eauth
"""
ret = salt_key_cli.run(
"-l", "acc", "--eauth", "pam", "--username", USERA, "--password", USERA_PWD
)
assert ret.exitcode == 0
expected = {"minions": [salt_minion.id, salt_sub_minion.id]}
assert ret.json == expected
@pytest.mark.skip_if_not_root
@pytest.mark.destructive_test
@pytest.mark.skip_on_windows(reason="PAM is not supported on Windows")
def test_list_acc_eauth_bad_creds(salt_key_cli, saltdev_account):
"""
test salt-key -l with eauth and bad creds
"""
ret = salt_key_cli.run(
"-l",
"acc",
"--eauth",
"pam",
"--username",
USERA,
"--password",
"wrongpassword",
)
assert (
ret.stdout
== 'Authentication failure of type "eauth" occurred for user {}.'.format(USERA)
)
def test_list_acc_wrong_eauth(salt_key_cli):
"""
test salt-key -l with wrong eauth
"""
ret = salt_key_cli.run(
"-l",
"acc",
"--eauth",
"wrongeauth",
"--username",
USERA,
"--password",
USERA_PWD,
)
assert ret.exitcode == 0, ret
assert re.search(
r"^The specified external authentication system \"wrongeauth\" is not available\nAvailable eauth types: auto, .*",
ret.stdout.replace("\r\n", "\n"),
)
def test_list_un(salt_key_cli):
"""
test salt-key -l un
"""
ret = salt_key_cli.run("-l", "un")
assert ret.exitcode == 0
expected = {"minions_pre": []}
assert ret.json == expected
def test_keys_generation(salt_key_cli):
with pytest.helpers.temp_directory() as tempdir:
ret = salt_key_cli.run("--gen-keys", "minibar", "--gen-keys-dir", tempdir)
assert ret.exitcode == 0
try:
key_names = ("minibar.pub", "minibar.pem")
for fname in key_names:
assert os.path.isfile(os.path.join(tempdir, fname))
finally:
for filename in os.listdir(tempdir):
os.chmod(os.path.join(tempdir, filename), 0o700)
def test_keys_generation_keysize_min(salt_key_cli):
with pytest.helpers.temp_directory() as tempdir:
ret = salt_key_cli.run(
"--gen-keys", "minibar", "--gen-keys-dir", tempdir, "--keysize", "1024"
)
assert ret.exitcode != 0
assert "error: The minimum value for keysize is 2048" in ret.stderr
def test_keys_generation_keysize_max(salt_key_cli):
with pytest.helpers.temp_directory() as tempdir:
ret = salt_key_cli.run(
"--gen-keys", "minibar", "--gen-keys-dir", tempdir, "--keysize", "32769"
)
assert ret.exitcode != 0
assert "error: The maximum value for keysize is 32768" in ret.stderr