summaryrefslogtreecommitdiff
path: root/cloudinit/config/cc_users_groups.py
blob: 62761aa4b8d2dec475b995bd60275ba1265ff734 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# 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]
    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",
            }

    # 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])

            # 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 "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))