diff options
| author | Ben Howard <ben.howard@canonical.com> | 2012-08-21 13:03:47 -0600 | 
|---|---|---|
| committer | Ben Howard <ben.howard@canonical.com> | 2012-08-21 13:03:47 -0600 | 
| commit | 3f4a556e59b127d2fb6ebb57a8a42f6a71248b59 (patch) | |
| tree | cec63a461ef1d09bd79a805365d5488b23e74e85 | |
| parent | 8aa59086146062fc767349be086fe0c35bf9c477 (diff) | |
| download | vyos-cloud-init-3f4a556e59b127d2fb6ebb57a8a42f6a71248b59.tar.gz vyos-cloud-init-3f4a556e59b127d2fb6ebb57a8a42f6a71248b59.zip | |
Implemented MP feedback.
| -rw-r--r-- | cloudinit/config/cc_ssh_import_id.py | 7 | ||||
| -rw-r--r-- | cloudinit/config/cc_users_groups.py | 245 | ||||
| -rw-r--r-- | cloudinit/distros/__init__.py | 187 | ||||
| -rw-r--r-- | cloudinit/distros/ubuntu.py | 57 | 
4 files changed, 246 insertions, 250 deletions
| 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 <scott.moser@canonical.com>  #    Author: Juerg Haefliger <juerg.haefliger@hp.com>  #    Author: Joshua Harlow <harlowja@yahoo-inc.com> +#    Author: Ben Howard <ben.howard@canonical.com>  #  #    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 | 
