Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[master] Fixing ssh auth options #66332

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/60769.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add logic for options handling when using source with ssh_auth
134 changes: 87 additions & 47 deletions salt/states/ssh_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,46 @@ def _present_test(
"""
result = None
if source:
keys = __salt__["ssh.check_key_file"](
user,
source,
config,
saltenv=__env__,
fingerprint_hash_type=fingerprint_hash_type,
)
if keys:
comment = ""
for key, status in keys.items():
if options:
key = __salt__["cp.get_file_str"](source, saltenv=__env__)
key = key.rstrip().split("\n")
for keyline in key:
comment = ""
keyline = keyline.split(" ")
key_type = keyline[0]
key_value = keyline[1]
key_comment = keyline[2] if len(keyline) > 2 else ""
status = __salt__["ssh.check_key"](
user,
key_value,
enc=key_type,
comment=key_comment,
options=options,
config=config,
cache_keys=None,
fingerprint_hash_type=fingerprint_hash_type,
)
if status == "exists":
continue
comment += f"Set to {status}: {key}\n"
if comment:
return result, comment
comment += f"Set to {status}: {','.join(options)} {key_value}\n"
if comment:
return result, comment
else:
keys = __salt__["ssh.check_key_file"](
user,
source,
config,
saltenv=__env__,
fingerprint_hash_type=fingerprint_hash_type,
)
if keys:
comment = ""
for key, status in list(keys.items()):
if status == "exists":
continue
comment += f"Set to {status}: {key}\n"
if comment:
return result, comment
err = sys.modules[__salt__["test.ping"].__module__].__context__.pop(
"ssh_auth.error", None
)
Expand Down Expand Up @@ -132,9 +157,7 @@ def _present_test(
comment = f"Key {name} for user {user} is set to be added"
elif check == "exists":
result = True
comment = "The authorized host key {} is already present for user {}".format(
name, user
)
comment = f"The authorized host key {name} is already present for user {user}"

return result, comment

Expand All @@ -147,21 +170,46 @@ def _absent_test(
"""
result = None
if source:
keys = __salt__["ssh.check_key_file"](
user,
source,
config,
saltenv=__env__,
fingerprint_hash_type=fingerprint_hash_type,
)
if keys:
comment = ""
for key, status in list(keys.items()):
if options:
key = __salt__["cp.get_file_str"](source, saltenv=__env__)
key = key.rstrip().split("\n")
for keyline in key:
comment = ""
keyline = keyline.split(" ")
key_type = keyline[0]
key_value = keyline[1]
key_comment = keyline[2] if len(keyline) > 2 else ""
status = __salt__["ssh.check_key"](
user,
key_value,
enc=key_type,
comment=key_comment,
options=options,
config=config,
cache_keys=None,
fingerprint_hash_type=fingerprint_hash_type,
)
if status == "add":
continue
comment += f"Set to remove: {key}\n"
if comment:
return result, comment
comment += f"Set to remove: {','.join(options)} {key_value}\n"
if comment:
return result, comment
else:
keys = __salt__["ssh.check_key_file"](
user,
source,
config,
saltenv=__env__,
fingerprint_hash_type=fingerprint_hash_type,
)
if keys:
comment = ""
for key, status in list(keys.items()):
if status == "add":
continue
comment += f"Set to remove: {key}\n"
if comment:
return result, comment
err = sys.modules[__salt__["test.ping"].__module__].__context__.pop(
"ssh_auth.error", None
)
Expand Down Expand Up @@ -308,13 +356,13 @@ def present(
data = "no key"
elif source != "" and source_path:
key = __salt__["cp.get_file_str"](source, saltenv=__env__)
filehasoptions = False
filewithoutoptions = False
# check if this is of form {options} {enc} {key} {comment}
sshre = re.compile(r"^(sk-)?(ssh\-|ecds).*")
key = key.rstrip().split("\n")
for keyline in key:
filehasoptions = sshre.match(keyline)
if not filehasoptions:
filewithoutoptions = sshre.match(keyline)
if filewithoutoptions and not options:
data = __salt__["ssh.set_auth_key_from_file"](
user,
source,
Expand Down Expand Up @@ -350,26 +398,18 @@ def present(

if data == "replace":
ret["changes"][name] = "Updated"
ret["comment"] = "The authorized host key {} for user {} was updated".format(
name, user
)
ret["comment"] = f"The authorized host key {name} for user {user} was updated"
return ret
elif data == "no change":
ret["comment"] = (
"The authorized host key {} is already present for user {}".format(
name, user
)
f"The authorized host key {name} is already present for user {user}"
)
elif data == "new":
ret["changes"][name] = "New"
ret["comment"] = "The authorized host key {} for user {} was added".format(
name, user
)
ret["comment"] = f"The authorized host key {name} for user {user} was added"
elif data == "no key":
ret["result"] = False
ret["comment"] = "Failed to add the ssh key. Source file {} is missing".format(
source
)
ret["comment"] = f"Failed to add the ssh key. Source file {source} is missing"
elif data == "fail":
ret["result"] = False
err = sys.modules[__salt__["test.ping"].__module__].__context__.pop(
Expand Down Expand Up @@ -457,13 +497,13 @@ def absent(
# Extract Key from file if source is present
if source != "":
key = __salt__["cp.get_file_str"](source, saltenv=__env__)
filehasoptions = False
filewithoutoptions = False
# check if this is of form {options} {enc} {key} {comment}
sshre = re.compile(r"^(sk-)?(ssh\-|ecds).*")
key = key.rstrip().split("\n")
for keyline in key:
filehasoptions = sshre.match(keyline)
if not filehasoptions:
filewithoutoptions = sshre.match(keyline)
if filewithoutoptions and not options:
ret["comment"] = __salt__["ssh.rm_auth_key_from_file"](
user,
source,
Expand Down
53 changes: 53 additions & 0 deletions tests/integration/states/test_ssh_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Test the ssh_auth states
"""

import logging
import os

import pytest
Expand All @@ -12,6 +13,9 @@
from tests.support.mixins import SaltReturnAssertsMixin
from tests.support.runtests import RUNTIME_VARS

# Setup logging
log = logging.getLogger(__name__)


class SSHAuthStateTests(ModuleCase, SaltReturnAssertsMixin):
@pytest.mark.destructive_test
Expand Down Expand Up @@ -123,3 +127,52 @@ def test_issue_10198_keyfile_from_another_env(self, username=None):
self.assertSaltTrueReturn(ret)
with salt.utils.files.fopen(authorized_keys_file, "r") as fhr:
self.assertEqual(fhr.read(), key_contents)

@pytest.mark.destructive_test
@with_system_user("issue_60769", on_existing="delete", delete=True)
@pytest.mark.slow_test
@pytest.mark.skip_if_not_root
def test_issue_60769_behavior_using_source_and_options(self, username=None):
userdetails = self.run_function("user.info", [username])
user_ssh_dir = os.path.join(userdetails["home"], ".ssh")
authorized_keys_file = os.path.join(user_ssh_dir, "authorized_keys")
pub_key_file = "issue_60769.id_rsa.pub"

# create a prepared authorized_keys file with option
try:
os.mkdir(user_ssh_dir)
except FileExistsError:
log.debug("folder %s already exists", user_ssh_dir)
with salt.utils.files.fopen(authorized_keys_file, "w") as authf:
authf.write(
"no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC3dd5ACsvJhnIOrn6bSOkX5"
"KyVDpTYsVAaJj3AmEo6Fr5cHXJFJoJS+Ld8K5vCscPzuXashdYUdrhL1E5Liz"
"bza+zneQ5AkJ7sn2NXymD6Bbra+infO4NgnQXbGMp/NyY65jbQGqJeQ081iEV"
f"YbDP2zXp6fmrqqmFCaakZfGRbVw== {username}\n"
)

# define a public key file to be used as source argument
with salt.utils.files.fopen(
os.path.join(RUNTIME_VARS.TMP_STATE_TREE, pub_key_file), "w"
) as pubf:
pubf.write(
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC3dd5ACsvJhnIOrn6bSOkX5"
"KyVDpTYsVAaJj3AmEo6Fr5cHXJFJoJS+Ld8K5vCscPzuXashdYUdrhL1E5Liz"
"bza+zneQ5AkJ7sn2NXymD6Bbra+infO4NgnQXbGMp/NyY65jbQGqJeQ081iEV"
f"YbDP2zXp6fmrqqmFCaakZfGRbVw== {username}\n"
)

ret = self.run_state(
"ssh_auth.present",
name="Setup Keys With Options",
source=f"salt://{pub_key_file}",
enc="ssh-rsa",
options=["no-pty"],
user=username,
test=True,
)
self.assertSaltTrueReturn(ret)
self.assertInSaltComment(
"All host keys in file salt://issue_60769.id_rsa.pub are already present",
ret,
)
Loading