From 67284eaae5fda31fd38fe8b11715444344da2de7 Mon Sep 17 00:00:00 2001 From: Manoj Srivastava Date: Mon, 28 Sep 2015 11:49:50 -0700 Subject: [PATCH 1/5] [admin_user]: Added support for password and static pubkey auth This commit adds authentication optionally with passwords or static ssh pubkeys for the admin user. There are now 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. Fixes: https://github.com/andsens/bootstrap-vz/issues/248 Signed-off-by: Manoj Srivastava --- bootstrapvz/plugins/admin_user/README.rst | 24 ++++++- bootstrapvz/plugins/admin_user/__init__.py | 9 ++- .../plugins/admin_user/manifest-schema.yml | 6 ++ bootstrapvz/plugins/admin_user/tasks.py | 62 +++++++++++++++++-- 4 files changed, 90 insertions(+), 11 deletions(-) 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)) From a56f20657bb440a42926118a637151244d8c0759 Mon Sep 17 00:00:00 2001 From: Manoj Srivastava Date: Mon, 28 Sep 2015 11:49:50 -0700 Subject: [PATCH 2/5] [admin_user]: Added support for password and static pubkey auth This commit adds authentication optionally with passwords or static ssh pubkeys for the admin user. There are now 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. Fixes: https://github.com/andsens/bootstrap-vz/issues/248 Signed-off-by: Manoj Srivastava --- bootstrapvz/plugins/admin_user/__init__.py | 44 ++--- bootstrapvz/plugins/admin_user/tasks.py | 177 ++++++++++++--------- 2 files changed, 125 insertions(+), 96 deletions(-) diff --git a/bootstrapvz/plugins/admin_user/__init__.py b/bootstrapvz/plugins/admin_user/__init__.py index 3e30b20..9a89022 100644 --- a/bootstrapvz/plugins/admin_user/__init__.py +++ b/bootstrapvz/plugins/admin_user/__init__.py @@ -1,28 +1,36 @@ def validate_manifest(data, validator, error): - import os.path - schema_path = os.path.normpath(os.path.join(os.path.dirname(__file__), 'manifest-schema.yml')) - validator(data, schema_path) + import os.path + schema_path = os.path.normpath(os.path.join(os.path.dirname(__file__), 'manifest-schema.yml')) + validator(data, schema_path) + if ('password' in data['plugins']['admin_user'] and 'pubkey' in data['plugins']['admin_user']): + msg = 'passwd and pubkey are mutually exclusive.' + error(msg, ['plugins', 'admin_user']) + full_path = data['plugins']['admin_user']['pubkey'] + if not os.path.exists(full_path): + msg = 'Could not find public key at %s' % full_path + error(msg, ['plugins', 'admin_user']) def resolve_tasks(taskset, manifest): - import tasks - from bootstrapvz.common.tasks import ssh - from bootstrapvz.providers.ec2.tasks import initd + import tasks + from bootstrapvz.common.tasks import ssh - from bootstrapvz.common.releases import jessie - if manifest.release < jessie: - taskset.update([ssh.DisableRootLogin]) + from bootstrapvz.common.releases import jessie + if manifest.release < jessie: + taskset.update([ssh.DisableRootLogin]) - if 'password' in manifest.plugins['admin_user']: + 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.add(tasks.AdminUserCredentialsPassword) + else: + if 'pubkey' in manifest.plugins['admin_user']: + taskset.add(tasks.AdminUserCredentialsPublicKey) + else: + taskset.add(tasks.AdminUserCredentialsEc2) - taskset.update([tasks.AddSudoPackage, - tasks.CreateAdminUser, - tasks.PasswordlessSudo, - ]) + taskset.update([tasks.AddSudoPackage, + tasks.CreateAdminUser, + tasks.PasswordlessSudo, + ]) diff --git a/bootstrapvz/plugins/admin_user/tasks.py b/bootstrapvz/plugins/admin_user/tasks.py index 26b53bf..d3e4c6c 100644 --- a/bootstrapvz/plugins/admin_user/tasks.py +++ b/bootstrapvz/plugins/admin_user/tasks.py @@ -1,104 +1,125 @@ from bootstrapvz.base import Task from bootstrapvz.common import phases from bootstrapvz.common.tasks.initd import InstallInitScripts +from bootstrapvz.providers.ec2.tasks.initd import AddEC2InitScripts + import logging import os class AddSudoPackage(Task): - description = 'Adding `sudo\' to the image packages' - phase = phases.preparation + description = 'Adding `sudo\' to the image packages' + phase = phases.preparation - @classmethod - def run(cls, info): - info.packages.add('sudo') + @classmethod + def run(cls, info): + info.packages.add('sudo') class CreateAdminUser(Task): - description = 'Creating the admin user' - phase = phases.system_modification + description = 'Creating the admin user' + phase = phases.system_modification - @classmethod - def run(cls, info): - from bootstrapvz.common.tools import log_check_call - log_check_call(['chroot', info.root, - 'useradd', - '--create-home', '--shell', '/bin/bash', - info.manifest.plugins['admin_user']['username']]) + @classmethod + def run(cls, info): + from bootstrapvz.common.tools import log_check_call + log_check_call(['chroot', info.root, + 'useradd', + '--create-home', '--shell', '/bin/bash', + info.manifest.plugins['admin_user']['username']]) class PasswordlessSudo(Task): - description = 'Allowing the admin user to use sudo without a password' - phase = phases.system_modification + description = 'Allowing the admin user to use sudo without a password' + phase = phases.system_modification - @classmethod - def run(cls, info): - sudo_admin_path = os.path.join(info.root, 'etc/sudoers.d/99_admin') - username = info.manifest.plugins['admin_user']['username'] - with open(sudo_admin_path, 'w') as sudo_admin: - sudo_admin.write('{username} ALL=(ALL) NOPASSWD:ALL'.format(username=username)) - import stat - ug_read_only = (stat.S_IRUSR | stat.S_IRGRP) - os.chmod(sudo_admin_path, ug_read_only) + @classmethod + def run(cls, info): + sudo_admin_path = os.path.join(info.root, 'etc/sudoers.d/99_admin') + username = info.manifest.plugins['admin_user']['username'] + with open(sudo_admin_path, 'w') as sudo_admin: + sudo_admin.write('{username} ALL=(ALL) NOPASSWD:ALL'.format(username=username)) + import stat + ug_read_only = (stat.S_IRUSR | stat.S_IRGRP) + os.chmod(sudo_admin_path, ug_read_only) -class AdminUserCredentials(Task): - description = 'Set up access credentials for the admin user' - phase = phases.system_modification - predecessors = [InstallInitScripts, CreateAdminUser] +class AdminUserCredentialsPassword(Task): + description = 'Set up access credentials for the admin user with a given password' + phase = phases.system_modification + predecessors = [InstallInitScripts, CreateAdminUser] - @classmethod - def run(cls, info): - from bootstrapvz.common.exceptions import TaskError - from bootstrapvz.common.tools import sed_i + @classmethod + def run(cls, info): 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 + log.debug('Setting the password for the admin user.') + log_check_call(['chroot', info.root, 'chpasswd'], + info.manifest.plugins['admin_user']['username'] + + ':' + info.manifest.plugins['admin_user']['password']) + return + + +class AdminUserCredentialsPublicKey(Task): + description = 'Set up access credentials for the admin user with a given public key' + phase = phases.system_modification + predecessors = [AddEC2InitScripts, CreateAdminUser] + successors = [InstallInitScripts] + + @classmethod + def run(cls, info): + from bootstrapvz.common.tools import log_check_call + + log = logging.getLogger(__name__) + + import stat + from shutil import copy + full_path = info.manifest.plugins['admin_user']['pubkey'] + log.debug('Copying public key from {path}'.format(path=full_path)) + + if 'ec2-get-credentials' in info.initd['install']: + log.warn('You are using a static public key for the admin account.' + ' This will conflict with the ec2 public key injection mechanisn.' + ' The ec2-get-credentials startup script has therefore been disabled.') + del info.initd['install']['ec2-get-credentials'] + + username = info.manifest.plugins['admin_user']['username'] + + ssh_file = os.path.join('/home/', username, '/.ssh/authorized_keys') + rel_ssh_file = os.path.realpath(info.root + '/%s' % ssh_file) + + ssh_dir = os.path.dirname(ssh_file) + rel_ssh_dir = os.path.realpath(info.root + '/%s' % ssh_dir) + if not os.path.exists(rel_ssh_dir): + log.debug('Creating %s mode 700' % rel_ssh_dir) + os.mkdir(rel_ssh_dir, 0700) + else: + log.debug('setting %s mode 700' % 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 + + +class AdminUserCredentialsEC2(Task): + description = 'Set up access credentials for the admin user using the EC2 credentials' + phase = phases.system_modification + predecessors = [InstallInitScripts, CreateAdminUser] + + @classmethod + def run(cls, info): + from bootstrapvz.common.exceptions import TaskError + from bootstrapvz.common.tools import sed_i + log = logging.getLogger(__name__) 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']) + if os.path.exists(getcreds_path): + log.debug('Updating EC2 get credentials script.') 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)) + sed_i(getcreds_path, "username='root'", + "username='{username}'".format(username=username)) + else: + raise TaskError('Could not find EC2 get credentials script.') From efeddc43a8861268116737c8b76946cdbab50959 Mon Sep 17 00:00:00 2001 From: Manoj Srivastava Date: Sun, 10 Jan 2016 02:12:58 -0800 Subject: [PATCH 3/5] [admin_user]: Update per comments As requested, this commit converts to tab indentation. Signed-off-by: Manoj Srivastava --- bootstrapvz/plugins/admin_user/__init__.py | 54 +++---- bootstrapvz/plugins/admin_user/tasks.py | 179 +++++++++++---------- 2 files changed, 120 insertions(+), 113 deletions(-) diff --git a/bootstrapvz/plugins/admin_user/__init__.py b/bootstrapvz/plugins/admin_user/__init__.py index 9a89022..e83088d 100644 --- a/bootstrapvz/plugins/admin_user/__init__.py +++ b/bootstrapvz/plugins/admin_user/__init__.py @@ -1,36 +1,36 @@ def validate_manifest(data, validator, error): - import os.path - schema_path = os.path.normpath(os.path.join(os.path.dirname(__file__), 'manifest-schema.yml')) - validator(data, schema_path) - if ('password' in data['plugins']['admin_user'] and 'pubkey' in data['plugins']['admin_user']): - msg = 'passwd and pubkey are mutually exclusive.' - error(msg, ['plugins', 'admin_user']) - full_path = data['plugins']['admin_user']['pubkey'] - if not os.path.exists(full_path): - msg = 'Could not find public key at %s' % full_path - error(msg, ['plugins', 'admin_user']) + import os.path + schema_path = os.path.normpath(os.path.join(os.path.dirname(__file__), 'manifest-schema.yml')) + validator(data, schema_path) + if ('password' in data['plugins']['admin_user'] and 'pubkey' in data['plugins']['admin_user']): + msg = 'passwd and pubkey are mutually exclusive.' + error(msg, ['plugins', 'admin_user']) + full_path = data['plugins']['admin_user']['pubkey'] + if not os.path.exists(full_path): + msg = 'Could not find public key at %s' % full_path + error(msg, ['plugins', 'admin_user']) def resolve_tasks(taskset, manifest): - import tasks - from bootstrapvz.common.tasks import ssh + import tasks + from bootstrapvz.common.tasks import ssh - from bootstrapvz.common.releases import jessie - if manifest.release < jessie: - taskset.update([ssh.DisableRootLogin]) + 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.AdminUserCredentialsPassword) - else: - if 'pubkey' in manifest.plugins['admin_user']: - taskset.add(tasks.AdminUserCredentialsPublicKey) - else: - taskset.add(tasks.AdminUserCredentialsEc2) + if 'password' in manifest.plugins['admin_user']: + taskset.discard(ssh.DisableSSHPasswordAuthentication) + taskset.add(tasks.AdminUserCredentialsPassword) + else: + if 'pubkey' in manifest.plugins['admin_user']: + taskset.add(tasks.AdminUserCredentialsPublicKey) + else: + taskset.add(tasks.AdminUserCredentialsEc2) - taskset.update([tasks.AddSudoPackage, - tasks.CreateAdminUser, - tasks.PasswordlessSudo, - ]) + taskset.update([tasks.AddSudoPackage, + tasks.CreateAdminUser, + tasks.PasswordlessSudo, + ]) diff --git a/bootstrapvz/plugins/admin_user/tasks.py b/bootstrapvz/plugins/admin_user/tasks.py index d3e4c6c..4520986 100644 --- a/bootstrapvz/plugins/admin_user/tasks.py +++ b/bootstrapvz/plugins/admin_user/tasks.py @@ -8,118 +8,125 @@ import os class AddSudoPackage(Task): - description = 'Adding `sudo\' to the image packages' - phase = phases.preparation + description = 'Adding `sudo\' to the image packages' + phase = phases.preparation - @classmethod - def run(cls, info): - info.packages.add('sudo') + @classmethod + def run(cls, info): + info.packages.add('sudo') class CreateAdminUser(Task): - description = 'Creating the admin user' - phase = phases.system_modification + description = 'Creating the admin user' + phase = phases.system_modification - @classmethod - def run(cls, info): - from bootstrapvz.common.tools import log_check_call - log_check_call(['chroot', info.root, - 'useradd', - '--create-home', '--shell', '/bin/bash', - info.manifest.plugins['admin_user']['username']]) + @classmethod + def run(cls, info): + from bootstrapvz.common.tools import log_check_call + log_check_call(['chroot', info.root, + 'useradd', + '--create-home', '--shell', '/bin/bash', + info.manifest.plugins['admin_user']['username']]) class PasswordlessSudo(Task): - description = 'Allowing the admin user to use sudo without a password' - phase = phases.system_modification + description = 'Allowing the admin user to use sudo without a password' + phase = phases.system_modification - @classmethod - def run(cls, info): - sudo_admin_path = os.path.join(info.root, 'etc/sudoers.d/99_admin') - username = info.manifest.plugins['admin_user']['username'] - with open(sudo_admin_path, 'w') as sudo_admin: - sudo_admin.write('{username} ALL=(ALL) NOPASSWD:ALL'.format(username=username)) - import stat - ug_read_only = (stat.S_IRUSR | stat.S_IRGRP) - os.chmod(sudo_admin_path, ug_read_only) + @classmethod + def run(cls, info): + sudo_admin_path = os.path.join(info.root, 'etc/sudoers.d/99_admin') + username = info.manifest.plugins['admin_user']['username'] + with open(sudo_admin_path, 'w') as sudo_admin: + sudo_admin.write('{username} ALL=(ALL) NOPASSWD:ALL'.format(username=username)) + import stat + ug_read_only = (stat.S_IRUSR | stat.S_IRGRP) + os.chmod(sudo_admin_path, ug_read_only) class AdminUserCredentialsPassword(Task): - description = 'Set up access credentials for the admin user with a given password' - phase = phases.system_modification - predecessors = [InstallInitScripts, CreateAdminUser] + description = 'Set up access credentials for the admin user with a given password' + phase = phases.system_modification + predecessors = [InstallInitScripts, CreateAdminUser] - @classmethod - def run(cls, info): - from bootstrapvz.common.tools import log_check_call - log = logging.getLogger(__name__) + @classmethod + def run(cls, info): + from bootstrapvz.common.tools import log_check_call + log = logging.getLogger(__name__) - log.debug('Setting the password for the admin user.') - log_check_call(['chroot', info.root, 'chpasswd'], - info.manifest.plugins['admin_user']['username'] + - ':' + info.manifest.plugins['admin_user']['password']) - return + log.debug('Setting the password for the admin user.') + log_check_call( + ['chroot', info.root, 'chpasswd'], + info.manifest.plugins['admin_user']['username'] + + ':' + info.manifest.plugins['admin_user']['password'] + ) + return class AdminUserCredentialsPublicKey(Task): - description = 'Set up access credentials for the admin user with a given public key' - phase = phases.system_modification - predecessors = [AddEC2InitScripts, CreateAdminUser] - successors = [InstallInitScripts] + description = 'Set up access credentials for the admin user with a given public key' + phase = phases.system_modification + predecessors = [AddEC2InitScripts, CreateAdminUser] + successors = [InstallInitScripts] - @classmethod - def run(cls, info): - from bootstrapvz.common.tools import log_check_call + @classmethod + def run(cls, info): + from bootstrapvz.common.tools import log_check_call - log = logging.getLogger(__name__) + log = logging.getLogger(__name__) - import stat - from shutil import copy - full_path = info.manifest.plugins['admin_user']['pubkey'] - log.debug('Copying public key from {path}'.format(path=full_path)) + import stat + from shutil import copy + full_path = info.manifest.plugins['admin_user']['pubkey'] + log.debug('Copying public key from {path}'.format(path=full_path)) - if 'ec2-get-credentials' in info.initd['install']: - log.warn('You are using a static public key for the admin account.' - ' This will conflict with the ec2 public key injection mechanisn.' - ' The ec2-get-credentials startup script has therefore been disabled.') - del info.initd['install']['ec2-get-credentials'] + if 'ec2-get-credentials' in info.initd['install']: + log.warn( + 'You are using a static public key for the admin account.' + ' This will conflict with the ec2 public key injection mechanisn.' + ' The ec2-get-credentials startup script has therefore been disabled.') + del info.initd['install']['ec2-get-credentials'] - username = info.manifest.plugins['admin_user']['username'] + username = info.manifest.plugins['admin_user']['username'] - ssh_file = os.path.join('/home/', username, '/.ssh/authorized_keys') - rel_ssh_file = os.path.realpath(info.root + '/%s' % ssh_file) + ssh_file = os.path.join('/home/', username, '.ssh/authorized_keys') + rel_ssh_file = os.path.realpath(info.root + '/%s' % ssh_file) - ssh_dir = os.path.dirname(ssh_file) - rel_ssh_dir = os.path.realpath(info.root + '/%s' % ssh_dir) - if not os.path.exists(rel_ssh_dir): - log.debug('Creating %s mode 700' % rel_ssh_dir) - os.mkdir(rel_ssh_dir, 0700) - else: - log.debug('setting %s mode 700' % 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 + ssh_dir = os.path.dirname(ssh_file) + rel_ssh_dir = os.path.realpath(info.root + '/%s' % ssh_dir) + if not os.path.exists(rel_ssh_dir): + log.debug('Creating %s.' % rel_ssh_dir) + os.mkdir(rel_ssh_dir) + + log.debug('setting %s mode 700' % rel_ssh_dir) + mode = (stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + os.chmod(rel_ssh_dir, mode) + + 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 class AdminUserCredentialsEC2(Task): - description = 'Set up access credentials for the admin user using the EC2 credentials' - phase = phases.system_modification - predecessors = [InstallInitScripts, CreateAdminUser] + description = 'Set up access credentials for the admin user using the EC2 credentials' + phase = phases.system_modification + predecessors = [InstallInitScripts, CreateAdminUser] - @classmethod - def run(cls, info): - from bootstrapvz.common.exceptions import TaskError - from bootstrapvz.common.tools import sed_i - log = logging.getLogger(__name__) + @classmethod + def run(cls, info): + from bootstrapvz.common.exceptions import TaskError + from bootstrapvz.common.tools import sed_i + log = logging.getLogger(__name__) - getcreds_path = os.path.join(info.root, 'etc/init.d/ec2-get-credentials') - if os.path.exists(getcreds_path): - 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)) - else: - raise TaskError('Could not find EC2 get credentials script.') + getcreds_path = os.path.join(info.root, 'etc/init.d/ec2-get-credentials') + if os.path.exists(getcreds_path): + 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)) + else: + raise TaskError('Could not find EC2 get credentials script.') From 32ef6fc571e910e3ffc1f236356aa0deecd83754 Mon Sep 17 00:00:00 2001 From: Manoj Srivastava Date: Sat, 6 Feb 2016 02:15:52 -0800 Subject: [PATCH 4/5] [admin_user]: More cleanups. Remove uneeded tests. Signed-off-by: Manoj Srivastava --- bootstrapvz/plugins/admin_user/__init__.py | 9 +++++---- bootstrapvz/plugins/admin_user/tasks.py | 16 ++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/bootstrapvz/plugins/admin_user/__init__.py b/bootstrapvz/plugins/admin_user/__init__.py index e83088d..b7e8696 100644 --- a/bootstrapvz/plugins/admin_user/__init__.py +++ b/bootstrapvz/plugins/admin_user/__init__.py @@ -7,10 +7,11 @@ def validate_manifest(data, validator, error): if ('password' in data['plugins']['admin_user'] and 'pubkey' in data['plugins']['admin_user']): msg = 'passwd and pubkey are mutually exclusive.' error(msg, ['plugins', 'admin_user']) - full_path = data['plugins']['admin_user']['pubkey'] - if not os.path.exists(full_path): - msg = 'Could not find public key at %s' % full_path - error(msg, ['plugins', 'admin_user']) + if 'pubkey' in data['plugins']['admin_user']: + full_path = data['plugins']['admin_user']['pubkey'] + if not os.path.exists(full_path): + msg = 'Could not find public key at %s' % full_path + error(msg, ['plugins', 'admin_user']) def resolve_tasks(taskset, manifest): diff --git a/bootstrapvz/plugins/admin_user/tasks.py b/bootstrapvz/plugins/admin_user/tasks.py index 4520986..bf8df54 100644 --- a/bootstrapvz/plugins/admin_user/tasks.py +++ b/bootstrapvz/plugins/admin_user/tasks.py @@ -90,10 +90,10 @@ class AdminUserCredentialsPublicKey(Task): username = info.manifest.plugins['admin_user']['username'] ssh_file = os.path.join('/home/', username, '.ssh/authorized_keys') - rel_ssh_file = os.path.realpath(info.root + '/%s' % ssh_file) + rel_ssh_file = os.path.join(info.root, ssh_file) ssh_dir = os.path.dirname(ssh_file) - rel_ssh_dir = os.path.realpath(info.root + '/%s' % ssh_dir) + rel_ssh_dir = os.path.join(info.root, ssh_dir) if not os.path.exists(rel_ssh_dir): log.debug('Creating %s.' % rel_ssh_dir) os.mkdir(rel_ssh_dir) @@ -118,15 +118,11 @@ class AdminUserCredentialsEC2(Task): @classmethod def run(cls, info): - from bootstrapvz.common.exceptions import TaskError from bootstrapvz.common.tools import sed_i log = logging.getLogger(__name__) getcreds_path = os.path.join(info.root, 'etc/init.d/ec2-get-credentials') - if os.path.exists(getcreds_path): - 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)) - else: - raise TaskError('Could not find EC2 get credentials script.') + 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)) From 4a29e5eec5542a47e79aad419a449634e61a982b Mon Sep 17 00:00:00 2001 From: Manoj Srivastava Date: Mon, 8 Feb 2016 02:50:55 -0800 Subject: [PATCH 5/5] Bit by os.path.join not liking leading / join ignores all path components to the left of any component that starts with a /. Tested and found working. Signed-off-by: Manoj Srivastava --- bootstrapvz/plugins/admin_user/tasks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bootstrapvz/plugins/admin_user/tasks.py b/bootstrapvz/plugins/admin_user/tasks.py index bf8df54..4293ee9 100644 --- a/bootstrapvz/plugins/admin_user/tasks.py +++ b/bootstrapvz/plugins/admin_user/tasks.py @@ -89,11 +89,11 @@ class AdminUserCredentialsPublicKey(Task): username = info.manifest.plugins['admin_user']['username'] - ssh_file = os.path.join('/home/', username, '.ssh/authorized_keys') - rel_ssh_file = os.path.join(info.root, ssh_file) + ssh_file = os.path.join('/home', username, '.ssh/authorized_keys') + rel_ssh_file = os.path.join(info.root, 'home', username, '.ssh/authorized_keys') ssh_dir = os.path.dirname(ssh_file) - rel_ssh_dir = os.path.join(info.root, ssh_dir) + rel_ssh_dir = os.path.dirname(rel_ssh_file) if not os.path.exists(rel_ssh_dir): log.debug('Creating %s.' % rel_ssh_dir) os.mkdir(rel_ssh_dir)