From 336ddbe13bdfc729495f5bfb8cc89b4360916157 Mon Sep 17 00:00:00 2001 From: Ben Howard Date: Mon, 20 Aug 2012 14:52:31 -0600 Subject: Added "userless" mode to cloud-init for handling the creation of the users and the default user on Ubuntu. cloudinit/config/cc_users_groups.py: new cloud-config module for creating users and groups on instance initialization. - Creates users and group - Sets "user" directive used in ssh_import_id cloudinit/config/cc_ssh_import_id.py: module will rely upon users_groups for setting the default user. Removed assumption of 'ubuntu' user. cloudinit/distros/__init__.py: Added new abstract methods for getting and creating the default user. cloudinit/distros/ubuntu.py: Defined abstract methods for getting and and creating the default 'ubuntu' user on Ubuntu instances. cloudinit/util.py: Added ability to hide command run through util.subp to prevent the commands from showing in the logs. Used by user_groups cloud-config module. config/cloud.cfg: Removed "user: ubuntu" directive and replaced with new user-less syntax. doc/examples/cloud-config.txt: Documented the creation of users and groups. --- cloudinit/config/cc_ssh_import_id.py | 7 +- cloudinit/config/cc_users_groups.py | 256 +++++++++++++++++++++++++++++++++++ cloudinit/distros/__init__.py | 8 ++ cloudinit/distros/ubuntu.py | 63 ++++++++- cloudinit/util.py | 12 +- 5 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 cloudinit/config/cc_users_groups.py (limited to 'cloudinit') diff --git a/cloudinit/config/cc_ssh_import_id.py b/cloudinit/config/cc_ssh_import_id.py index c58b28ec..f18e1fc5 100644 --- a/cloudinit/config/cc_ssh_import_id.py +++ b/cloudinit/config/cc_ssh_import_id.py @@ -32,7 +32,12 @@ def handle(name, cfg, _cloud, log, args): if len(args) > 1: ids = args[1:] else: - user = util.get_cfg_option_str(cfg, "user", "ubuntu") + try: + user = cloud.distro.get_default_username() + except NotImplementedError: + pass + + user = None ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) if len(ids) == 0: diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py new file mode 100644 index 00000000..1a428217 --- /dev/null +++ b/cloudinit/config/cc_users_groups.py @@ -0,0 +1,256 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2012 Canonical Ltd. +# +# Author: Ben Howard +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program 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 this program. If not, see . + +import grp +import pwd +import os +import traceback + +from cloudinit import templater +from cloudinit import util +from cloudinit import ssh_util +from cloudinit.settings import PER_INSTANCE + +frequency = PER_INSTANCE + +def handle(name, cfg, cloud, log, _args): + + groups_cfg = None + users_cfg = None + user_zero = None + + if 'groups' in cfg: + groups_cfg = cfg['groups'] + create_groups(groups_cfg, log) + + if 'users' in cfg: + users_cfg = cfg['users'] + user_zero = users_cfg.keys()[0] + + for name, user_config in users_cfg.iteritems(): + if name == "default" and user_config: + log.info("Creating default user") + + # Create the default user if so defined + try: + cloud.distro.add_default_user() + + except NotImplementedError as e: + log.warn(("Distro has not implemented default user" + "creation. No default user will be created")) + + # Get the distro user + if user_zero == 'default': + try: + user_zero = cloud.distro.get_default_username() + + except NotImplementedError: + pass + + else: + create_user(name, user_config, log, cloud) + + # Override user directive + if user_zero and check_user(user_zero): + cfg['user'] = user_zero + log.info("Override user directive with '%s'" % user_zero) + + +def check_user(user): + try: + user = pwd.getpwnam(user) + return True + + except KeyError: + return False + + return False + +def create_user(user, user_config, log, cloud): + # Iterate over the users definition and create the users + + if check_user(user): + log.warn("User %s already exists, skipping." % user) + + else: + log.info("Creating user %s" % user) + + adduser_cmd = ['useradd', user] + adduser_opts = { + "gecos": '-c', + "homedir": '--home', + "primary-group": '-g', + "groups": '-G', + "passwd": '-p', + "shell": '-s', + "expiredate": '-e', + "inactive": '-f', + } + + adduser_opts_flags = { + "no-user-group": '-N', + "system": '-r', + "no-log-init": '-l', + "no-create-home": "-M", + } + + # Now check the value and create the command + for option in user_config: + value = user_config[option] + if option in adduser_opts and value \ + and type(value).__name__ == "str": + adduser_cmd.extend([adduser_opts[option], value]) + + if option in adduser_opts_flags and value: + adduser_cmd.append(adduser_opts_flags[option]) + + # Default to creating home directory unless otherwise directed + # Also, we do not create home directories for system users. + if "no-create-home" not in user_config and \ + "system" not in user_config: + adduser_cmd.append('-m') + + print adduser_cmd + + # Create the user + try: + util.subp(adduser_cmd, + hidden="cloudinit.user_config.cc_users_groups(%s)" % user) + + except Exception as e: + log.warn("Failed to create user %s due to error.\n%s" % user) + + + # Double check to make sure that the user exists + if not check_user(user): + log.warn("User creation for %s failed for unknown reasons" % user) + return False + + # unlock the password if so-user_configured + if 'lock-passwd' not in user_config or \ + user_config['lock-passwd']: + + try: + util.subp(['passwd', '-l', user]) + + except Exception as e: + log.warn("Failed to disable password logins for user %s\n%s" \ + % (user, e)) + + # write out sudo options + if 'sudo' in user_config: + write_sudo(user, user_config['sudo'], log) + + # import ssh id's from launchpad + if 'ssh-import-id' in user_config: + import_ssh_id(user, user_config['ssh-import-id'], log) + + # write ssh-authorized-keys + if 'ssh-authorized-keys' in user_config: + keys = set(user_config['ssh-authorized-keys']) or [] + user_home = pwd.getpwnam(user).pw_dir + ssh_util.setup_user_keys(keys, user, None, cloud.paths) + +def import_ssh_id(user, keys, log): + + if not os.path.exists('/usr/bin/ssh-import-id'): + log.warn("ssh-import-id does not exist on this system, skipping") + return + + cmd = ["sudo", "-Hu", user, "ssh-import-id"] + keys + log.debug("Importing ssh ids for user %s.", user) + + try: + util.subp(cmd, capture=False) + + except util.ProcessExecutionError as e: + log.warn("Failed to run command to import %s ssh ids", user) + log.warn(traceback.print_exc(e)) + + +def write_sudo(user, rules, log): + sudo_file = "/etc/sudoers.d/90-cloud-init-users" + + content = "%s %s" % (user, rules) + if type(rules).__name__ == "list": + content = "" + for rule in rules: + content += "%s %s\n" % (user, rule) + + if not os.path.exists(sudo_file): + content = "# Added by cloud-init\n%s\n" % content + util.write_file(sudo_file, content, 0644) + + else: + old_content = None + try: + with open(sudo_file, 'r') as f: + old_content = f.read() + f.close() + + except IOError as e: + log.warn("Failed to read %s, not adding sudo rules for %s" % \ + (sudo_file, user)) + + content = "%s\n\n%s" % (old_content, content) + util.write_file(sudo_file, content, 0644) + +def create_groups(groups, log): + existing_groups = [x.gr_name for x in grp.getgrall()] + existing_users = [x.pw_name for x in pwd.getpwall()] + + for group in groups: + + group_add_cmd = ['groupadd'] + group_name = None + group_members = [] + + if type(group).__name__ == "dict": + group_name = [ x for x in group ][0] + for user in group[group_name]: + if user in existing_users: + group_members.append(user) + else: + log.warn("Unable to add non-existant user '%s' to" \ + " group '%s'" % (user, group_name)) + else: + group_name = group + group_add_cmd.append(group) + + group_add_cmd.append(group_name) + + # Check if group exists, and then add it doesn't + if group_name in existing_groups: + log.warn("Group '%s' already exists, skipping creation." % \ + group_name) + + else: + try: + util.subp(group_add_cmd) + log.info("Created new group %s" % group) + + except Exception as e: + log.warn("Failed to create group %s\n%s" % (group, e)) + + # Add members to the group, if so defined + if len(group_members) > 0: + for member in group_members: + util.subp(['usermod', '-a', '-G', group_name, member]) + log.info("Added user '%s' to group '%s'" % (member, group)) + + diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index da4d0180..8aec1199 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -46,6 +46,14 @@ class Distro(object): self._cfg = cfg self.name = name + @abc.abstractmethod + def add_default_user(self): + raise NotImplementedError() + + @abc.abstractmethod + def get_default_username(self): + raise NotImplementedError() + @abc.abstractmethod def install_packages(self, pkglist): raise NotImplementedError() diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 77c2aff4..e6672c4f 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -7,6 +7,7 @@ # Author: Scott Moser # Author: Juerg Haefliger # Author: Joshua Harlow +# Author: Ben Howard # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as @@ -20,12 +21,70 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from cloudinit import distros from cloudinit.distros import debian - +from cloudinit import helpers from cloudinit import log as logging +from cloudinit import util +from cloudinit.settings import PER_INSTANCE + +import pwd LOG = logging.getLogger(__name__) class Distro(debian.Distro): - pass + + distro_name = 'ubuntu' + __default_user_name__ = 'ubuntu-test' + + def __init__(self, name, cfg, paths): + distros.Distro.__init__(self, name, cfg, paths) + # This will be used to restrict certain + # calls from repeatly happening (when they + # should only happen say once per instance...) + self._runner = helpers.Runners(paths) + + def get_default_username(self): + return self.__default_user_name__ + + def add_default_user(self): + # Adds the ubuntu user using the rules: + # - Password is 'ubuntu', but is locked + # - nopasswd sudo access + + + if self.__default_user_name__ in [x[0] for x in pwd.getpwall()]: + LOG.warn("'%s' user already exists, not creating it." % \ + self.__default_user_name__) + return + + try: + util.subp(['adduser', + '--shell', '/bin/bash', + '--home', '/home/%s' % self.__default_user_name__, + '--disabled-password', + '--gecos', 'Ubuntu', + self.__default_user_name__, + ]) + + pass_string = '%(u)s:%(u)s' % {'u': self.__default_user_name__} + util.subp(['chpasswd'], pass_string) + util.subp(['passwd', '-l', self.__default_user_name__]) + + ubuntu_sudoers=""" +# Added by cloud-init +# %(user)s user is default user in cloud-images. +# It needs passwordless sudo functionality. +%(user)s ALL=(ALL) NOPASSWD:ALL +""" % { 'user': self.__default_user_name__ } + + util.write_file('/etc/sudoers.d/90-cloud-init-ubuntu', + ubuntu_sudoers, + mode=0440) + + LOG.info("Added default 'ubuntu' user with passwordless sudo") + + except Exception as e: + util.logexc(LOG, "Failed to create %s user\n%s" % + (self.__default_user_name__, e)) diff --git a/cloudinit/util.py b/cloudinit/util.py index a8c0cceb..0fbf9832 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -1329,12 +1329,18 @@ def delete_dir_contents(dirname): del_file(node_fullpath) -def subp(args, data=None, rcs=None, env=None, capture=True, shell=False): +def subp(args, data=None, rcs=None, env=None, capture=True, shell=False, hidden=False): if rcs is None: rcs = [0] try: - LOG.debug(("Running command %s with allowed return codes %s" - " (shell=%s, capture=%s)"), args, rcs, shell, capture) + + if not hidden: + LOG.debug(("Running command %s with allowed return codes %s" + " (shell=%s, capture=%s)"), args, rcs, shell, capture) + else: + LOG.debug(("Running hidden command to protect sensative output " + " Calling function: %s" ), hidden) + if not capture: stdout = None stderr = None -- cgit v1.2.3 From 8aa59086146062fc767349be086fe0c35bf9c477 Mon Sep 17 00:00:00 2001 From: Ben Howard Date: Mon, 20 Aug 2012 15:39:39 -0600 Subject: Dropped hidden command; replaced with logstring. Also changed useradd command to use log options over short --- cloudinit/config/cc_users_groups.py | 31 +++++++++++++++++++------------ cloudinit/distros/ubuntu.py | 5 +++-- cloudinit/util.py | 8 ++++---- 3 files changed, 26 insertions(+), 18 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py index 1a428217..62761aa4 100644 --- a/cloudinit/config/cc_users_groups.py +++ b/cloudinit/config/cc_users_groups.py @@ -91,21 +91,22 @@ def create_user(user, user_config, log, cloud): log.info("Creating user %s" % user) adduser_cmd = ['useradd', user] + x_adduser_cmd = adduser_cmd adduser_opts = { - "gecos": '-c', + "gecos": '--comment', "homedir": '--home', - "primary-group": '-g', - "groups": '-G', - "passwd": '-p', - "shell": '-s', - "expiredate": '-e', - "inactive": '-f', + "primary-group": '--gid', + "groups": '--groups', + "passwd": '--password', + "shell": '--shell', + "expiredate": '--expiredate', + "inactive": '--inactive', } adduser_opts_flags = { - "no-user-group": '-N', - "system": '-r', - "no-log-init": '-l', + "no-user-group": '--no-user-group', + "system": '--system', + "no-log-init": '--no-log-init', "no-create-home": "-M", } @@ -116,8 +117,15 @@ def create_user(user, user_config, log, cloud): and type(value).__name__ == "str": adduser_cmd.extend([adduser_opts[option], value]) + # Redact the password field from the logs + if option != "password": + x_adduser_cmd.extend([adduser_opts[option], value]) + else: + x_adduser_cmd.extend([adduser_opts[option], 'REDACTED']) + if option in adduser_opts_flags and value: adduser_cmd.append(adduser_opts_flags[option]) + x_adduser_cmd.append(adduser_opts_flags[option]) # Default to creating home directory unless otherwise directed # Also, we do not create home directories for system users. @@ -129,8 +137,7 @@ def create_user(user, user_config, log, cloud): # Create the user try: - util.subp(adduser_cmd, - hidden="cloudinit.user_config.cc_users_groups(%s)" % user) + util.subp(adduser_cmd, logstring=x_adduser_cmd) except Exception as e: log.warn("Failed to create user %s due to error.\n%s" % user) diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index e6672c4f..fbca5eb5 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -65,11 +65,12 @@ class Distro(debian.Distro): '--home', '/home/%s' % self.__default_user_name__, '--disabled-password', '--gecos', 'Ubuntu', - self.__default_user_name__, + self.__default_user_name__, ]) pass_string = '%(u)s:%(u)s' % {'u': self.__default_user_name__} - util.subp(['chpasswd'], pass_string) + x_pass_string = '%(u)s:REDACTED' % {'u': self.__default_user_name__} + util.subp(['chpasswd'], pass_string, logstring=x_pass_string) util.subp(['passwd', '-l', self.__default_user_name__]) ubuntu_sudoers=""" diff --git a/cloudinit/util.py b/cloudinit/util.py index 0fbf9832..a7d72d59 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -1329,17 +1329,17 @@ def delete_dir_contents(dirname): del_file(node_fullpath) -def subp(args, data=None, rcs=None, env=None, capture=True, shell=False, hidden=False): +def subp(args, data=None, rcs=None, env=None, capture=True, shell=False, logstring=False): if rcs is None: rcs = [0] try: - if not hidden: + if not logstring: LOG.debug(("Running command %s with allowed return codes %s" " (shell=%s, capture=%s)"), args, rcs, shell, capture) else: - LOG.debug(("Running hidden command to protect sensative output " - " Calling function: %s" ), hidden) + LOG.debug(("Running hidden command to protect sensitive input/output " + " logstring: %s" ), logstring) if not capture: stdout = None -- cgit v1.2.3 From 3f4a556e59b127d2fb6ebb57a8a42f6a71248b59 Mon Sep 17 00:00:00 2001 From: Ben Howard Date: Tue, 21 Aug 2012 13:03:47 -0600 Subject: Implemented MP feedback. --- cloudinit/config/cc_ssh_import_id.py | 7 +- cloudinit/config/cc_users_groups.py | 245 +++++------------------------------ cloudinit/distros/__init__.py | 187 +++++++++++++++++++++++++- cloudinit/distros/ubuntu.py | 57 ++++---- 4 files changed, 246 insertions(+), 250 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/config/cc_ssh_import_id.py b/cloudinit/config/cc_ssh_import_id.py index f18e1fc5..e733d14a 100644 --- a/cloudinit/config/cc_ssh_import_id.py +++ b/cloudinit/config/cc_ssh_import_id.py @@ -25,19 +25,20 @@ from cloudinit import util distros = ['ubuntu'] -def handle(name, cfg, _cloud, log, args): +def handle(name, cfg, cloud, log, args): if len(args) != 0: user = args[0] ids = [] if len(args) > 1: ids = args[1:] else: + user = None + try: - user = cloud.distro.get_default_username() + user = cloud.distro.get_configured_user() except NotImplementedError: pass - user = None ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) if len(ids) == 0: diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py index 62761aa4..828b0d94 100644 --- a/cloudinit/config/cc_users_groups.py +++ b/cloudinit/config/cc_users_groups.py @@ -28,236 +28,57 @@ from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE -def handle(name, cfg, cloud, log, _args): +def handle(name, cfg, cloud, log, _args): groups_cfg = None users_cfg = None user_zero = None if 'groups' in cfg: - groups_cfg = cfg['groups'] - create_groups(groups_cfg, log) + for group in cfg['groups']: + if isinstance(group, dict): + for name, values in group.iteritems(): + if isinstance(values, list): + cloud.distro.create_group(name, values) + elif isinstance(values, str): + cloud.distro.create_group(name, values.split(',')) + else: + cloud.distro.create_group(item, []) if 'users' in cfg: - users_cfg = cfg['users'] - user_zero = users_cfg.keys()[0] + user_zero = None + + for name, user_config in cfg['users'].iteritems(): + if not user_zero: + user_zero = name - for name, user_config in users_cfg.iteritems(): + # Handle the default user creation if name == "default" and user_config: log.info("Creating default user") # Create the default user if so defined try: - cloud.distro.add_default_user() - - except NotImplementedError as e: - log.warn(("Distro has not implemented default user" - "creation. No default user will be created")) - - # Get the distro user - if user_zero == 'default': - try: - user_zero = cloud.distro.get_default_username() - - except NotImplementedError: - pass - - else: - create_user(name, user_config, log, cloud) - - # Override user directive - if user_zero and check_user(user_zero): - cfg['user'] = user_zero - log.info("Override user directive with '%s'" % user_zero) - - -def check_user(user): - try: - user = pwd.getpwnam(user) - return True - - except KeyError: - return False + cloud.distro.add_default_user() - return False + if user_zero == name: + user_zero = cloud.distro.get_default_user() -def create_user(user, user_config, log, cloud): - # Iterate over the users definition and create the users - - if check_user(user): - log.warn("User %s already exists, skipping." % user) - - else: - log.info("Creating user %s" % user) - - adduser_cmd = ['useradd', user] - x_adduser_cmd = adduser_cmd - adduser_opts = { - "gecos": '--comment', - "homedir": '--home', - "primary-group": '--gid', - "groups": '--groups', - "passwd": '--password', - "shell": '--shell', - "expiredate": '--expiredate', - "inactive": '--inactive', - } - - adduser_opts_flags = { - "no-user-group": '--no-user-group', - "system": '--system', - "no-log-init": '--no-log-init', - "no-create-home": "-M", - } + except NotImplementedError as e: - # Now check the value and create the command - for option in user_config: - value = user_config[option] - if option in adduser_opts and value \ - and type(value).__name__ == "str": - adduser_cmd.extend([adduser_opts[option], value]) + if user_zero == name: + user_zero = None - # Redact the password field from the logs - if option != "password": - x_adduser_cmd.extend([adduser_opts[option], value]) + log.warn("Distro has not implemented default user " \ + "creation. No default user will be created") else: - x_adduser_cmd.extend([adduser_opts[option], 'REDACTED']) - - if option in adduser_opts_flags and value: - adduser_cmd.append(adduser_opts_flags[option]) - x_adduser_cmd.append(adduser_opts_flags[option]) - - # Default to creating home directory unless otherwise directed - # Also, we do not create home directories for system users. - if "no-create-home" not in user_config and \ - "system" not in user_config: - adduser_cmd.append('-m') - - print adduser_cmd - - # Create the user - try: - util.subp(adduser_cmd, logstring=x_adduser_cmd) - - except Exception as e: - log.warn("Failed to create user %s due to error.\n%s" % user) - - - # Double check to make sure that the user exists - if not check_user(user): - log.warn("User creation for %s failed for unknown reasons" % user) - return False - - # unlock the password if so-user_configured - if 'lock-passwd' not in user_config or \ - user_config['lock-passwd']: - - try: - util.subp(['passwd', '-l', user]) - - except Exception as e: - log.warn("Failed to disable password logins for user %s\n%s" \ - % (user, e)) - - # write out sudo options - if 'sudo' in user_config: - write_sudo(user, user_config['sudo'], log) - - # import ssh id's from launchpad - if 'ssh-import-id' in user_config: - import_ssh_id(user, user_config['ssh-import-id'], log) - - # write ssh-authorized-keys - if 'ssh-authorized-keys' in user_config: - keys = set(user_config['ssh-authorized-keys']) or [] - user_home = pwd.getpwnam(user).pw_dir - ssh_util.setup_user_keys(keys, user, None, cloud.paths) - -def import_ssh_id(user, keys, log): - - if not os.path.exists('/usr/bin/ssh-import-id'): - log.warn("ssh-import-id does not exist on this system, skipping") - return - - cmd = ["sudo", "-Hu", user, "ssh-import-id"] + keys - log.debug("Importing ssh ids for user %s.", user) - - try: - util.subp(cmd, capture=False) - - except util.ProcessExecutionError as e: - log.warn("Failed to run command to import %s ssh ids", user) - log.warn(traceback.print_exc(e)) - - -def write_sudo(user, rules, log): - sudo_file = "/etc/sudoers.d/90-cloud-init-users" - - content = "%s %s" % (user, rules) - if type(rules).__name__ == "list": - content = "" - for rule in rules: - content += "%s %s\n" % (user, rule) - - if not os.path.exists(sudo_file): - content = "# Added by cloud-init\n%s\n" % content - util.write_file(sudo_file, content, 0644) - - else: - old_content = None - try: - with open(sudo_file, 'r') as f: - old_content = f.read() - f.close() - - except IOError as e: - log.warn("Failed to read %s, not adding sudo rules for %s" % \ - (sudo_file, user)) - - content = "%s\n\n%s" % (old_content, content) - util.write_file(sudo_file, content, 0644) - -def create_groups(groups, log): - existing_groups = [x.gr_name for x in grp.getgrall()] - existing_users = [x.pw_name for x in pwd.getpwall()] - - for group in groups: - - group_add_cmd = ['groupadd'] - group_name = None - group_members = [] - - if type(group).__name__ == "dict": - group_name = [ x for x in group ][0] - for user in group[group_name]: - if user in existing_users: - group_members.append(user) - else: - log.warn("Unable to add non-existant user '%s' to" \ - " group '%s'" % (user, group_name)) - else: - group_name = group - group_add_cmd.append(group) - - group_add_cmd.append(group_name) - - # Check if group exists, and then add it doesn't - if group_name in existing_groups: - log.warn("Group '%s' already exists, skipping creation." % \ - group_name) - - else: - try: - util.subp(group_add_cmd) - log.info("Created new group %s" % group) - - except Exception as e: - log.warn("Failed to create group %s\n%s" % (group, e)) - - # Add members to the group, if so defined - if len(group_members) > 0: - for member in group_members: - util.subp(['usermod', '-a', '-G', group_name, member]) - log.info("Added user '%s' to group '%s'" % (member, group)) + # Make options friendly for distro.create_user + new_opts = {} + if isinstance(user_config, dict): + for opt in user_config: + new_opts[opt.replace('-', '')] = user_config[opt] + cloud.distro.create_user(name, **new_opts) + if user_zero: + cloud.distro.set_configured_user(user_zero) + log.info("Set configured user for this instance to %s" % user_zero) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 8aec1199..776a2417 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -7,6 +7,7 @@ # Author: Scott Moser # Author: Juerg Haefliger # Author: Joshua Harlow +# Author: Ben Howard # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3, as @@ -23,10 +24,13 @@ from StringIO import StringIO import abc - +import pwd +import grp +import os from cloudinit import importer from cloudinit import log as logging from cloudinit import util +from cloudinit import ssh_util # TODO: Make this via config?? IFACE_ACTIONS = { @@ -51,7 +55,7 @@ class Distro(object): raise NotImplementedError() @abc.abstractmethod - def get_default_username(self): + def get_default_user(self): raise NotImplementedError() @abc.abstractmethod @@ -158,6 +162,185 @@ class Distro(object): util.logexc(LOG, "Running interface command %s failed", cmd) return False + def isuser(self, name): + try: + if pwd.getpwnam(name): + return True + except KeyError: + return False + + def set_configured_user(self, name): + self.default_user = name + + def get_configured_user(self): + try: + return getattr(self, 'default_user') + except: + return None + + def create_user(self, name, **kwargs): + """ + Creates users for the system using the GNU passwd tools. This + will work on an GNU system. This should be overriden on + distros where useradd is not desirable or not available. + """ + + if self.isuser(name): + LOG.warn("User %s already exists, skipping." % name) + else: + LOG.debug("Creating name %s" % name) + + adduser_cmd = ['useradd', name] + x_adduser_cmd = adduser_cmd + + # Since we are creating users, we want to carefully validate the + # inputs. If something goes wrong, we can end up with a system + # that nobody can login to. + adduser_opts = { + "gecos": '--comment', + "homedir": '--home', + "primarygroup": '--gid', + "groups": '--groups', + "passwd": '--password', + "shell": '--shell', + "expiredate": '--expiredate', + "inactive": '--inactive', + } + + adduser_opts_flags = { + "nousergroup": '--no-user-group', + "system": '--system', + "nologinit": '--no-log-init', + "nocreatehome": "-M", + } + + # Now check the value and create the command + for option in kwargs: + value = kwargs[option] + if option in adduser_opts and value \ + and isinstance(value, str): + adduser_cmd.extend([adduser_opts[option], value]) + + # Redact the password field from the logs + if option != "password": + x_adduser_cmd.extend([adduser_opts[option], value]) + else: + x_adduser_cmd.extend([adduser_opts[option], 'REDACTED']) + + if option in adduser_opts_flags and value: + adduser_cmd.append(adduser_opts_flags[option]) + x_adduser_cmd.append(adduser_opts_flags[option]) + + # Default to creating home directory unless otherwise directed + # Also, we do not create home directories for system users. + if "nocreatehome" not in kwargs and "system" not in kwargs: + adduser_cmd.append('-m') + + # Create the user + try: + util.subp(adduser_cmd, logstring=x_adduser_cmd) + except Exception as e: + util.logexc(LOG, "Failed to create user %s due to error.", e) + return False + + # Set password if plain-text password provided + if 'plain_text_passwd' in kwargs and kwargs['plain_text_passwd']: + self.set_passwd(name, kwargs['plain_text_passwd']) + + # Default locking down the account. + if ('lockpasswd' not in kwargs and + ('lockpasswd' in kwargs and kwargs['lockpasswd']) or + 'system' not in kwargs): + try: + util.subp(['passwd', '--lock', name]) + except Exception as e: + util.logexc(LOG, ("Failed to disable password logins for" + "user %s" % name), e) + return False + + # Configure sudo access + if 'sudo' in kwargs: + self.write_sudo_rules(name, kwargs['sudo']) + + # Import SSH keys + if 'sshauthorizedkeys' in kwargs: + keys = set(kwargs['sshauthorizedkeys']) or [] + ssh_util.setup_user_keys(keys, name, None, self._paths) + + return True + + def set_passwd(self, user, passwd, hashed=False): + pass_string = '%s:%s' % (user, passwd) + cmd = ['chpasswd'] + + if hashed: + cmd.append('--encrypted') + + try: + util.subp(cmd, pass_string, logstring="chpasswd for %s" % user) + except Exception as e: + util.logexc(LOG, "Failed to set password for %s" % user) + return False + + return True + + def write_sudo_rules(self, + user, + rules, + sudo_file="/etc/sudoers.d/90-cloud-init-users", + ): + + content_header = "# user rules for %s" % user + content = "%s\n%s %s\n\n" % (content_header, user, rules) + + if isinstance(rules, list): + content = "%s\n" % content_header + for rule in rules: + content += "%s %s\n" % (user, rule) + content += "\n" + + if not os.path.exists(sudo_file): + util.write_file(sudo_file, content, 0644) + + else: + try: + with open(sudo_file, 'a') as f: + f.write(content) + f.close() + except IOError as e: + util.logexc(LOG, "Failed to write %s" % sudo_file, e) + + def isgroup(self, name): + try: + if grp.getgrpnam(name): + return True + except: + return False + + def create_group(self, name, members): + group_add_cmd = ['groupadd', name] + + # Check if group exists, and then add it doesn't + if self.isgroup(name): + LOG.warn("Skipping creation of existing group '%s'" % name) + else: + try: + util.subp(group_add_cmd) + LOG.info("Created new group %s" % name) + except Exception as e: + util.logexc("Failed to create group %s" % name, e) + + # Add members to the group, if so defined + if len(members) > 0: + for member in members: + if not self.isuser(member): + LOG.warn("Unable to add group member '%s' to group '%s'" + "; user does not exist." % (member, name)) + continue + + util.subp(['usermod', '-a', '-G', name, member]) + LOG.info("Added user '%s' to group '%s'" % (member, name)) + def fetch(name): locs = importer.find_module(name, diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index fbca5eb5..423fee73 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -27,7 +27,7 @@ from cloudinit import helpers from cloudinit import log as logging from cloudinit import util from cloudinit.settings import PER_INSTANCE - +import hashlib import pwd LOG = logging.getLogger(__name__) @@ -36,7 +36,7 @@ LOG = logging.getLogger(__name__) class Distro(debian.Distro): distro_name = 'ubuntu' - __default_user_name__ = 'ubuntu-test' + __default_user_name__ = 'ubuntu' def __init__(self, name, cfg, paths): distros.Distro.__init__(self, name, cfg, paths) @@ -45,7 +45,7 @@ class Distro(debian.Distro): # should only happen say once per instance...) self._runner = helpers.Runners(paths) - def get_default_username(self): + def get_default_user(self): return self.__default_user_name__ def add_default_user(self): @@ -53,39 +53,30 @@ class Distro(debian.Distro): # - Password is 'ubuntu', but is locked # - nopasswd sudo access + self.create_user(self.__default_user_name__, + plain_text_passwd=self.__default_user_name__, + home="/home/%s" % self.__default_user_name__, + shell="/bin/bash", + lockpasswd=True, + gecos="Ubuntu", + sudo="ALL=(ALL) NOPASSWD:ALL") - if self.__default_user_name__ in [x[0] for x in pwd.getpwall()]: - LOG.warn("'%s' user already exists, not creating it." % \ - self.__default_user_name__) - return - - try: - util.subp(['adduser', - '--shell', '/bin/bash', - '--home', '/home/%s' % self.__default_user_name__, - '--disabled-password', - '--gecos', 'Ubuntu', - self.__default_user_name__, - ]) + LOG.info("Added default 'ubuntu' user with passwordless sudo") - pass_string = '%(u)s:%(u)s' % {'u': self.__default_user_name__} - x_pass_string = '%(u)s:REDACTED' % {'u': self.__default_user_name__} - util.subp(['chpasswd'], pass_string, logstring=x_pass_string) - util.subp(['passwd', '-l', self.__default_user_name__]) + def create_user(self, name, **kargs): - ubuntu_sudoers=""" -# Added by cloud-init -# %(user)s user is default user in cloud-images. -# It needs passwordless sudo functionality. -%(user)s ALL=(ALL) NOPASSWD:ALL -""" % { 'user': self.__default_user_name__ } + if not super(Distro, self).create_user(name, **kargs): + return False - util.write_file('/etc/sudoers.d/90-cloud-init-ubuntu', - ubuntu_sudoers, - mode=0440) + if 'sshimportid' in kargs: + cmd = ["sudo", "-Hu", name, "ssh-import-id"] + kargs['sshimportid'] + LOG.debug("Importing ssh ids for user %s, post user creation." + % name) - LOG.info("Added default 'ubuntu' user with passwordless sudo") + try: + util.subp(cmd, capture=True) + except util.ProcessExecutionError as e: + util.logexc(LOG, "Failed to import %s ssh ids", name) + raise e - except Exception as e: - util.logexc(LOG, "Failed to create %s user\n%s" % - (self.__default_user_name__, e)) + return True -- cgit v1.2.3 From a6752e739a0bb9052585b9b043ce1964bd77bb42 Mon Sep 17 00:00:00 2001 From: Ben Howard Date: Wed, 22 Aug 2012 16:32:18 -0600 Subject: Simplified users[0] detection, and ensured compatability with previous user password control code --- cloudinit/config/cc_set_passwords.py | 12 ++++++++++-- cloudinit/config/cc_ssh_import_id.py | 11 ++++++----- cloudinit/config/cc_users_groups.py | 4 ---- cloudinit/distros/__init__.py | 21 ++++++++------------- 4 files changed, 24 insertions(+), 24 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/config/cc_set_passwords.py b/cloudinit/config/cc_set_passwords.py index ab266741..4bf62aa9 100644 --- a/cloudinit/config/cc_set_passwords.py +++ b/cloudinit/config/cc_set_passwords.py @@ -50,8 +50,16 @@ def handle(_name, cfg, cloud, log, args): expire = util.get_cfg_option_bool(chfg, 'expire', expire) if not plist and password: - user = util.get_cfg_option_str(cfg, "user", "ubuntu") - plist = "%s:%s" % (user, password) + user = cloud.distro.get_default_user() + + if 'users' in cfg: + user_zero = cfg['users'].keys()[0] + + if user_zero != "default": + user = user_zero + + if user: + plist = "%s:%s" % (user, password) errors = [] if plist: diff --git a/cloudinit/config/cc_ssh_import_id.py b/cloudinit/config/cc_ssh_import_id.py index e733d14a..9aee2166 100644 --- a/cloudinit/config/cc_ssh_import_id.py +++ b/cloudinit/config/cc_ssh_import_id.py @@ -32,12 +32,13 @@ def handle(name, cfg, cloud, log, args): if len(args) > 1: ids = args[1:] else: - user = None + user = cloud.distro.get_default_user() - try: - user = cloud.distro.get_configured_user() - except NotImplementedError: - pass + if 'users' in cfg: + user_zero = cfg['users'].keys()[0] + + if user_zero != "default": + user = user_zero ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py index 828b0d94..7e5ecc7b 100644 --- a/cloudinit/config/cc_users_groups.py +++ b/cloudinit/config/cc_users_groups.py @@ -78,7 +78,3 @@ def handle(name, cfg, cloud, log, _args): new_opts[opt.replace('-', '')] = user_config[opt] cloud.distro.create_user(name, **new_opts) - - if user_zero: - cloud.distro.set_configured_user(user_zero) - log.info("Set configured user for this instance to %s" % user_zero) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 776a2417..614545f2 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -49,15 +49,12 @@ class Distro(object): self._paths = paths self._cfg = cfg self.name = name + self.default_user = None @abc.abstractmethod def add_default_user(self): raise NotImplementedError() - @abc.abstractmethod - def get_default_user(self): - raise NotImplementedError() - @abc.abstractmethod def install_packages(self, pkglist): raise NotImplementedError() @@ -172,11 +169,9 @@ class Distro(object): def set_configured_user(self, name): self.default_user = name - def get_configured_user(self): - try: - return getattr(self, 'default_user') - except: - return None + def get_default_user(self): + return None + def create_user(self, name, **kwargs): """ @@ -241,7 +236,7 @@ class Distro(object): util.subp(adduser_cmd, logstring=x_adduser_cmd) except Exception as e: util.logexc(LOG, "Failed to create user %s due to error.", e) - return False + raise e # Set password if plain-text password provided if 'plain_text_passwd' in kwargs and kwargs['plain_text_passwd']: @@ -256,7 +251,7 @@ class Distro(object): except Exception as e: util.logexc(LOG, ("Failed to disable password logins for" "user %s" % name), e) - return False + raise e # Configure sudo access if 'sudo' in kwargs: @@ -280,7 +275,7 @@ class Distro(object): util.subp(cmd, pass_string, logstring="chpasswd for %s" % user) except Exception as e: util.logexc(LOG, "Failed to set password for %s" % user) - return False + raise e return True @@ -306,9 +301,9 @@ class Distro(object): try: with open(sudo_file, 'a') as f: f.write(content) - f.close() except IOError as e: util.logexc(LOG, "Failed to write %s" % sudo_file, e) + raise e def isgroup(self, name): try: -- cgit v1.2.3 From d59ab509f6482b8b3af4fd9f2728ad1209d5b2e2 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 22 Aug 2012 20:39:15 -0400 Subject: fix errors from 'make pep8' --- cloudinit/config/cc_set_passwords.py | 2 +- cloudinit/config/cc_ssh_import_id.py | 2 +- cloudinit/config/cc_users_groups.py | 12 ++++++------ cloudinit/distros/__init__.py | 3 +-- cloudinit/distros/ubuntu.py | 3 ++- cloudinit/util.py | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/config/cc_set_passwords.py b/cloudinit/config/cc_set_passwords.py index 4bf62aa9..7d0fbd9f 100644 --- a/cloudinit/config/cc_set_passwords.py +++ b/cloudinit/config/cc_set_passwords.py @@ -53,7 +53,7 @@ def handle(_name, cfg, cloud, log, args): user = cloud.distro.get_default_user() if 'users' in cfg: - user_zero = cfg['users'].keys()[0] + user_zero = cfg['users'].keys()[0] if user_zero != "default": user = user_zero diff --git a/cloudinit/config/cc_ssh_import_id.py b/cloudinit/config/cc_ssh_import_id.py index 9aee2166..cd97b99b 100644 --- a/cloudinit/config/cc_ssh_import_id.py +++ b/cloudinit/config/cc_ssh_import_id.py @@ -35,7 +35,7 @@ def handle(name, cfg, cloud, log, args): user = cloud.distro.get_default_user() if 'users' in cfg: - user_zero = cfg['users'].keys()[0] + user_zero = cfg['users'].keys()[0] if user_zero != "default": user = user_zero diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py index 7e5ecc7b..cbb351dc 100644 --- a/cloudinit/config/cc_users_groups.py +++ b/cloudinit/config/cc_users_groups.py @@ -17,14 +17,14 @@ # along with this program. If not, see . import grp -import pwd import os +import pwd import traceback +from cloudinit.settings import PER_INSTANCE +from cloudinit import ssh_util from cloudinit import templater from cloudinit import util -from cloudinit import ssh_util -from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE @@ -45,7 +45,7 @@ def handle(name, cfg, cloud, log, _args): else: cloud.distro.create_group(item, []) - if 'users' in cfg: + if 'users' in cfg: user_zero = None for name, user_config in cfg['users'].iteritems(): @@ -68,8 +68,8 @@ def handle(name, cfg, cloud, log, _args): if user_zero == name: user_zero = None - log.warn("Distro has not implemented default user " \ - "creation. No default user will be created") + log.warn("Distro has not implemented default user " + "creation. No default user will be created") else: # Make options friendly for distro.create_user new_opts = {} diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 2dfb1409..1f46c5aa 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -31,8 +31,8 @@ import re from cloudinit import importer from cloudinit import log as logging -from cloudinit import util from cloudinit import ssh_util +from cloudinit import util # TODO(harlowja): Make this via config?? IFACE_ACTIONS = { @@ -192,7 +192,6 @@ class Distro(object): def get_default_user(self): return None - def create_user(self, name, **kwargs): """ Creates users for the system using the GNU passwd tools. This diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 423fee73..a6d665e0 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -25,8 +25,9 @@ from cloudinit import distros from cloudinit.distros import debian from cloudinit import helpers from cloudinit import log as logging -from cloudinit import util from cloudinit.settings import PER_INSTANCE +from cloudinit import util + import hashlib import pwd diff --git a/cloudinit/util.py b/cloudinit/util.py index 7d56e8be..7f47341a 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -1337,10 +1337,10 @@ def subp(args, data=None, rcs=None, env=None, capture=True, shell=False, logstri if not logstring: LOG.debug(("Running command %s with allowed return codes %s" - " (shell=%s, capture=%s)"), args, rcs, shell, capture) + " (shell=%s, capture=%s)"), args, rcs, shell, capture) else: LOG.debug(("Running hidden command to protect sensitive input/output " - " logstring: %s" ), logstring) + " logstring: %s"), logstring) if not capture: stdout = None -- cgit v1.2.3 From 2de30c15e706a9610c19979886f13addecf7ea1b Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 22 Aug 2012 21:20:34 -0400 Subject: fix issues from 'make pylint' In an effort to pylint errors about NonImlementedError and add_default_user, I moved this method to distro and genericized it. Now, assuming a sane 'create_user' for the distro, this should work. Also: * removed the unused set_configured_user method --- cloudinit/config/cc_users_groups.py | 14 ++------------ cloudinit/distros/__init__.py | 28 +++++++++++++++++++++------- cloudinit/distros/ubuntu.py | 32 ++------------------------------ cloudinit/util.py | 7 ++++--- 4 files changed, 29 insertions(+), 52 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py index cbb351dc..1e241623 100644 --- a/cloudinit/config/cc_users_groups.py +++ b/cloudinit/config/cc_users_groups.py @@ -16,22 +16,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import grp -import os -import pwd -import traceback - from cloudinit.settings import PER_INSTANCE -from cloudinit import ssh_util -from cloudinit import templater -from cloudinit import util frequency = PER_INSTANCE def handle(name, cfg, cloud, log, _args): - groups_cfg = None - users_cfg = None user_zero = None if 'groups' in cfg: @@ -43,7 +33,7 @@ def handle(name, cfg, cloud, log, _args): elif isinstance(values, str): cloud.distro.create_group(name, values.split(',')) else: - cloud.distro.create_group(item, []) + cloud.distro.create_group(group, []) if 'users' in cfg: user_zero = None @@ -63,7 +53,7 @@ def handle(name, cfg, cloud, log, _args): if user_zero == name: user_zero = cloud.distro.get_default_user() - except NotImplementedError as e: + except NotImplementedError: if user_zero == name: user_zero = None diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 1f46c5aa..12a0d9b4 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -46,16 +46,33 @@ LOG = logging.getLogger(__name__) class Distro(object): __metaclass__ = abc.ABCMeta + default_user = None def __init__(self, name, cfg, paths): self._paths = paths self._cfg = cfg self.name = name - self.default_user = None @abc.abstractmethod def add_default_user(self): - raise NotImplementedError() + # Adds the distro user using the rules: + # - Password is same as username but is locked + # - nopasswd sudo access + + user = self.get_default_user() + if not user: + raise NotImplementedError("No Default user") + + self.create_user(user, + plain_text_passwd=user, + home="/home/%s" % user, + shell="/bin/bash", + lockpasswd=True, + gecos="%s%s" % (user[0:1].upper(),user[1:]), + sudo="ALL=(ALL) NOPASSWD:ALL") + + LOG.info("Added default '%s' user with passwordless sudo", user) + @abc.abstractmethod def install_packages(self, pkglist): @@ -186,11 +203,8 @@ class Distro(object): except KeyError: return False - def set_configured_user(self, name): - self.default_user = name - def get_default_user(self): - return None + return self.default_user def create_user(self, name, **kwargs): """ @@ -326,7 +340,7 @@ class Distro(object): def isgroup(self, name): try: - if grp.getgrpnam(name): + if grp.getgrnam(name): return True except: return False diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index a6d665e0..17584f15 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -21,48 +21,20 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from cloudinit import distros from cloudinit.distros import debian -from cloudinit import helpers from cloudinit import log as logging -from cloudinit.settings import PER_INSTANCE from cloudinit import util -import hashlib -import pwd - LOG = logging.getLogger(__name__) class Distro(debian.Distro): distro_name = 'ubuntu' - __default_user_name__ = 'ubuntu' + default_user = 'ubuntu' def __init__(self, name, cfg, paths): - distros.Distro.__init__(self, name, cfg, paths) - # This will be used to restrict certain - # calls from repeatly happening (when they - # should only happen say once per instance...) - self._runner = helpers.Runners(paths) - - def get_default_user(self): - return self.__default_user_name__ - - def add_default_user(self): - # Adds the ubuntu user using the rules: - # - Password is 'ubuntu', but is locked - # - nopasswd sudo access - - self.create_user(self.__default_user_name__, - plain_text_passwd=self.__default_user_name__, - home="/home/%s" % self.__default_user_name__, - shell="/bin/bash", - lockpasswd=True, - gecos="Ubuntu", - sudo="ALL=(ALL) NOPASSWD:ALL") - - LOG.info("Added default 'ubuntu' user with passwordless sudo") + super(Distro, self).__init__(self, name, cfg, paths) def create_user(self, name, **kargs): diff --git a/cloudinit/util.py b/cloudinit/util.py index 7f47341a..6872cc31 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -1330,7 +1330,8 @@ def delete_dir_contents(dirname): del_file(node_fullpath) -def subp(args, data=None, rcs=None, env=None, capture=True, shell=False, logstring=False): +def subp(args, data=None, rcs=None, env=None, capture=True, shell=False, + logstring=False): if rcs is None: rcs = [0] try: @@ -1339,8 +1340,8 @@ def subp(args, data=None, rcs=None, env=None, capture=True, shell=False, logstri LOG.debug(("Running command %s with allowed return codes %s" " (shell=%s, capture=%s)"), args, rcs, shell, capture) else: - LOG.debug(("Running hidden command to protect sensitive input/output " - " logstring: %s"), logstring) + LOG.debug(("Running hidden command to protect sensitive " + "input/output logstring: %s"), logstring) if not capture: stdout = None -- cgit v1.2.3 From bf03883b75637d4816c3712fbf63b2c42f6e648b Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 22 Aug 2012 21:32:49 -0400 Subject: distros: add_default_user is no longer abstract --- cloudinit/distros/__init__.py | 1 - 1 file changed, 1 deletion(-) (limited to 'cloudinit') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 12a0d9b4..478e3993 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -53,7 +53,6 @@ class Distro(object): self._cfg = cfg self.name = name - @abc.abstractmethod def add_default_user(self): # Adds the distro user using the rules: # - Password is same as username but is locked -- cgit v1.2.3 From 2916a7be3bf416651458dc6640d67bd9ca173f0d Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 22 Aug 2012 21:38:45 -0400 Subject: remove now pointless __init__ in ubuntu --- cloudinit/distros/ubuntu.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 17584f15..4b3f8572 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -33,9 +33,6 @@ class Distro(debian.Distro): distro_name = 'ubuntu' default_user = 'ubuntu' - def __init__(self, name, cfg, paths): - super(Distro, self).__init__(self, name, cfg, paths) - def create_user(self, name, **kargs): if not super(Distro, self).create_user(name, **kargs): -- cgit v1.2.3 From d05dd3963eda608df0b14042e8548d42c860835e Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 22 Aug 2012 22:35:18 -0400 Subject: fix duplicate flags being passed to useradd Fix bug here: adduser_cmd = ['useradd', name] x_adduser_cmd = adduser_cmd is different than x_adduser_cmd = ['useradd', name] The problem was they referenced the same list, and we were doubly appending. --- cloudinit/distros/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 478e3993..8da9a0b5 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -218,7 +218,7 @@ class Distro(object): LOG.debug("Creating name %s" % name) adduser_cmd = ['useradd', name] - x_adduser_cmd = adduser_cmd + x_adduser_cmd = ['useradd', name] # Since we are creating users, we want to carefully validate the # inputs. If something goes wrong, we can end up with a system @@ -254,7 +254,7 @@ class Distro(object): else: x_adduser_cmd.extend([adduser_opts[option], 'REDACTED']) - if option in adduser_opts_flags and value: + elif option in adduser_opts_flags and value: adduser_cmd.append(adduser_opts_flags[option]) x_adduser_cmd.append(adduser_opts_flags[option]) -- cgit v1.2.3 From fc8f46d5f49c29d234560749bdf9789812571327 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 22 Aug 2012 22:40:40 -0400 Subject: pep8 fixes --- cloudinit/distros/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 8da9a0b5..669dca18 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -67,12 +67,11 @@ class Distro(object): home="/home/%s" % user, shell="/bin/bash", lockpasswd=True, - gecos="%s%s" % (user[0:1].upper(),user[1:]), + gecos="%s%s" % (user[0:1].upper(), user[1:]), sudo="ALL=(ALL) NOPASSWD:ALL") LOG.info("Added default '%s' user with passwordless sudo", user) - @abc.abstractmethod def install_packages(self, pkglist): raise NotImplementedError() -- cgit v1.2.3 From 700cb1a5d568dae2ecc8a9620874cebca97536a5 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 22 Aug 2012 22:40:52 -0400 Subject: do not attempt the useradd command if user exists Previously we were only logging that the user existed and then still trying to run the command (which would raise error) As a result, none of the rest of the things would be done (sshimport id and such) --- cloudinit/distros/__init__.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 669dca18..686c6a9b 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -211,11 +211,6 @@ class Distro(object): distros where useradd is not desirable or not available. """ - if self.isuser(name): - LOG.warn("User %s already exists, skipping." % name) - else: - LOG.debug("Creating name %s" % name) - adduser_cmd = ['useradd', name] x_adduser_cmd = ['useradd', name] @@ -263,11 +258,15 @@ class Distro(object): adduser_cmd.append('-m') # Create the user - try: - util.subp(adduser_cmd, logstring=x_adduser_cmd) - except Exception as e: - util.logexc(LOG, "Failed to create user %s due to error.", e) - raise e + if self.isuser(name): + LOG.warn("User %s already exists, skipping." % name) + else: + LOG.debug("Creating name %s" % name) + try: + util.subp(adduser_cmd, logstring=x_adduser_cmd) + except Exception as e: + util.logexc(LOG, "Failed to create user %s due to error.", e) + raise e # Set password if plain-text password provided if 'plain_text_passwd' in kwargs and kwargs['plain_text_passwd']: -- cgit v1.2.3 From bbbaeca0c375dc166ef8ffe0598d5f384b722c00 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 22 Aug 2012 23:38:20 -0400 Subject: cc_ssh.py: add the "get user zero" logic, disable root even if no user cc_ssh.py was getting user of None, which ended up with a root user not getting ssh keys updated. That was bad. So, I duplicated the "get user zero" code that appeared other places here. Then, we disable the root user even if there is not a user. In that case we just use the string "NONE" in the disable message. --- cloudinit/config/cc_ssh.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'cloudinit') diff --git a/cloudinit/config/cc_ssh.py b/cloudinit/config/cc_ssh.py index 3431bd2a..439c8eb8 100644 --- a/cloudinit/config/cc_ssh.py +++ b/cloudinit/config/cc_ssh.py @@ -102,7 +102,16 @@ def handle(_name, cfg, cloud, log, _args): " %s to file %s"), keytype, keyfile) try: - user = util.get_cfg_option_str(cfg, 'user') + # TODO(utlemming): consolidate this stanza that occurs in: + # cc_ssh_import_id, cc_set_passwords, maybe cc_users_groups.py + user = cloud.distro.get_default_user() + + if 'users' in cfg: + user_zero = cfg['users'].keys()[0] + + if user_zero != "default": + user = user_zero + disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", DISABLE_ROOT_OPTS) @@ -124,7 +133,9 @@ def apply_credentials(keys, user, paths, disable_root, disable_root_opts): if user: ssh_util.setup_user_keys(keys, user, '', paths) - if disable_root and user: + if disable_root: + if not user: + user = "NONE" key_prefix = disable_root_opts.replace('$USER', user) else: key_prefix = '' -- cgit v1.2.3