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
|
required: false
|
||||||
default: null
|
default: null
|
||||||
version_added: "1.4"
|
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:
|
description:
|
||||||
- "Adds or removes authorized keys for particular user accounts"
|
- "Adds or removes authorized keys for particular user accounts"
|
||||||
author: Brad Olson
|
author: Brad Olson
|
||||||
|
@ -101,6 +110,9 @@ EXAMPLES = '''
|
||||||
key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
|
key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
|
||||||
key_options='no-port-forwarding,host="10.0.1.1"'
|
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.
|
# 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)
|
manage_dir = params.get("manage_dir", True)
|
||||||
state = params.get("state", "present")
|
state = params.get("state", "present")
|
||||||
key_options = params.get("key_options", None)
|
key_options = params.get("key_options", None)
|
||||||
|
exclusive = params.get("exclusive", False)
|
||||||
error_msg = "Error getting key from: %s"
|
error_msg = "Error getting key from: %s"
|
||||||
|
|
||||||
# if the key is a url, request it and use it as key source
|
# 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)
|
params["keyfile"] = keyfile(module, user, do_write, path, manage_dir)
|
||||||
existing_keys = readkeys(module, params["keyfile"])
|
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.
|
# Check our new keys, if any of them exist we'll continue.
|
||||||
for new_key in key:
|
for new_key in key:
|
||||||
parsed_new_key = parsekey(module, new_key)
|
parsed_new_key = parsekey(module, new_key)
|
||||||
|
@ -386,6 +403,7 @@ def enforce_state(module, params):
|
||||||
|
|
||||||
# handle idempotent state=present
|
# handle idempotent state=present
|
||||||
if state=="present":
|
if state=="present":
|
||||||
|
keys_to_exist.append(parsed_new_key[0])
|
||||||
if len(non_matching_keys) > 0:
|
if len(non_matching_keys) > 0:
|
||||||
for non_matching_key in non_matching_keys:
|
for non_matching_key in non_matching_keys:
|
||||||
if non_matching_key[0] in existing_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]]
|
del existing_keys[parsed_new_key[0]]
|
||||||
do_write = True
|
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 do_write:
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True)
|
module.exit_json(changed=True)
|
||||||
|
@ -424,6 +449,7 @@ def main():
|
||||||
state = dict(default='present', choices=['absent','present']),
|
state = dict(default='present', choices=['absent','present']),
|
||||||
key_options = dict(required=False, type='str'),
|
key_options = dict(required=False, type='str'),
|
||||||
unique = dict(default=False, type='bool'),
|
unique = dict(default=False, type='bool'),
|
||||||
|
exclusive = dict(default=False, type='bool'),
|
||||||
),
|
),
|
||||||
supports_check_mode=True
|
supports_check_mode=True
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue