Add exclusive option to authorized_keys
This option allows the module to ensure that ONLY the specified keys exist in the authorized_keys file. All others will be removed. This is quite useful when rotating keys and ensuring no other key will be accepted.
This commit is contained in:
parent
095f8681db
commit
e0c5b4340d
1 changed files with 26 additions and 0 deletions
|
@ -70,6 +70,15 @@ options:
|
|||
required: false
|
||||
default: null
|
||||
version_added: "1.4"
|
||||
exclusive:
|
||||
description:
|
||||
- Whether to remove all other non-specified keys from the
|
||||
authorized_keys file. Multiple keys can be specified in a single
|
||||
key= string value by separating them by newlines.
|
||||
required: false
|
||||
choices: [ yes", "no" ]
|
||||
default: "no"
|
||||
version_added: "1.9"
|
||||
description:
|
||||
- "Adds or removes authorized keys for particular user accounts"
|
||||
author: Brad Olson
|
||||
|
@ -101,6 +110,9 @@ EXAMPLES = '''
|
|||
key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
|
||||
key_options='no-port-forwarding,host="10.0.1.1"'
|
||||
|
||||
# Set up authorized_keys exclusively with one key
|
||||
- authorized_keys: user=root key=public_keys/doe-jane state=present
|
||||
exclusive=yes
|
||||
'''
|
||||
|
||||
# Makes sure the public key line is present or absent in the user's .ssh/authorized_keys.
|
||||
|
@ -336,6 +348,7 @@ def enforce_state(module, params):
|
|||
manage_dir = params.get("manage_dir", True)
|
||||
state = params.get("state", "present")
|
||||
key_options = params.get("key_options", None)
|
||||
exclusive = params.get("exclusive", False)
|
||||
error_msg = "Error getting key from: %s"
|
||||
|
||||
# if the key is a url, request it and use it as key source
|
||||
|
@ -357,6 +370,10 @@ def enforce_state(module, params):
|
|||
params["keyfile"] = keyfile(module, user, do_write, path, manage_dir)
|
||||
existing_keys = readkeys(module, params["keyfile"])
|
||||
|
||||
# Add a place holder for keys that should exist in the state=present and
|
||||
# exclusive=true case
|
||||
keys_to_exist = []
|
||||
|
||||
# Check our new keys, if any of them exist we'll continue.
|
||||
for new_key in key:
|
||||
parsed_new_key = parsekey(module, new_key)
|
||||
|
@ -386,6 +403,7 @@ def enforce_state(module, params):
|
|||
|
||||
# handle idempotent state=present
|
||||
if state=="present":
|
||||
keys_to_exist.append(parsed_new_key[0])
|
||||
if len(non_matching_keys) > 0:
|
||||
for non_matching_key in non_matching_keys:
|
||||
if non_matching_key[0] in existing_keys:
|
||||
|
@ -402,6 +420,13 @@ def enforce_state(module, params):
|
|||
del existing_keys[parsed_new_key[0]]
|
||||
do_write = True
|
||||
|
||||
# remove all other keys to honor exclusive
|
||||
if state == "present" and exclusive:
|
||||
to_remove = frozenset(existing_keys).difference(keys_to_exist)
|
||||
for key in to_remove:
|
||||
del existing_keys[key]
|
||||
do_write = True
|
||||
|
||||
if do_write:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True)
|
||||
|
@ -424,6 +449,7 @@ def main():
|
|||
state = dict(default='present', choices=['absent','present']),
|
||||
key_options = dict(required=False, type='str'),
|
||||
unique = dict(default=False, type='bool'),
|
||||
exclusive = dict(default=False, type='bool'),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue