xenago / libnss_shim

Perform Name Service Switch (NSS) lookups with custom commands
GNU General Public License v3.0
6 stars 0 forks source link

Changing user doesn't work with nss lookup #3

Closed nguyen102 closed 5 months ago

nguyen102 commented 5 months ago

Hi,

I installed libnss_shim on a linux server and I set up the config to call a Python script. The python script looks like this

print("fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash")

When I run getent passwd I see fakeuser-2 is printed as shown below

...
testuser-1:x:1002:1002::/home/testuser-1:/bin/bash
testuser-2:x:1003:1003::/home/testuser-2:/bin/bash
fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash

However, when I run su fakeuser-2, I get the error su: user fakeuser-2 does not exist. This does not happen if I edit /etc/passwd manually. I would like to add fakeuser-2 dynamically and not have to edit /etc/passwd directly. Do you know how I can do that?

-Tim

nguyen102 commented 5 months ago

When I run id fakuser-1 I also get id: fakeuser-1: no such user

xenago commented 5 months ago

Based on this info, it seems like you have set up a command in etc/libnss_shim/config.json for the passwd function get_all_entries, but not for get_entry_by_name.

When su runs, it will make an NSS request for the name fakeuser-2, which wouldn't return anything for that user if get_entry_by_name is not configured. This is like running the command getent passwd fakeuser-2:

image

To handle this, you will need to create a command that takes in the username and checks if that username exists.

Example config:

{
  "databases": {
    "passwd": {
      "functions": {
        "get_all_entries": {
          "command": "python3 /etc/libnss_shim/my-passwd-script.py --all-users"
        },
        "get_entry_by_uid": {
          "command": ""
        },
        "get_entry_by_name": {
          "command": "python3 /etc/libnss_shim/my-passwd-script.py --name",
          "env": {
            "PASS_NAME_ENV_VAR": "<$name>"
          }
        }
      }
    },
}

Example python script:

import os
import sys

cli_argument = sys.argv[1]

passwd_users = {
    "testuser-1": "testuser-1:x:1002:1002::/home/testuser-1:/bin/bash",
    "testuser-2": "testuser-2:x:1003:1003::/home/testuser-2:/bin/bash",
    "fakeuser-2": "fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash"
}

if cli_argument == "--all-users":
    for passwd_entry in passwd_users.values():
        print(passwd_entry)
elif cli_argument == "--name":
    provided_name = os.getenv("PASS_NAME_ENV_VAR", default = None)
    if provided_name is not None and provided_name in passwd_users:
        print(passwd_users[provided_name])

Result:

image

nguyen102 commented 5 months ago

I updated my config file to the format below

{
  "databases": {
    "passwd": {
      "functions": {
        "get_all_entries": {
          "command": "echo 'fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash'"
        },
        "get_entry_by_uid": {
          "command": "echo 'fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash'"
        },
        "get_entry_by_name": {
          "command": "echo 'fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash'"
        }
      }
    }
  }
}

Now when I run the command su fakeuser-2, I'm getting this error

[root@ip-123-123-123-123 ~]# su fakeuser-2

/usr/bin/id: cannot find name for group ID 1004
/usr/bin/id: cannot find name for user ID 1004

Is there anything else I need to mock out to be able to change users?

xenago commented 5 months ago

I updated my config file to the format below

{
  "databases": {
    "passwd": {
      "functions": {
        "get_all_entries": {
          "command": "echo 'fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash'"
        },
        "get_entry_by_uid": {
          "command": "echo 'fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash'"
        },
        "get_entry_by_name": {
          "command": "echo 'fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash'"
        }
      }
    }
  }
}

I don't recommend using that config, because it is always responding 'yes, this user exists and here is their info' to uid and name queries, even if that uid/name is not valid. That's why in the example python script I posted in my comment above, it checks if the input username is in the expected list before outputting the corresponding information.

xenago commented 5 months ago
/usr/bin/id: cannot find name for group ID 1004

This is an equivalent error for the group NSS database. It should go away if you add a command which outputs to the corresponding group query get_entry_by_gid.

/usr/bin/id: cannot find name for user ID 1004

I believe this is an equivalent error for the passwd NSS database. It should go away if you add commands which output to the corresponding passwd query get_entry_by_uid.

nguyen102 commented 5 months ago

I updated my config file to the format below

{
  "databases": {
    "passwd": {
      "functions": {
        "get_all_entries": {
          "command": "echo 'fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash'"
        },
        "get_entry_by_uid": {
          "command": "echo 'fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash'"
        },
        "get_entry_by_name": {
          "command": "echo 'fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash'"
        }
      }
    }
  }
}

I don't recommend using that config, because it is always responding 'yes, this user exists and here is their info' to uid and name queries, even if that uid/name is not valid. That's why in the example python script I posted in my comment above, it checks if the input username is in the expected list before outputting the corresponding information.

Ah yes, that make sense. I'll update the commands to actually use the uid and return the correct user given the uid.

nguyen102 commented 5 months ago
/usr/bin/id: cannot find name for group ID 1004

This is an equivalent error for the group NSS database. It should go away if you add a command which outputs to the corresponding group query get_entry_by_gid.

/usr/bin/id: cannot find name for user ID 1004

I believe this is an equivalent error for the passwd NSS database. It should go away if you add commands which output to the corresponding passwd query get_entry_by_uid.

In my config file, I add a command to output fakeuser-2 for get_entry_by_uid, but I'm still getting the same error.

It's interesting though because when I run id fakeuser-2 as root, I get

[root@ip-1-1-1-1 ~]# id fakeuser-2
uid=1004(fakeuser-2) gid=1004 groups=1004

However, after I try to change user and run id I get the error.

[root@ip-1-1-1-1 ~]# su fakeuser-2
Python version 3.7.16
/usr/bin/id: cannot find name for group ID 1004
/usr/bin/id: cannot find name for user ID 1004
[I have no name!@ip-1-1-1-1 root]$ id fakeuser-2
id: fakeuser-2: no such user
xenago commented 5 months ago

Have you tried implementing all of the available database functions for group and passwd? shadow may not be required but normal system operation generally relies on them:

group
    get_all_entries()
    get_entry_by_gid(uint32 gid)
    get_entry_by_name(str name)
passwd
    get_all_entries()
    get_entry_by_uid(uint32 uid)
    get_entry_by_name(str name)
shadow
    get_all_entries()
    get_entry_by_name(str name)

From my comment above:

/usr/bin/id: cannot find name for group ID 1004

This is an equivalent error for the group NSS database. It should go away if you add a command which outputs to the corresponding group query get_entry_by_gid.

/usr/bin/id: cannot find name for user ID 1004

I believe this is an equivalent error for the passwd NSS database. It should go away if you add commands which output to the corresponding passwd query get_entry_by_uid.

nguyen102 commented 5 months ago

Based on this info, it seems like you have set up a command in etc/libnss_shim/config.json for the passwd function get_all_entries, but not for get_entry_by_name.

When su runs, it will make an NSS request for the name fakeuser-2, which wouldn't return anything for that user if get_entry_by_name is not configured. This is like running the command getent passwd fakeuser-2:

image

To handle this, you will need to create a command that takes in the username and checks if that username exists.

Example config:

{
  "databases": {
    "passwd": {
      "functions": {
        "get_all_entries": {
          "command": "python3 /etc/libnss_shim/my-passwd-script.py --all-users"
        },
        "get_entry_by_uid": {
          "command": ""
        },
        "get_entry_by_name": {
          "command": "python3 /etc/libnss_shim/my-passwd-script.py --name",
          "env": {
            "PASS_NAME_ENV_VAR": "<$name>"
          }
        }
      }
    },
}

Example python script:

import os
import sys

cli_argument = sys.argv[1]

passwd_users = {
    "testuser-1": "testuser-1:x:1002:1002::/home/testuser-1:/bin/bash",
    "testuser-2": "testuser-2:x:1003:1003::/home/testuser-2:/bin/bash",
    "fakeuser-2": "fakeuser-2:x:1004:1004::/home/fakeuser-2:/bin/bash"
}

if cli_argument == "--all-users":
    for passwd_entry in passwd_users.values():
        print(passwd_entry)
elif cli_argument == "--name":
    provided_name = os.getenv("PASS_NAME_ENV_VAR", default = None)
    if provided_name is not None and provided_name in passwd_users:
        print(passwd_users[provided_name])

Result:

image

After you change to fakuser-2 can you run whoami? Do you get the error whoami: cannot find name for user ID 1004: Permission denied

I launched a new ec2 instance, and was able to replicate your steps.

[root@ip-172-31-55-251 ~]# id fakeuser-2
uid=1004(fakeuser-2) gid=1004(testuser-1) groups=1004(testuser-1)
[root@ip-172-31-55-251 ~]# su fakeuser-2
bash-5.2$ whoami
whoami: cannot find name for user ID 1004: Permission denied
bash-5.2$ id fakeuser-2
id: ‘fakeuser-2’: no such user
nguyen102 commented 5 months ago

Have you tried implementing all of the available database functions for group and passwd? shadow may not be required but normal system operation generally relies on them:

group
    get_all_entries()
    get_entry_by_gid(uint32 gid)
    get_entry_by_name(str name)
passwd
    get_all_entries()
    get_entry_by_uid(uint32 uid)
    get_entry_by_name(str name)
shadow
    get_all_entries()
    get_entry_by_name(str name)

From my comment above:

/usr/bin/id: cannot find name for group ID 1004

This is an equivalent error for the group NSS database. It should go away if you add a command which outputs to the corresponding group query get_entry_by_gid.

/usr/bin/id: cannot find name for user ID 1004

I believe this is an equivalent error for the passwd NSS database. It should go away if you add commands which output to the corresponding passwd query get_entry_by_uid.

Yep, I tried implementing all of the methods. I still got the permission error.

nguyen102 commented 5 months ago

I noticed this statement in the main README

This NSS plugin runs commands defined in the file /etc/libnss_shim/config.json, which is only accessible to root by default.

Is the permission error because when we change to a different user, that new user doesn't have permission to execute the libnss_shim module and get the username?

nguyen102 commented 5 months ago

I noticed this statement in the main README

This NSS plugin runs commands defined in the file /etc/libnss_shim/config.json, which is only accessible to root by default.

Is the permission error because when we change to a different user, that new user doesn't have permission to execute the libnss_shim module and get the username?

Ah I think it is a permission issue. After I updated the config permission and the python script permission, I can now login in as fakeuser-2.

Commands shown below

[root@ip-1-1-1-1 libnss_shim]# chmod 644 my-passwd-script.py
[root@ip-1-1-1-1 libnss_shim]# chmod 644 config.json
[root@ip-10-3-129-44 libnss_shim]# su fakeuser-2
[fakeuser-2@ip-1-1-1-1 libnss_shim]$ whoami
fakeuser-2
xenago commented 5 months ago

Ah, nice find! I will update the README with information about the permission for the script and adjust the default permission for config.json to 644 for the next release (previously set to 640 in Cargo.toml for RPM and deb packaging configs).

xenago commented 5 months ago

The latest release incorporates this change; config.json should be 644 when installed going forward.

https://github.com/xenago/libnss_shim/commit/b06310923175030e5d8049b47f6e1f260103ef50#diff-2e9d962a08321605940b5a657135052fbcef87b5e360662bb527c96d9a615542R46