5

Is there a way to run ssh as a user that doesn't exist on the host initiating the ssh connection?

Let's say I have a remote host with an account setup and sshd running. I can ssh into that machine from using ssh remote-user@remote-host. If I have setup keys, ssh will use it and if no keys work, it will prompt me for the password. Let assume that password authentication is enabled.

Let's say I have a Docker image called docker-image with ssh and other required software installed. I can start the container and ssh without a problem. Note that the root user exists.

hostname$ docker run -it --rm docker-image bash
container-id$ whoami
root
container-id$ ssh remote-user@remote-host
remote-user@remote-host's password: 

If I start a container using a user that doesn't exist in the image, I can't connect to remote-host using ssh.

hostname$ docker run -it --rm -u 1234:100 docker-image bash
container-id$ whoami
whoami: cannot find name for user ID 1234
container-id$ ssh remote-user@remote-host
No user exists for uid 1234
container-id$ echo $?
255

The error is coming from ssh.c:

    /* Get user data. */
    pw = getpwuid(getuid());
    if (!pw) {
        logit("No user exists for uid %lu", (u_long)getuid());
        exit(255);
    }

The error message makes sense because ssh uses sshconfig, keys, knownhosts etc. files from user's home directory but I think one would expect it to work when the user specifies different locations for those files like shown below.

ssh -vvv -i /dev/null  -F /dev/null   \
         -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null      \
         -o PubkeyAuthentication=no  -o PreferredAuthentications=password \
         -o PasswordAuthentication=yes
    remote-user@remote-host

In the command above, none of the files that ssh needs (as far as I can tell) are in the user's home directory. Is there a way to run ssh in this case without having to mount host's /etc/passwd or mount a fake passwd file or mess with /etc/passwd file or creating a local user in the image? Am I overlooking any options?

How does it matter who the owner of the ssh process on the client is? Is there a good reason for forcing the local user check or is it just an implementation option that ssh developers took?

2
  • 2
    its not really clear to me why the container started at all, using a userid that is not an authorized user, but yes, all processes need to be associated with a user. that is how linux determines what actions the process is allowed to perform. Commented Feb 11, 2022 at 19:45
  • 1
    This question seems better suited for asking directly to the openssh developers. Perhaps via the public maillist. SSH probably doesn't technically need a specific id, but before containers, it is a pretty unusual situation for anyone to try doing that.
    – Zoredache
    Commented Feb 11, 2022 at 23:12

1 Answer 1

2

So I just had the same problem: I needed to run ssh from inside a docker container without an entry in /etc/passwd.

As I have no access to the host nor root access inside the container itself I just went ahead and replaced the offending code...


Warning: Security isn't a concern in my use case so I'm totally ok with messing things up as long as it works. I have no idea if this will cause security issues down the road so please proceed at your own risk. This is a quick and dirty hack to get the job done at all costs:


Inside ssh.c I replaced the call to getpwuid(getuid()) with:

/* Get user data without using getpwuid
 * to run ssh inside a docker container 
 * without a valid passwd entry for the current user
 */
struct passwd fake_user_data = {
    .pw_name = "ssh",
    .pw_passwd = "",
    .pw_uid = getuid(),
    .pw_gid = getgid(),
    .pw_gecos = "",
    .pw_dir = getenv("HOME"),
    .pw_shell = getenv("SHELL")
};

/* pw = getpwuid(getuid()); */
pw = &fake_user_data;

And in misc.c I modified tilde_expand_filename:

path = strchr(filename, '/');
struct passwd fake_user_data = {
    .pw_dir = getenv("HOME")
};
if (path != NULL && path > filename) {          /* ~user/path */
    ...
} else if ((pw = getpwuid(uid)) == NULL) {
    /* bypass missing passwd entry...
       fatal("tilde_expand_filename: No such uid %ld", (long)uid); */

    pw = &fake_user_data;
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .