3

I have a docker container running on the server side as a user's login shell so that anyone can ssh into the server and get access to some resource inside.

Say, I have a user called test and I want people to be able to SSH into test's account using some publicly available password. Here's what I have in /etc/passwd

test:1000:1000::/:/bin/test-shell

and in /bin/test-shell

#!/bin/bash
docker run -it --rm --network none python:3.10-alpine /bin/sh

Now, whenever someone ssh into my machine using ssh test@example.com, they are immediately dropped into a disposable docker container. So far so good.

The problem I have is, if the user doesn't exit the shell by either calling exit or pressing Ctrl-D but just closes their terminal window instead, the container is left running indefinitely and taking up limited server resources. I'm wondering if it is possible (and if so, how) to make sure the container is properly stopped (and therefore deleted) when a user disconnects.

I have seen Why does SIGHUP not work on busybox sh in an Alpine Docker container? and tried the approach of trapping both SIGHUP and SIGPIPE (running trap exit SIGHUP SIGPIPE inside the container), unfortunately nothing happens. I'm suspecting maybe the signals are received by the host shell instead of inside the shell inside the container, but I'm not sure how I can leverage that (if that's really what happens) considering I have no way to get the dynamically generated container name, and I can't name the container because I want every single ssh attempt to spawn a different container.

Mia
  • 2,466
  • 22
  • 38
  • Have you tried a `.bash_logout` script in the user's home directory to stop the container? But that probably won't work if the docker container is keeping the login shell alive. – sytech Jul 31 '22 at 06:37
  • @sytech unfortunately it doesn't work because the user's login shell is not bash but the shell of the docker container itself. The login shell simply invokes bash for bootstrapping. I know I can completely replace the process by using `exec docker ...` but that doesn't make a difference either. – Mia Aug 01 '22 at 01:08
  • did you try to create a test user with home and use .bashrc to register a script to stop container using `trap /bin/stop-container EXIT` and run container running `test-shell`? At the end put exit to exit from bash when container exit. – Lety Aug 07 '22 at 18:28
  • @Lety all containers are anonymous and I can't think of a way to find out the container ID of the one that just disconnected programmatically – Mia Aug 13 '22 at 20:55
  • you should run container with a name like mycontainer-randomNum or with --cidfile /home/user/docker_randomNum.cid and register trap script with parameter that is the name of container or the file name used to store container id – Lety Aug 14 '22 at 08:41

3 Answers3

1

I think this answer may help you: https://unix.stackexchange.com/a/85429
For example you can try something like this:

#!/bin/bash
[[ "$PAM_USER" -ne "test" ]] && exit 0

SESSION_COUNT="$(w -h "$PAM_USER" | wc -l)"

if (( SESSION_COUNT == 0 )) && [[ "$PAM_TYPE" == "close_session" ]]; then
  docker kill <containerId>
fi

You should use some unique tag for containerId maybe you can associate id of the container with the session id

alexHX12
  • 403
  • 2
  • 9
  • That's a cool idea I haven't thought of. The problem is, I still don't have a way to associate container ID (generated dynamically at runtime) with the session ID – Mia Aug 13 '22 at 20:58
0

Maybe setting up a connection timout with ClientAliveInterval and ClientAliveCountMax from /etc/ssh/sshd_config will help. By default it is not active.

https://linux.die.net/man/5/sshd_config

Slava Kuravsky
  • 2,702
  • 10
  • 16
  • I have tried this and it doesn't seem to work. It seems like I can just kill the corresponding ssh process and the container is still running. – Mia Aug 06 '22 at 06:21
0

You could add this in your /bin/test-shell script:

#!/bin/bash
# Generate a random name for container
CONTAINER_NAME=${RANDOM}

# Register script to stop container with name
trap '/bin/stop-container "$CONTAINER_NAME"' EXIT

# Run container with name
docker run --name $CONTAINER_NAME -it --rm --network none python:3.10-alpine /bin/sh

# Unregister trap if normal exit
trap - EXIT

At the moment, I can't test it, but it could works, because as described in trap manual:

The environment in which the shell executes a trap on EXIT shall be identical to the environment immediately after the last command executed before the trap on EXIT was taken.

Hope this help you to find the right direction.

Lety
  • 2,511
  • 21
  • 25