[OE-core] [PATCH v2 2/2] systemd: add users/groups at build rather than runtime

Stephano Cetola stephano.cetola at linux.intel.com
Tue May 10 00:55:23 UTC 2016


On 05/09, Khem Raj wrote:
> 
> > On May 9, 2016, at 4:42 PM, Stephano Cetola <stephano.cetola at linux.intel.com> wrote:
> > 
> > This fix ensures that systemd users are created at build and that any
> > staticids for the users or groups are preserved. This functionality only
> > existed for readonly filesystems, however it seems wise to be sure these
> > users are added rather than relying on systemd to create them.
> 
> We need to cover the case where online package management is used. If you were to fix
> it in systemd recipe itself which takes care of creating these users in post installs
> that will fix it for this case too.
I'm not sure I follow you. I can see how this could be done in a post
install script, but I'm missing how this would help online package
management. Could you please point me in the right direction?
> 
> > 
> > [ YOCTO #9497 ]
> > 
> > Signed-off-by: Stephano Cetola <stephano.cetola at linux.intel.com>
> > ---
> > meta/classes/rootfs-postcommands.bbclass | 66 +++++++++++++++++++++-----------
> > meta/classes/useradd-staticids.bbclass   | 56 +--------------------------
> > useradd-staticids-list.bbclass           | 56 +++++++++++++++++++++++++++
> > 3 files changed, 100 insertions(+), 78 deletions(-)
> > create mode 100644 useradd-staticids-list.bbclass
> > 
> > diff --git a/meta/classes/rootfs-postcommands.bbclass b/meta/classes/rootfs-postcommands.bbclass
> > index 95d28af..db4cc75 100644
> > --- a/meta/classes/rootfs-postcommands.bbclass
> > +++ b/meta/classes/rootfs-postcommands.bbclass
> > @@ -1,4 +1,4 @@
> > -
> > +inherit useradd-staticids-list
> > # Zap the root password if debug-tweaks feature is not enabled
> > ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains_any("IMAGE_FEATURES", [ 'debug-tweaks', 'empty-root-password' ], "", "zap_empty_root_password ; ",d)}'
> > 
> > @@ -23,6 +23,9 @@ POSTINST_LOGFILE ?= "${localstatedir}/log/postinstall.log"
> > SYSTEMD_DEFAULT_TARGET ?= '${@bb.utils.contains("IMAGE_FEATURES", "x11-base", "graphical.target", "multi-user.target", d)}'
> > ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("DISTRO_FEATURES", "systemd", "set_systemd_default_target; ", "", d)}'
> > 
> > +# Create systemd users at build rather than at runtime
> > +ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("DISTRO_FEATURES", "systemd", "create_systemd_sysusers; ", "", d)}'
> > +
> > ROOTFS_POSTPROCESS_COMMAND += 'empty_var_volatile;'
> > 
> > # Disable DNS lookups, the SSH_DISABLE_DNS_LOOKUP can be overridden to allow
> > @@ -30,7 +33,45 @@ ROOTFS_POSTPROCESS_COMMAND += 'empty_var_volatile;'
> > SSH_DISABLE_DNS_LOOKUP ?= " ssh_disable_dns_lookup ; "
> > ROOTFS_POSTPROCESS_COMMAND_append_qemuall = "${SSH_DISABLE_DNS_LOOKUP}"
> > 
> > -
> > +# Add systemd users checking for any staticids
> > +python create_systemd_sysusers () {
> > +    import shlex
> > +    import subprocess
> > +    s_users = merge_files(get_passwd_list(d), 7)
> > +    s_groups = merge_files(get_group_list(d), 4)
> > +    confs = ['%s/usr/lib/sysusers.d/systemd.conf' % d.getVar('IMAGE_ROOTFS', d), '%s/usr/lib/sysusers.d/systemd-remote.conf' % d.getVar('IMAGE_ROOTFS', d)]
> > +    for conf in confs:
> > +        if os.path.isfile(conf):
> > +            with open(conf, 'r') as f:
> > +                for line in f:
> > +                    if line.startswith('#'):
> > +                        continue
> > +                    fields = shlex.split(line.rstrip())
> > +                    if not fields:
> > +                        continue
> > +                    if fields[0] == 'u':
> > +                        useradd_params = ''
> > +                        if fields[1] in s_users:
> > +                            useradd_params += '--uid %s' % s_users[fields[1]][2]
> > +                            bb.warn("Changing user %s's uid to (%s), verify configuration files!" % (fields[1], s_users[fields[1]][2]))
> > +                        elif fields[2] != '-':
> > +                            useradd_params += '--uid %s' % fields[2]
> > +                        if fields[3] != '-':
> > +                            useradd_params += ' --comment "%s"' % fields[3]
> > +                        useradd_params += ' --root "%s" --shell /sbin/nologin  --system %s' % (d.getVar("IMAGE_ROOTFS", d), fields[1])
> > +                        code = subprocess.call('useradd %s' % useradd_params, shell=True)
> > +                    elif fields[0] == 'g':
> > +                        groupadd_params = ""
> > +                        if fields[1] in s_groups:
> > +                            groupadd_params += '--gid %s' % s_groups[fields[1]][2]
> > +                            bb.warn("Changing group %s's gid to (%s), verify configuration files!" % (fields[1], s_groups[fields[1]][2]))
> > +                        elif fields[2] != '-':
> > +                            groupadd_params += '--gid %s' % fields[2]
> > +                        if fields[3] != '-':
> > +                            groupadd_params += ' --comment "%s"' % (d.getVar("IMAGE_ROOTFS", d), fields[3])
> > +                        groupadd_params += ' --root "%s" --system %s' % (d.getVar("IMAGE_ROOTFS", d), fields[1])
> > +                        code = subprocess.call('groupadd %s' % groupadd_params, shell=True)
> > +}
> > 
> > #
> > # A hook function to support read-only-rootfs IMAGE_FEATURES
> > @@ -73,27 +114,6 @@ read_only_rootfs_hook () {
> > 			${IMAGE_ROOTFS}/etc/init.d/populate-volatile.sh
> > 		fi
> > 	fi
> > -
> > -	if ${@bb.utils.contains("DISTRO_FEATURES", "systemd", "true", "false", d)}; then
> > -	    # Update user database files so that services don't fail for a read-only systemd system
> > -	    for conffile in ${IMAGE_ROOTFS}/usr/lib/sysusers.d/systemd.conf ${IMAGE_ROOTFS}/usr/lib/sysusers.d/systemd-remote.conf; do
> > -		[ -e $conffile ] || continue
> > -		grep -v "^#" $conffile | sed -e '/^$/d' | while read type name id comment; do
> > -		    if [ "$type" = "u" ]; then
> > -			useradd_params=""
> > -			[ "$id" != "-" ] && useradd_params="$useradd_params --uid $id"
> > -			[ "$comment" != "-" ] && useradd_params="$useradd_params --comment $comment"
> > -			useradd_params="$useradd_params --system $name"
> > -			eval useradd --root ${IMAGE_ROOTFS} $useradd_params || true
> > -		    elif [ "$type" = "g" ]; then
> > -			groupadd_params=""
> > -			[ "$id" != "-" ] && groupadd_params="$groupadd_params --gid $id"
> > -			groupadd_params="$groupadd_params --system $name"
> > -			eval groupadd --root ${IMAGE_ROOTFS} $groupadd_params || true
> > -		    fi
> > -		done
> > -	    done
> > -	fi
> > }
> > 
> > #
> > diff --git a/meta/classes/useradd-staticids.bbclass b/meta/classes/useradd-staticids.bbclass
> > index a9b506d..e9dfe69 100644
> > --- a/meta/classes/useradd-staticids.bbclass
> > +++ b/meta/classes/useradd-staticids.bbclass
> > @@ -1,3 +1,4 @@
> > +inherit useradd-staticids-list
> > # In order to support a deterministic set of 'dynamic' users/groups,
> > # we need a function to reformat the params based on a static file
> > def update_useradd_static_config(d):
> > @@ -17,35 +18,6 @@ def update_useradd_static_config(d):
> >         def error(self, message):
> >             raise bb.build.FuncFailed(message)
> > 
> > -    def list_extend(iterable, length, obj = None):
> > -        """Ensure that iterable is the specified length by extending with obj
> > -        and return it as a list"""
> > -        return list(itertools.islice(itertools.chain(iterable, itertools.repeat(obj)), length))
> > -
> > -    def merge_files(file_list, exp_fields):
> > -        """Read each passwd/group file in file_list, split each line and create
> > -        a dictionary with the user/group names as keys and the split lines as
> > -        values. If the user/group name already exists in the dictionary, then
> > -        update any fields in the list with the values from the new list (if they
> > -        are set)."""
> > -        id_table = dict()
> > -        for conf in file_list.split():
> > -            if os.path.exists(conf):
> > -                f = open(conf, "r")
> > -                for line in f:
> > -                    if line.startswith('#'):
> > -                        continue
> > -                    # Make sure there always are at least exp_fields elements in
> > -                    # the field list. This allows for leaving out trailing
> > -                    # colons in the files.
> > -                    fields = list_extend(line.rstrip().split(":"), exp_fields)
> > -                    if fields[0] not in id_table:
> > -                        id_table[fields[0]] = fields
> > -                    else:
> > -                        id_table[fields[0]] = list(itertools.imap(lambda x, y: x or y, fields, id_table[fields[0]]))
> > -
> > -        return id_table
> > -
> >     # We parse and rewrite the useradd components
> >     def rewrite_useradd(params):
> >         # The following comes from --help on useradd from shadow
> > @@ -73,19 +45,6 @@ def update_useradd_static_config(d):
> >         parser.add_argument("-U", "--user-group", help="create a group with the same name as the user", action="store_const", const=True)
> >         parser.add_argument("LOGIN", help="Login name of the new user")
> > 
> > -        # Return a list of configuration files based on either the default
> > -        # files/passwd or the contents of USERADD_UID_TABLES
> > -        # paths are resolved via BBPATH
> > -        def get_passwd_list(d):
> > -            str = ""
> > -            bbpath = d.getVar('BBPATH', True)
> > -            passwd_tables = d.getVar('USERADD_UID_TABLES', True)
> > -            if not passwd_tables:
> > -                passwd_tables = 'files/passwd'
> > -            for conf_file in passwd_tables.split():
> > -                str += " %s" % bb.utils.which(bbpath, conf_file)
> > -            return str
> > -
> >         newparams = []
> >         users = None
> >         for param in re.split('''[ \t]*;[ \t]*(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', params):
> > @@ -206,19 +165,6 @@ def update_useradd_static_config(d):
> >         parser.add_argument("-r", "--system", help="create a system account", action="store_true")
> >         parser.add_argument("GROUP", help="Group name of the new group")
> > 
> > -        # Return a list of configuration files based on either the default
> > -        # files/group or the contents of USERADD_GID_TABLES
> > -        # paths are resolved via BBPATH
> > -        def get_group_list(d):
> > -            str = ""
> > -            bbpath = d.getVar('BBPATH', True)
> > -            group_tables = d.getVar('USERADD_GID_TABLES', True)
> > -            if not group_tables:
> > -                group_tables = 'files/group'
> > -            for conf_file in group_tables.split():
> > -                str += " %s" % bb.utils.which(bbpath, conf_file)
> > -            return str
> > -
> >         newparams = []
> >         groups = None
> >         for param in re.split('''[ \t]*;[ \t]*(?=(?:[^'"]|'[^']*'|"[^"]*")*$)''', params):
> > diff --git a/useradd-staticids-list.bbclass b/useradd-staticids-list.bbclass
> > new file mode 100644
> > index 0000000..377f748
> > --- /dev/null
> > +++ b/useradd-staticids-list.bbclass
> > @@ -0,0 +1,56 @@
> > +# Return a list of configuration files based on either the default
> > +# files/group or the contents of USERADD_GID_TABLES
> > +# paths are resolved via BBPATH
> > +def get_group_list(d):
> > +    str = ""
> > +    bbpath = d.getVar('BBPATH', True)
> > +    group_tables = d.getVar('USERADD_GID_TABLES', True)
> > +    if not group_tables:
> > +        group_tables = 'files/group'
> > +    for conf_file in group_tables.split():
> > +        str += " %s" % bb.utils.which(bbpath, conf_file)
> > +    return str
> > +
> > +def list_extend(iterable, length, obj = None):
> > +    import itertools
> > +    """Ensure that iterable is the specified length by extending with obj
> > +    and return it as a list"""
> > +    return list(itertools.islice(itertools.chain(iterable, itertools.repeat(obj)), length))
> > +
> > +def merge_files(file_list, exp_fields):
> > +    import itertools
> > +    """Read each passwd/group file in file_list, split each line and create
> > +    a dictionary with the user/group names as keys and the split lines as
> > +    values. If the user/group name already exists in the dictionary, then
> > +    update any fields in the list with the values from the new list (if they
> > +    are set)."""
> > +    id_table = dict()
> > +    for conf in file_list.split():
> > +        if os.path.exists(conf):
> > +            f = open(conf, "r")
> > +            for line in f:
> > +                if line.startswith('#'):
> > +                    continue
> > +                # Make sure there always are at least exp_fields elements in
> > +                # the field list. This allows for leaving out trailing
> > +                # colons in the files.
> > +                fields = list_extend(line.rstrip().split(":"), exp_fields)
> > +                if fields[0] not in id_table:
> > +                    id_table[fields[0]] = fields
> > +                else:
> > +                    id_table[fields[0]] = list(itertools.imap(lambda x, y: x or y, fields, id_table[fields[0]]))
> > +
> > +    return id_table
> > +
> > +# Return a list of configuration files based on either the default
> > +# files/passwd or the contents of USERADD_UID_TABLES
> > +# paths are resolved via BBPATH
> > +def get_passwd_list(d):
> > +    str = ""
> > +    bbpath = d.getVar('BBPATH', True)
> > +    passwd_tables = d.getVar('USERADD_UID_TABLES', True)
> > +    if not passwd_tables:
> > +        passwd_tables = 'files/passwd'
> > +    for conf_file in passwd_tables.split():
> > +        str += " %s" % bb.utils.which(bbpath, conf_file)
> > +    return str
> > --
> > 2.8.0
> > 
> > --
> > _______________________________________________
> > Openembedded-core mailing list
> > Openembedded-core at lists.openembedded.org
> > http://lists.openembedded.org/mailman/listinfo/openembedded-core
> 





More information about the Openembedded-core mailing list