summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloudinit/config/cc_ssh_import_id.py7
-rw-r--r--cloudinit/config/cc_users_groups.py245
-rw-r--r--cloudinit/distros/__init__.py187
-rw-r--r--cloudinit/distros/ubuntu.py57
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