summaryrefslogtreecommitdiff
path: root/cloudinit/config/cc_users_groups.py
diff options
context:
space:
mode:
authorBen Howard <ben.howard@canonical.com>2012-08-20 14:52:31 -0600
committerBen Howard <ben.howard@canonical.com>2012-08-20 14:52:31 -0600
commit336ddbe13bdfc729495f5bfb8cc89b4360916157 (patch)
tree3d551bde0b99b0db8c1c33f9bbd7e9e22acebb2f /cloudinit/config/cc_users_groups.py
parent4540821caa31dc9ed0bedf521cd36975ddafebfa (diff)
downloadcloud-init-git-336ddbe13bdfc729495f5bfb8cc89b4360916157.tar.gz
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.
Diffstat (limited to 'cloudinit/config/cc_users_groups.py')
-rw-r--r--cloudinit/config/cc_users_groups.py256
1 files changed, 256 insertions, 0 deletions
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 <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
+# 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 <http://www.gnu.org/licenses/>.
+
+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))
+
+