Extend authorized_keys module for use with alternate AuthorizedKeysFile configurations

This commit is contained in:
Greg Swift 2013-03-26 10:12:09 -05:00
parent 9c5028eb8b
commit 34a38a74b1

View file

@ -40,6 +40,17 @@ options:
- the SSH public key, as a string - the SSH public key, as a string
required: true required: true
default: null default: null
path:
description:
- Alternate path to the authorized_keys file
required: false
default: "/home/user/.ssh/authorized_keys"
manage_dir:
description:
- Wheter this module should manage the directory of the authorized_keys file
required: false
choices: [ "yes", "no" ]
default: "yes"
state: state:
description: description:
- whether the given key should or should not be in the file - whether the given key should or should not be in the file
@ -51,6 +62,8 @@ examples:
description: "Example from Ansible Playbooks" description: "Example from Ansible Playbooks"
- code: "authorized_key: user=charlie key='$FILE(/home/charlie/.ssh/id_rsa.pub)'" - code: "authorized_key: user=charlie key='$FILE(/home/charlie/.ssh/id_rsa.pub)'"
description: "Shorthand available in Ansible 0.8 and later" description: "Shorthand available in Ansible 0.8 and later"
- code: "authorized_key: user=charlie key='$FILE(/home/charlie/.ssh/id_rsa.pub)' sshdir='/etc/ssh/authorized_keys/charlie' manage_dir=no"
description: "Advanced usage with an alternate AuthorizedKeysFile configuration"
author: Brad Olson author: Brad Olson
''' '''
@ -60,6 +73,8 @@ author: Brad Olson
# ========= # =========
# user = username # user = username
# key = line to add to authorized_keys for user # key = line to add to authorized_keys for user
# path = path to the user's authorized_keys file (default: ~/.ssh/authorized_keys)
# manage_dir = whether to create, and control ownership of the directory (default: true)
# state = absent|present (default: present) # state = absent|present (default: present)
# #
# see example in examples/playbooks # see example in examples/playbooks
@ -71,13 +86,15 @@ import os.path
import tempfile import tempfile
import shutil import shutil
def keyfile(module, user, write=False): def keyfile(module, user, write=False, path=None, manage_dir=True):
""" """
Calculate name of authorized keys file, optionally creating the Calculate name of authorized keys file, optionally creating the
directories and file, properly setting permissions. directories and file, properly setting permissions.
:param str user: name of user in passwd file :param str user: name of user in passwd file
:param bool write: if True, write changes to authorized_keys file (creating directories if needed) :param bool write: if True, write changes to authorized_keys file (creating directories if needed)
:param str path: if not None, use provided path rather than default of '~user/.ssh/authorized_keys'
:param bool manage_dir: if True, create and set ownership of the parent dir of the authorized_keys file
:return: full path string to authorized_keys for user :return: full path string to authorized_keys for user
""" """
@ -85,9 +102,13 @@ def keyfile(module, user, write=False):
user_entry = pwd.getpwnam(user) user_entry = pwd.getpwnam(user)
except KeyError, e: except KeyError, e:
module.fail_json(msg="Failed to lookup user %s: %s" % (user, str(e))) module.fail_json(msg="Failed to lookup user %s: %s" % (user, str(e)))
homedir = user_entry.pw_dir if path is None:
sshdir = os.path.join(homedir, ".ssh") homedir = user_entry.pw_dir
keysfile = os.path.join(sshdir, "authorized_keys") sshdir = os.path.join(homedir, ".ssh")
keysfile = os.path.join(sshdir, "authorized_keys")
else:
sshdir = os.path.dirname(path)
keysfile = path
if not write: if not write:
return keysfile return keysfile
@ -95,12 +116,13 @@ def keyfile(module, user, write=False):
uid = user_entry.pw_uid uid = user_entry.pw_uid
gid = user_entry.pw_gid gid = user_entry.pw_gid
if not os.path.exists(sshdir): if manage_dir in BOOLEANS_TRUE:
os.mkdir(sshdir, 0700) if not os.path.exists(sshdir):
if module.selinux_enabled(): os.mkdir(sshdir, 0700)
module.set_default_selinux_context(sshdir, False) if module.selinux_enabled():
os.chown(sshdir, uid, gid) module.set_default_selinux_context(sshdir, False)
os.chmod(sshdir, 0700) os.chown(sshdir, uid, gid)
os.chmod(sshdir, 0700)
if not os.path.exists( keysfile): if not os.path.exists( keysfile):
try: try:
@ -139,14 +161,17 @@ def enforce_state(module, params):
Add or remove key. Add or remove key.
""" """
user = params["user"] user = params["user"]
key = params["key"] key = params["key"]
state = params.get("state", "present") path = params.get("path", None)
manage_dir = params.get("manage_dir", True)
state = params.get("state", "present")
key = key.split('\n') key = key.split('\n')
# check current state -- just get the filename, don't create file # check current state -- just get the filename, don't create file
params["keyfile"] = keyfile(module, user, write=False) write = False
params["keyfile"] = keyfile(module, user, write, path, manage_dir)
keys = readkeys(params["keyfile"]) keys = readkeys(params["keyfile"])
# Check our new keys, if any of them exist we'll continue. # Check our new keys, if any of them exist we'll continue.
@ -157,14 +182,16 @@ def enforce_state(module, params):
if present: if present:
continue continue
keys.append(new_key) keys.append(new_key)
writekeys(module, keyfile(module, user,write=True), keys) write = True
writekeys(module, keyfile(module, user, write, path, manage_dir), keys)
params['changed'] = True params['changed'] = True
elif state=="absent": elif state=="absent":
if not present: if not present:
continue continue
keys.remove(new_key) keys.remove(new_key)
writekeys(module, keyfile(module, user,write=True), keys) write = True
writekeys(module, keyfile(module, user, write, path, manage_dir), keys)
params['changed'] = True params['changed'] = True
return params return params
@ -173,9 +200,11 @@ def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec = dict( argument_spec = dict(
user = dict(required=True), user = dict(required=True, type='str'),
key = dict(required=True), key = dict(required=True, type='str'),
state = dict(default='present', choices=['absent','present']) path = dict(required=False, type='str'),
manage_dir = dict(required=False, type='bool', choices=BOOLEANS),
state = dict(default='present', choices=['absent','present'])
) )
) )