I want to share data with different friends, but I don’t really want to create new users on my system. Because in most Linux distributions, the default umask is 022, which means that other users can access my home directory and read files at will, which means no privacy and no security. If my friend’s account/password is accidentally leaked, it will also endanger all the data in my system, I can’t imagine.
So I’m going to start a sshd service inside my Docker container. This way, I can use Linux namespace to isolate the file system, network and other resources, and cgroups to limit the quota of users. Although Linux namespace and cgroups are not absolutely secure and isolated, it is safer and less expensive than creating users directly.
Container Mirroring
There are already GitHub users who have made very simple and easy to use sshd images that can be used with a simple configuration. Of course, there are some concepts about ssh and sshd that should be clear (more on that later).
- Container image repository: panubo/docker-sshd
- DockerHub: panubo/sshd
This image was made very simple: the sshd service was initialized and run directly based on the Alpine Linux installation.
Here is its Dockerfile.
|
|
Configuration-related focus is all in this entry.sh
, which contains various initialization scripts, such as initializing key pairs, creating users, etc.
Configuration files
sshd
will load the /etc/ssh/sshd_config
configuration file (also visible from Dockerfile) when it starts. If you have your own configuration file, you can mount it directly. If you don’t, it’s a simpler solution to use the environment variables provided by the image author, whose script will automatically create this configuration file for us with some of these environment variables.
Environment variables
The following is a non-exhaustive list of common environment variables, a full list and updates can be found earlier on the GitHub project home page.
name | description | example |
---|---|---|
SSH_USERS |
Initialize the user list. The format is: username:user ID:group ID . entry.sh creates these users during initialization. If you don’t need to create users other than root, you can leave them unset. |
SSH_USERS=www:48:48,admin:1000:1000 |
SSH_ENABLE_ROOT |
Whether to enable the root account. Not enabled by default. | SSH_ENABLE_ROOT=false |
SSH_ENABLE_PASSWORD_AUTH |
Whether to enable password login. If not, you can only use the public key to log in. | SSH_ENABLE_PASSWORD_AUTH=true |
Note: Environment variables are set in different ways in different environments. For example, in bash, in docker run parameters, in docker-compose files, etc. Readers should distinguish between them.
Server Key Pairs (Host Keys)
When an ssh client communicates with an ssh server (i.e. sshd), it needs to confirm each other’s identity. Both communicating parties need to provide credentials (public keys) and keep them in each party’s local configuration file after the first communication is manually confirmed, and subsequently do not need to confirm them again as long as the credentials remain the same. Otherwise, it will warn of credential changes, possible man-in-the-middle attacks, and require manual reconfirmation.
The author’s entry.sh script will automatically create these key pairs for us (if they don’t exist), but, as mentioned above, these key pairs should not change often or they will re-confirm the communication. So we should mount the directory where the key pair is located inside the container to prevent it from being recreated every time.
The path of this directory inside the container is: /etc/ssh/keys/
.
The directory after a successful run generally has generated various types of key pairs.
|
|
The private keys without extensions and the public keys with .pub
extensions are 4 pairs in total.
These files are also set with the correct permissions to prevent unauthorized access.
Creating users
Set the above environment variable SSH_USERS
directly. Multiple users are separated by commas.
You can also add users inside the container using the useradd command, which is not discussed in this article.
If you don’t need to create users other than root, you don’t need this environment variable.
Public key login
After the ssh client is authenticated with the ssh server public key, the server writes this credential to .ssh/authorized_keys
in the user’s home directory. You can mount your public key to this file (if you are the only one using the account) or append to it (multiple users using the same account). This will allow password-free login.
Special note: this public key file inside the container must be consistent with the uid/gid of the corresponding user inside the container, otherwise it is invalid (because it is not secure).
Password Login
Note: Logging in with a public key is a more secure and simpler way than logging in with a password and should be used in preference.
To enable password login, the environment variable SSH_ENABLE_PASSWORD_AUTH
should be set to true
. This step is executed in the entry.sh script before the sshd service is started. The new user will be added to the /etc/passwd
file and the user password will be encrypted and saved in the /etc/shadow
file.
Set password
Passwords need to be encrypted and then written to the /etc/shadow
file. There are two ways to encrypt passwords, either with the mkpasswd command or with the web-side tool provided by the mirror author.
- mkpasswd runs the following command.
|
|
After running, you will be prompted for a password, enter and the encrypted password will be displayed.
- The web tool address is here: https://trnubo.github.io/passwd.html The author has declared that this is only a browser-side script and no data will be sent to any server. After entering the password and confirming it, you will get the encrypted password.
Once the password for the username is generated, it should be saved as an executable script file (e.g. set-passwd.sh
) in the following format.
Finally, mount this script in the /etc/entrypoint.d/
directory inside the container, and all custom scripts in this directory will be executed when entry.sh is executed before sshd starts.
Example docker-compose.yml file
|
|
Then docker-compose up
will start it.
I won’t write the commands to run directly with docker for now, they can be easily converted from the docker-compose file. If you need help, you can comment after the article.
You can download it directly: sshd.tgz.
Login example
Conclusion
The above process may seem like a lot of work, but the security and convenience it provides is much more rewarding than this small amount of work.
It’s also fun to go through the whole process and learn how our everyday ssh works!