blob: e8f04c8ee020739b79d096a571bcbb823047380f [file] [log] [blame]
From c4576e56dbf84c0a10a71cb7fd609c2375b3d290 Mon Sep 17 00:00:00 2001
From: Wei Xu <weixugc@google.com>
Date: Tue, 21 Jan 2020 18:27:35 -0800
Subject: [PATCH] Make UID generation more stable/predictable.
This patch modifies users-groups with the goal of making it behave better in
configuration where /etc/passwd is stateless. Specifically it:
- adds 'uid' options so that UIDs can be set from cloud-config, that is
now recommended and a warning is generated if some users do not have that
option;
- reserves range [2000, 5000) for UIDs created by cloud-init;
- makes sure that users are created in the alphabetical order to provide
more predictable and consistent UIDs;
- adds a requirement that if homedir already exists the UID assigned to user
must be the same as the owner of that directory, this is to protect from
assigning incorrect UIDs after config changes.
Original Author: Andrey Ulanov <andreyu@google.com>
Original Date: Thu Feb 25 18:41:48 2016 -0800
---
cloudinit/config/cc_users_groups.py | 32 +++++++++++++++++++++++++++++
cloudinit/distros/__init__.py | 6 ++++--
2 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py
index c32a743a..3bc6d563 100644
--- a/cloudinit/config/cc_users_groups.py
+++ b/cloudinit/config/cc_users_groups.py
@@ -128,6 +128,9 @@ from cloudinit import log as logging
from cloudinit.settings import PER_INSTANCE
+import os
+import pwd
+
LOG = logging.getLogger(__name__)
frequency = PER_INSTANCE
@@ -139,6 +142,8 @@ def handle(name, cfg, cloud, _log, _args):
cloud_keys = cloud.get_public_ssh_keys() or []
for (name, members) in groups.items():
cloud.distro.create_group(name, members)
+
+ # First create all users that have UID explicitly specified in cloud-config.
for (user, config) in users.items():
ssh_redirect_user = config.pop("ssh_redirect_user", False)
if ssh_redirect_user:
@@ -162,6 +167,33 @@ def handle(name, cfg, cloud, _log, _args):
else:
config['ssh_redirect_user'] = default_user
config['cloud_public_ssh_keys'] = cloud_keys
+ if 'uid' in config:
+ config['uid'] = str(config['uid'])
+ cloud.distro.create_user(user, **config)
+
+ # dict is an unordered container. To make UIDs assigned to users more
+ # predictable we sort them by the name and create accounts in that order.
+ nouids = []
+ for user in sorted(users):
+ config = users[user]
+ if 'uid' in config:
+ continue
+ nouids.append(user)
+
cloud.distro.create_user(user, **config)
+ pw = pwd.getpwnam(user)
+
+ homedir = config['homedir'] if 'homedir' in config else ('/home/%s' % user)
+ if os.path.isdir(homedir):
+ stats = os.stat(homedir)
+ if stats.st_uid != pw.pw_uid:
+ LOG.error('UID %d was assigned to user %s, but homedir %s '
+ 'is owned by %d', pw.pw_uid, user, homedir, stats.st_uid)
+ raise
+
+ if nouids:
+ LOG.warn('No explicit UID specified for the following users: %s. '
+ 'UID may change after reboot.', ', '.join(nouids))
+
# vi: ts=4 expandtab
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index 2b559fe6..02465787 100755
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -396,8 +396,9 @@ class Distro(object):
else:
create_groups = True
- useradd_cmd = ['useradd', name]
- log_useradd_cmd = ['useradd', name]
+ useradd_cmd = ['useradd', name,
+ '-K', 'UID_MIN=2000', '-K', 'UID_MAX=4999', ]
+ log_useradd_cmd = useradd_cmd[:]
if util.system_is_snappy():
useradd_cmd.append('--extrausers')
log_useradd_cmd.append('--extrausers')
@@ -416,6 +417,7 @@ class Distro(object):
"expiredate": '--expiredate',
"inactive": '--inactive',
"selinux_user": '--selinux-user',
+ "uid": '--uid',
}
useradd_flags = {
--
2.25.1.481.gfbce0eb801-goog