diff --git a/src/pyinfra/operations/server.py b/src/pyinfra/operations/server.py index e832e6b96..09a5d66c7 100644 --- a/src/pyinfra/operations/server.py +++ b/src/pyinfra/operations/server.py @@ -949,6 +949,18 @@ def read_any_pub_key_file(key): public_keys = [key for key_or_file in public_keys for key in read_any_pub_key_file(key_or_file)] + # follow symlinks to the actual authorized_keys location: + while True: + auth_key_link = host.get_fact(Link, f"{authorized_key_directory}/{authorized_key_filename}") + if not auth_key_link: + break # we're targeting the final file + link_target = auth_key_link["link_target"] + authorized_key_filename = os.path.basename(link_target) + if "/" == str(link_target)[0]: + authorized_key_directory = os.path.dirname(link_target) + else: + authorized_key_directory += "/" + os.path.dirname(link_target) + # Ensure .ssh directory # note that this always outputs commands unless the SSH user has access to the # authorized_keys file, ie the SSH user is the user defined in this function diff --git a/tests/operations/server.user/key_files.json b/tests/operations/server.user/key_files.json index 38b2b52fc..0b2c4c301 100644 --- a/tests/operations/server.user/key_files.json +++ b/tests/operations/server.user/key_files.json @@ -12,6 +12,9 @@ "dirs": {} }, "facts": { + "files.Link": { + "path=homedir/.ssh/authorized_keys": false + }, "server.Os": "Linux", "server.Users": { "someuser": { diff --git a/tests/operations/server.user/keys.json b/tests/operations/server.user/keys.json index ed3624a56..a314e8f4c 100644 --- a/tests/operations/server.user/keys.json +++ b/tests/operations/server.user/keys.json @@ -5,6 +5,9 @@ "public_keys": ["abc"] }, "facts": { + "files.Link": { + "path=homedir/.ssh/authorized_keys": false + }, "server.Os": "Linux", "server.Users": { "someuser": { diff --git a/tests/operations/server.user/keys_delete.json b/tests/operations/server.user/keys_delete.json index a625dc82d..e2488d5f9 100644 --- a/tests/operations/server.user/keys_delete.json +++ b/tests/operations/server.user/keys_delete.json @@ -6,6 +6,9 @@ "delete_keys": true }, "facts": { + "files.Link": { + "path=homedir/.ssh/authorized_keys": false + }, "server.Os": "Linux", "server.Users": { "someuser": { diff --git a/tests/operations/server.user/keys_nohome.json b/tests/operations/server.user/keys_nohome.json index bdc5aea56..82b1f559a 100644 --- a/tests/operations/server.user/keys_nohome.json +++ b/tests/operations/server.user/keys_nohome.json @@ -4,6 +4,9 @@ "public_keys": ["abc"] }, "facts": { + "files.Link": { + "path=/root/.ssh/authorized_keys": false + }, "server.Os": "Linux", "server.Users": { "root": { diff --git a/tests/operations/server.user/keys_single.json b/tests/operations/server.user/keys_single.json index e921ef495..58b01eede 100644 --- a/tests/operations/server.user/keys_single.json +++ b/tests/operations/server.user/keys_single.json @@ -5,6 +5,9 @@ "public_keys": "abc" }, "facts": { + "files.Link": { + "path=homedir/.ssh/authorized_keys": false + }, "server.Os": "Linux", "server.Users": { "someuser": { diff --git a/tests/operations/server.user_authorized_keys/all_keys_present.json b/tests/operations/server.user_authorized_keys/all_keys_present.json index a66910ba4..1efbfd3de 100644 --- a/tests/operations/server.user_authorized_keys/all_keys_present.json +++ b/tests/operations/server.user_authorized_keys/all_keys_present.json @@ -4,6 +4,9 @@ "public_keys": ["ssh-ed25519 AAAAkey1 alice", "ssh-rsa AAAAkey2 bob"] }, "facts": { + "files.Link": { + "path=/home/someuser/.ssh/authorized_keys": false + }, "server.Home": { "user=someuser": "/home/someuser" }, diff --git a/tests/operations/server.user_authorized_keys/append_missing_only.json b/tests/operations/server.user_authorized_keys/append_missing_only.json index 838557603..d0b3eae24 100644 --- a/tests/operations/server.user_authorized_keys/append_missing_only.json +++ b/tests/operations/server.user_authorized_keys/append_missing_only.json @@ -4,6 +4,9 @@ "public_keys": ["ssh-ed25519 AAAAkey1 alice", "ssh-rsa AAAAkey2 bob"] }, "facts": { + "files.Link": { + "path=/home/someuser/.ssh/authorized_keys": false + }, "server.Home": { "user=someuser": "/home/someuser" }, diff --git a/tests/operations/server.user_authorized_keys/delete_match.json b/tests/operations/server.user_authorized_keys/delete_match.json index 833f48d26..4100eb078 100644 --- a/tests/operations/server.user_authorized_keys/delete_match.json +++ b/tests/operations/server.user_authorized_keys/delete_match.json @@ -5,6 +5,9 @@ "delete_keys": true }, "facts": { + "files.Link": { + "path=/home/someuser/.ssh/authorized_keys": false + }, "server.Home": { "user=someuser": "/home/someuser" }, diff --git a/tests/operations/server.user_authorized_keys/delete_mismatch.json b/tests/operations/server.user_authorized_keys/delete_mismatch.json index 12c3cb5dd..6acbf81ae 100644 --- a/tests/operations/server.user_authorized_keys/delete_mismatch.json +++ b/tests/operations/server.user_authorized_keys/delete_mismatch.json @@ -5,6 +5,9 @@ "delete_keys": true }, "facts": { + "files.Link": { + "path=/home/someuser/.ssh/authorized_keys": false + }, "server.Home": { "user=someuser": "/home/someuser" }, diff --git a/tests/operations/server.user_authorized_keys/follow_symlinks.json b/tests/operations/server.user_authorized_keys/follow_symlinks.json new file mode 100644 index 000000000..868c215b5 --- /dev/null +++ b/tests/operations/server.user_authorized_keys/follow_symlinks.json @@ -0,0 +1,50 @@ +{ + "args": ["someuser"], + "kwargs": { + "public_keys": ["ssh-ed25519 AAAAkey1 alice", "ssh-rsa AAAAkey2 bob"] + }, + "facts": { + "server.Home": { + "user=someuser": "/home/someuser" + }, + "files.Directory": { + "path=/home/someuser/.other_secret_place": { + "user": "someuser", + "group": "someuser", + "mode": 700 + }, + "path=/home/someuser/.ssh": { + "user": "someuser", + "group": "someuser", + "mode": 700 + } + }, + "files.File": { + "path=/home/someuser/.other_secret_place/auth_keys": { + "user": "someuser", + "group": "someuser", + "mode": 600 + } + }, + "files.Link": { + "path=/home/someuser/.ssh/authorized_keys": { + "link_target": "../.another_symlink" + }, + "path=/home/someuser/.ssh/../.another_symlink": { + "link_target": "/home/someuser/.other_secret_place/auth_keys" + }, + "path=/home/someuser/.other_secret_place/auth_keys": false + }, + "server.AuthorizedKeys": { + "path=/home/someuser/.other_secret_place/auth_keys, user=someuser": [ + "ssh-ed25519 AAAAkey1 alice" + ] + }, + "files.FindInFile": { + "extended_regex=False, interpolate_variables=False, path=/home/someuser/.other_secret_place/auth_keys, pattern=^.*ssh-rsa AAAAkey2 bob.*$": [] + } + }, + "commands": [ + "( [ $(tail -c1 /home/someuser/.other_secret_place/auth_keys | wc -l) -eq 0 ] && echo ; echo 'ssh-rsa AAAAkey2 bob' ) >> /home/someuser/.other_secret_place/auth_keys" + ] +}