diff --git a/bootstrapvz/plugins/admin_user/README.rst b/bootstrapvz/plugins/admin_user/README.rst index 8da68d1..3e5d0f8 100644 --- a/bootstrapvz/plugins/admin_user/README.rst +++ b/bootstrapvz/plugins/admin_user/README.rst @@ -2,11 +2,29 @@ Admin user ---------- This plugin creates a user with passwordless sudo privileges. It also -disables the SSH root login. If the EC2 init scripts are installed, the -script for fetching the SSH authorized keys will be adjust to match the -username specified. +disables the SSH root login. There are three ways to grant access to +the admin user: +- Set a password for the user, or +- Provide a ssh public key to allow remote ssh login, or +- Use the EC2 public key (EC2 machines only) + +If a password is provided, this plugin sets the admin password. This +also re-enables password login (off by default in Jessie). + +If the optional argument pubkey is present (it should be a full path +to a ssh public key), it will ensure that the ssh public key is used +to set up password less remote login for the admin user. + +Only one of these options (password, or pubkey) may be specified. + +If neither the password not a ssh public key location are specified, +and if the EC2 init scripts are installed, the script for fetching the +SSH authorized keys will be adjust to match the username specified. Settings ~~~~~~~~ - ``username``: The username of the account to create. ``required`` +- ``password``: An optional password for the account to create. ``optional`` +- ``pubkey``: The full path to an ssh public key to allow + remote access into the admin account. ``optional`` diff --git a/bootstrapvz/plugins/admin_user/__init__.py b/bootstrapvz/plugins/admin_user/__init__.py index 11888de..3e30b20 100644 --- a/bootstrapvz/plugins/admin_user/__init__.py +++ b/bootstrapvz/plugins/admin_user/__init__.py @@ -10,13 +10,18 @@ def resolve_tasks(taskset, manifest): import tasks from bootstrapvz.common.tasks import ssh from bootstrapvz.providers.ec2.tasks import initd - if initd.AddEC2InitScripts in taskset: - taskset.add(tasks.AdminUserCredentials) from bootstrapvz.common.releases import jessie if manifest.release < jessie: taskset.update([ssh.DisableRootLogin]) + if 'password' in manifest.plugins['admin_user']: + taskset.discard(ssh.DisableSSHPasswordAuthentication) + taskset.add(tasks.AdminUserCredentials) + else: + if initd.AddEC2InitScripts in taskset or 'pubkey' in manifest.plugins['admin_user']: + taskset.add(tasks.AdminUserCredentials) + taskset.update([tasks.AddSudoPackage, tasks.CreateAdminUser, tasks.PasswordlessSudo, diff --git a/bootstrapvz/plugins/admin_user/manifest-schema.yml b/bootstrapvz/plugins/admin_user/manifest-schema.yml index 02cce0a..4d20396 100644 --- a/bootstrapvz/plugins/admin_user/manifest-schema.yml +++ b/bootstrapvz/plugins/admin_user/manifest-schema.yml @@ -10,5 +10,11 @@ properties: type: object properties: username: {type: string} + password: {type: string} + pubkey: {$ref: '#/definitions/absolute_path'} required: [username] additionalProperties: false +definitions: + absolute_path: + pattern: ^/[^\0]+$ + type: string diff --git a/bootstrapvz/plugins/admin_user/tasks.py b/bootstrapvz/plugins/admin_user/tasks.py index ae7f221..26b53bf 100644 --- a/bootstrapvz/plugins/admin_user/tasks.py +++ b/bootstrapvz/plugins/admin_user/tasks.py @@ -1,6 +1,7 @@ from bootstrapvz.base import Task from bootstrapvz.common import phases from bootstrapvz.common.tasks.initd import InstallInitScripts +import logging import os @@ -42,13 +43,62 @@ class PasswordlessSudo(Task): class AdminUserCredentials(Task): - description = 'Modifying ec2-get-credentials to copy the ssh public key to the admin user' + description = 'Set up access credentials for the admin user' phase = phases.system_modification - predecessors = [InstallInitScripts] + predecessors = [InstallInitScripts, CreateAdminUser] @classmethod def run(cls, info): - from bootstrapvz.common.tools import sed_i - getcreds_path = os.path.join(info.root, 'etc/init.d/ec2-get-credentials') - username = info.manifest.plugins['admin_user']['username'] - sed_i(getcreds_path, 'username=\'root\'', 'username=\'{username}\''.format(username=username)) + from bootstrapvz.common.exceptions import TaskError + from bootstrapvz.common.tools import sed_i + from bootstrapvz.common.tools import log_check_call + log = logging.getLogger(__name__) + + if 'password' in info.manifest.plugins['admin_user']: + if 'pubkey' in info.manifest.plugins['admin_user']: + msg = 'The options password and pubkey are mutually exclusive' + raise TaskError(msg) + log_check_call(['chroot', info.root, 'chpasswd'], + info.manifest.plugins['admin_user']['username'] + + ':' + info.manifest.plugins['admin_user']['password']) + return + + getcreds_path = os.path.join(info.root, 'etc/init.d/ec2-get-credentials') + if 'pubkey' in info.manifest.plugins['admin_user']: + import stat + from shutil import copy + full_path = info.manifest.plugins['admin_user']['pubkey'] + if not os.path.exists(full_path): + msg = 'Could not find public key at {full_path}'.format(full_path=full_path) + raise TaskError(msg) + log.debug('Copying public key from {path}'.format(path=full_path)) + + if os.path.exists(getcreds_path): + log.warn('You are using a static public key for the admin account.' + ' This will conflict with the ec2 public key njection mechanisn.' + ' The ec2-get-credentials startup script has therefore been disabled.') + log_check_call(['chroot', info.root, 'insserv', '--remove', + 'ec2-get-credentials']) + username = info.manifest.plugins['admin_user']['username'] + + ssh_file = os.path.join('/home/' + '{username}/.ssh/authorized_keys'.format(username=username)) + rel_ssh_file = os.path.realpath(info.root + '/{ssh_file}'.format(ssh_file=ssh_file)) + + ssh_dir = os.path.dirname(ssh_file) + rel_ssh_dir = os.path.realpath(info.root + '/{ssh_dir}'.format(ssh_dir=ssh_dir)) + if not os.path.exists(rel_ssh_dir): + log.debug('Creating {ssh_dir} mode 700'.format(ssh_dir=rel_ssh_dir)) + os.mkdir(rel_ssh_dir, 0700) + else: + log.debug('setting {ssh_dir} mode 700'.format(ssh_dir=rel_ssh_dir)) + os.chmod(rel_ssh_dir, 0700) + copy(full_path, rel_ssh_file) + mode = (stat.S_IRUSR | stat.S_IWUSR) + os.chmod(rel_ssh_file, mode) + log_check_call(['chroot', info.root, 'chown', '-R', username, ssh_dir]) + return + + log.debug('Updating EC2 get credentials script.') + username = info.manifest.plugins['admin_user']['username'] + sed_i(getcreds_path, 'username=\'root\'', 'username=\'{username}\''.format(username=username))