summaryrefslogtreecommitdiff
path: root/library/system/authorized_key
diff options
context:
space:
mode:
Diffstat (limited to 'library/system/authorized_key')
-rw-r--r--library/system/authorized_key421
1 files changed, 0 insertions, 421 deletions
diff --git a/library/system/authorized_key b/library/system/authorized_key
deleted file mode 100644
index f964113127..0000000000
--- a/library/system/authorized_key
+++ /dev/null
@@ -1,421 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-"""
-Ansible module to add authorized_keys for ssh logins.
-(c) 2012, Brad Olson <brado@movedbylight.com>
-
-This file is part of Ansible
-
-Ansible is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Ansible is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Ansible. If not, see <http://www.gnu.org/licenses/>.
-"""
-
-DOCUMENTATION = '''
----
-module: authorized_key
-short_description: Adds or removes an SSH authorized key
-description:
- - Adds or removes an SSH authorized key for a user from a remote host.
-version_added: "0.5"
-options:
- user:
- description:
- - The username on the remote host whose authorized_keys file will be modified
- required: true
- default: null
- aliases: []
- key:
- description:
- - The SSH public key, as a string
- required: true
- default: null
- path:
- description:
- - Alternate path to the authorized_keys file
- required: false
- default: "(homedir)+/.ssh/authorized_keys"
- version_added: "1.2"
- manage_dir:
- description:
- - Whether this module should manage the directory of the authorized key file. If
- set, the module will create the directory, as well as set the owner and permissions
- of an existing directory. Be sure to
- set C(manage_dir=no) if you are using an alternate directory for
- authorized_keys, as set with C(path), since you could lock yourself out of
- SSH access. See the example below.
- required: false
- choices: [ "yes", "no" ]
- default: "yes"
- version_added: "1.2"
- state:
- description:
- - Whether the given key (with the given key_options) should or should not be in the file
- required: false
- choices: [ "present", "absent" ]
- default: "present"
- key_options:
- description:
- - A string of ssh key options to be prepended to the key in the authorized_keys file
- required: false
- default: null
- version_added: "1.4"
-description:
- - "Adds or removes authorized keys for particular user accounts"
-author: Brad Olson
-'''
-
-EXAMPLES = '''
-# Example using key data from a local file on the management machine
-- authorized_key: user=charlie key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
-
-# Using alternate directory locations:
-- authorized_key: user=charlie
- key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
- path='/etc/ssh/authorized_keys/charlie'
- manage_dir=no
-
-# Using with_file
-- name: Set up authorized_keys for the deploy user
- authorized_key: user=deploy
- key="{{ item }}"
- with_file:
- - public_keys/doe-jane
- - public_keys/doe-john
-
-# Using key_options:
-- authorized_key: user=charlie
- key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
- key_options='no-port-forwarding,host="10.0.1.1"'
-'''
-
-# Makes sure the public key line is present or absent in the user's .ssh/authorized_keys.
-#
-# Arguments
-# =========
-# user = username
-# 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)
-#
-# see example in examples/playbooks
-
-import sys
-import os
-import pwd
-import os.path
-import tempfile
-import re
-import shlex
-
-class keydict(dict):
-
- """ a dictionary that maintains the order of keys as they are added """
-
- # http://stackoverflow.com/questions/2328235/pythonextend-the-dict-class
-
- def __init__(self, *args, **kw):
- super(keydict,self).__init__(*args, **kw)
- self.itemlist = super(keydict,self).keys()
- def __setitem__(self, key, value):
- self.itemlist.append(key)
- super(keydict,self).__setitem__(key, value)
- def __iter__(self):
- return iter(self.itemlist)
- def keys(self):
- return self.itemlist
- def values(self):
- return [self[key] for key in self]
- def itervalues(self):
- return (self[key] for key in self)
-
-def keyfile(module, user, write=False, path=None, manage_dir=True):
- """
- Calculate name of authorized keys file, optionally creating the
- directories and file, properly setting permissions.
-
- :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 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
- """
-
- try:
- user_entry = pwd.getpwnam(user)
- except KeyError, e:
- module.fail_json(msg="Failed to lookup user %s: %s" % (user, str(e)))
- if path is None:
- homedir = user_entry.pw_dir
- sshdir = os.path.join(homedir, ".ssh")
- keysfile = os.path.join(sshdir, "authorized_keys")
- else:
- sshdir = os.path.dirname(path)
- keysfile = path
-
- if not write:
- return keysfile
-
- uid = user_entry.pw_uid
- gid = user_entry.pw_gid
-
- if manage_dir:
- if not os.path.exists(sshdir):
- os.mkdir(sshdir, 0700)
- if module.selinux_enabled():
- module.set_default_selinux_context(sshdir, False)
- os.chown(sshdir, uid, gid)
- os.chmod(sshdir, 0700)
-
- if not os.path.exists(keysfile):
- basedir = os.path.dirname(keysfile)
- if not os.path.exists(basedir):
- os.makedirs(basedir)
- try:
- f = open(keysfile, "w") #touches file so we can set ownership and perms
- finally:
- f.close()
- if module.selinux_enabled():
- module.set_default_selinux_context(keysfile, False)
-
- try:
- os.chown(keysfile, uid, gid)
- os.chmod(keysfile, 0600)
- except OSError:
- pass
-
- return keysfile
-
-def parseoptions(module, options):
- '''
- reads a string containing ssh-key options
- and returns a dictionary of those options
- '''
- options_dict = keydict() #ordered dict
- if options:
- try:
- # the following regex will split on commas while
- # ignoring those commas that fall within quotes
- regex = re.compile(r'''((?:[^,"']|"[^"]*"|'[^']*')+)''')
- parts = regex.split(options)[1:-1]
- for part in parts:
- if "=" in part:
- (key, value) = part.split("=", 1)
- options_dict[key] = value
- elif part != ",":
- options_dict[part] = None
- except:
- module.fail_json(msg="invalid option string: %s" % options)
-
- return options_dict
-
-def parsekey(module, raw_key):
- '''
- parses a key, which may or may not contain a list
- of ssh-key options at the beginning
- '''
-
- VALID_SSH2_KEY_TYPES = [
- 'ssh-ed25519',
- 'ecdsa-sha2-nistp256',
- 'ecdsa-sha2-nistp384',
- 'ecdsa-sha2-nistp521',
- 'ssh-dss',
- 'ssh-rsa',
- ]
-
- options = None # connection options
- key = None # encrypted key string
- key_type = None # type of ssh key
- type_index = None # index of keytype in key string|list
-
- # remove comment yaml escapes
- raw_key = raw_key.replace('\#', '#')
-
- # split key safely
- lex = shlex.shlex(raw_key)
- lex.quotes = []
- lex.commenters = '' #keep comment hashes
- lex.whitespace_split = True
- key_parts = list(lex)
-
- for i in range(0, len(key_parts)):
- if key_parts[i] in VALID_SSH2_KEY_TYPES:
- type_index = i
- key_type = key_parts[i]
- break
-
- # check for options
- if type_index is None:
- return None
- elif type_index > 0:
- options = " ".join(key_parts[:type_index])
-
- # parse the options (if any)
- options = parseoptions(module, options)
-
- # get key after the type index
- key = key_parts[(type_index + 1)]
-
- # set comment to everything after the key
- if len(key_parts) > (type_index + 1):
- comment = " ".join(key_parts[(type_index + 2):])
-
- return (key, key_type, options, comment)
-
-def readkeys(module, filename):
-
- if not os.path.isfile(filename):
- return {}
-
- keys = {}
- f = open(filename)
- for line in f.readlines():
- key_data = parsekey(module, line)
- if key_data:
- # use key as identifier
- keys[key_data[0]] = key_data
- else:
- # for an invalid line, just append the line
- # to the array so it will be re-output later
- keys[line] = line
- f.close()
- return keys
-
-def writekeys(module, filename, keys):
-
- fd, tmp_path = tempfile.mkstemp('', 'tmp', os.path.dirname(filename))
- f = open(tmp_path,"w")
- try:
- for index, key in keys.items():
- try:
- (keyhash,type,options,comment) = key
- option_str = ""
- if options:
- option_strings = []
- for option_key in options.keys():
- if options[option_key]:
- option_strings.append("%s=%s" % (option_key, options[option_key]))
- else:
- option_strings.append("%s" % option_key)
-
- option_str = ",".join(option_strings)
- option_str += " "
- key_line = "%s%s %s %s\n" % (option_str, type, keyhash, comment)
- except:
- key_line = key
- f.writelines(key_line)
- except IOError, e:
- module.fail_json(msg="Failed to write to file %s: %s" % (tmp_path, str(e)))
- f.close()
- module.atomic_move(tmp_path, filename)
-
-def enforce_state(module, params):
- """
- Add or remove key.
- """
-
- user = params["user"]
- key = params["key"]
- path = params.get("path", None)
- manage_dir = params.get("manage_dir", True)
- state = params.get("state", "present")
- key_options = params.get("key_options", None)
-
- # extract indivial keys into an array, skipping blank lines and comments
- key = [s for s in key.splitlines() if s and not s.startswith('#')]
-
-
- # check current state -- just get the filename, don't create file
- do_write = False
- params["keyfile"] = keyfile(module, user, do_write, path, manage_dir)
- existing_keys = readkeys(module, params["keyfile"])
-
- # Check our new keys, if any of them exist we'll continue.
- for new_key in key:
- parsed_new_key = parsekey(module, new_key)
- if key_options is not None:
- parsed_options = parseoptions(module, key_options)
- parsed_new_key = (parsed_new_key[0], parsed_new_key[1], parsed_options, parsed_new_key[3])
-
- if not parsed_new_key:
- module.fail_json(msg="invalid key specified: %s" % new_key)
-
- present = False
- matched = False
- non_matching_keys = []
-
- if parsed_new_key[0] in existing_keys:
- present = True
- # Then we check if everything matches, including
- # the key type and options. If not, we append this
- # existing key to the non-matching list
- # We only want it to match everything when the state
- # is present
- if parsed_new_key != existing_keys[parsed_new_key[0]] and state == "present":
- non_matching_keys.append(existing_keys[parsed_new_key[0]])
- else:
- matched = True
-
-
- # handle idempotent state=present
- if state=="present":
- if len(non_matching_keys) > 0:
- for non_matching_key in non_matching_keys:
- if non_matching_key[0] in existing_keys:
- del existing_keys[non_matching_key[0]]
- do_write = True
-
- if not matched:
- existing_keys[parsed_new_key[0]] = parsed_new_key
- do_write = True
-
- elif state=="absent":
- if not matched:
- continue
- del existing_keys[parsed_new_key[0]]
- do_write = True
-
- if do_write:
- if module.check_mode:
- module.exit_json(changed=True)
- writekeys(module, keyfile(module, user, do_write, path, manage_dir), existing_keys)
- params['changed'] = True
- else:
- if module.check_mode:
- module.exit_json(changed=False)
-
- return params
-
-def main():
-
- module = AnsibleModule(
- argument_spec = dict(
- user = dict(required=True, type='str'),
- key = dict(required=True, type='str'),
- path = dict(required=False, type='str'),
- manage_dir = dict(required=False, type='bool', default=True),
- state = dict(default='present', choices=['absent','present']),
- key_options = dict(required=False, type='str'),
- unique = dict(default=False, type='bool'),
- ),
- supports_check_mode=True
- )
-
- results = enforce_state(module, module.params)
- module.exit_json(**results)
-
-# import module snippets
-from ansible.module_utils.basic import *
-main()