Default mysql shell /bin/sh is not compliant with CIS audit rule 5.2.4.7

Bug #2060644 reported by Gaetan Gouzi
14
This bug affects 2 people
Affects Status Importance Assigned to Milestone
MySQL Router Charm
In Progress
Medium
Adithya Rajendran

Bug Description

When trying to harden a unit with mysql-router subordinate charm, the audit fails on rule `xccdf_org.ssgproject.content_rule_no_shelllogin_for_systemaccounts`: "Ensure that System Accounts Do Not Run a Shell Upon Login"

Indeed, the mysql user account has a /bin/sh shell associated. It should be /sbin/nologin for security standards.

Attached initial usg report.
- Version: 8.0.36
- Revision: 154

CIS documentation suggests the following script to check which user accounts need to be fixed:
```
#!/usr/bin/env bash
{
l_valid_shells="^($(awk -F\/ '$NF != "nologin" {print}' /etc/shells | sed -rn '/^\//{s,/,\\\\/,g;p}' | paste -s -d '|' - ))$"
awk -v pat="$l_valid_shells" -F: '($1!~/^(root|halt|sync|shutdown|nfsnobody)$/ && ($3<'"$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)"' || $3 == 65534) && $(NF) ~ pat) {print "Service account: \"" $1 "\" has a valid shell: " $7}' /etc/passwd
}
```

Output is:
```
Service account: "mysql" has a valid shell: /bin/sh
```

Do we have a good reason to keep /bin/sh for mysql account that will maybe justify the rationale that breaks this rule ?

Revision history for this message
Gaetan Gouzi (ggouzi) wrote :
Gaetan Gouzi (ggouzi)
summary: - Default mysql login /bin/sh not compliant with hardening rule 5.2.4.7
+ Default mysql shell /bin/sh is not compliant with hardening rule 5.2.4.7
summary: - Default mysql shell /bin/sh is not compliant with hardening rule 5.2.4.7
+ Default mysql shell /bin/sh is not compliant with CIS audit rule 5.2.4.7
description: updated
Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

Please could you add the version of the charm and the platform that it is being installed on? i.e. track/risk and machine Ubuntu version.

Thanks.

Changed in charm-mysql-router:
status: New → Incomplete
Revision history for this message
Gaetan Gouzi (ggouzi) wrote :

Hello Alex,

The charm is installed on Jammy machine.
Channel is 8.0/stable.

Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

Thanks @ggouzi

So the charm "ought" to be using /usr/sbin/nologin as the shell, according to this code in (https://opendev.org/openstack/charm-mysql-router/src/commit/46c5d68b7256cd4f9416cc5578a77205da0be493/src/lib/charm/openstack/mysql_router.py#L299):

        if not ch_core.host.user_exists(self.mysqlrouter_user):
            ch_core.host.adduser(
                self.mysqlrouter_user, shell="/usr/sbin/nologin",
                system_user=True, primary_group=self.mysqlrouter_group,
                home_dir=self.mysqlrouter_home_dir)

So perhaps something else is adding the /bin/sh for mysql-router? Is the mysql-client installed?

Revision history for this message
Gaetan Gouzi (ggouzi) wrote :

Hi Alex,

Thank you for your answer.
There is no mysql-client installed. The mysql user exists.

You can reproduce with the following:
```
juju add-model bug-2060644
juju deploy mysql --channel 8.0/stable
juju deploy mysql-router --channel 8.0/stable
juju deploy keystone --channel=yoga/stable
juju relate mysql:db-router mysql-router:db-router
juju relate mysql-router:shared-db keystone:shared-db
juju exec -u keystone/0 -- grep mysql /etc/passwd
mysql:x:998:125::/var/lib/mysql:/bin/sh
```

I can grant you access to my lab if you want.

FYI this problem seems to be have already been reported previously: https://bugs.launchpad.net/charm-mysql-router/+bug/1993649

Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :
Download full text (3.3 KiB)

Okay, I did some more digging and I think it might be a bug in charm-helpers.

Essentially, if you do:

$ lxc launch
$ lxc shell jammy
# addgroup --system mysql
# useradd --system -g mysql mysql
# grep mysql /etc/passwd
mysql:x:998:121::/home/mysql:/bin/sh

You get the /bin/sh.

And this is the what the mysql-router code is doing:

        if not ch_core.host.group_exists(self.mysqlrouter_group):
            ch_core.host.add_group(
                self.mysqlrouter_group, system_group=True)
        # Create the user
        if not ch_core.host.user_exists(self.mysqlrouter_user):
            ch_core.host.adduser(
                self.mysqlrouter_user, shell="/usr/sbin/nologin",
                system_user=True, primary_group=self.mysqlrouter_group,
                home_dir=self.mysqlrouter_home_dir)

i.e. it thinks it's asking for a shell of "/usr/sbin/nologin"

However, charmhelpers.host.adduser is doing this:

def adduser(username, password=None, shell='/bin/bash',
            system_user=False, primary_group=None,
            secondary_groups=None, uid=None, home_dir=None):
    """Add a user to the system.

    Will log but otherwise succeed if the user already exists.

    :param str username: Username to create
    :param str password: Password for user; if ``None``, create a system user
    :param str shell: The default shell for the user
    :param bool system_user: Whether to create a login or system user
    :param str primary_group: Primary group for user; defaults to username
    :param list secondary_groups: Optional list of additional groups
    :param int uid: UID for user being created
    :param str home_dir: Home directory for user

    :returns: The password database entry struct, as returned by `pwd.getpwnam`
    """
    try:
        user_info = pwd.getpwnam(username)
        log('user {0} already exists!'.format(username))
        if uid:
            user_info = pwd.getpwuid(int(uid))
            log('user with uid {0} already exists!'.format(uid))
    except KeyError:
        log('creating user {0}'.format(username))
        cmd = ['useradd']
        if uid:
            cmd.extend(['--uid', str(uid)])
        if home_dir:
            cmd.extend(['--home', str(home_dir)])
        if system_user or password is None:
            cmd.append('--system')
        else:
            cmd.extend([
                '--create-home',
                '--shell', shell,
                '--password', password,
            ])
        if not primary_group:
            try:
                grp.getgrnam(username)
                primary_group = username # avoid "group exists" error
            except KeyError:
                pass
        if primary_group:
            cmd.extend(['-g', primary_group])
        if secondary_groups:
            cmd.extend(['-G', ','.join(secondary_groups)])
        cmd.append(username)
        subprocess.check_call(cmd)
        user_info = pwd.getpwnam(username)
    return user_info

i.e. if adduser() is passed system_user=True, then it doesn't set the --shell parameter, and leaves it as the default.

i.e. the expectation of the charm vs charm-helpers is not met. I'm not sure if the charm-helpers code is a bug...

Read more...

Revision history for this message
Alex Kavanagh (ajkavanagh) wrote :

Triaged to medium; the mysql-router doesn't actually need a shell at all, and so a work-around might be to post install wipe the information out of the /etc/passwd directly.

Changed in charm-mysql-router:
importance: Undecided → Medium
status: Incomplete → Triaged
tags: added: good-first-bug
Changed in charm-mysql-router:
assignee: nobody → Adithya Rajendran (adithya-raj)
Changed in charm-mysql-router:
status: Triaged → In Progress
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Duplicates of this bug

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.