I have slowly been working on a MUSH, which is an online, multi-player, text-based social game. The game Zork (1977) is probably the most popular game in this style, although it is not multi-player. I decided to use PennMUSH as the server distro since it seems to have the widest support.
MUSH servers open a raw TCP socket for incoming connections and most sessions occur over plaintext. There are a handful of MUSH clients that players can use but a simple TELNET connection also works. MUSH servers understand enough of the TELNET protocol to refuse option negotiation.
Rather than expose the MUSH server to the open internet, I decided to create restricted shell accounts for users and have them connect to the MUSH via localhost
instead. This drastically reduces the accessibility of the MUSH and would likely deter prospective players but it works for our current user base.
Despite the huge number of active MUDs on the internet, forcing users through SSH still seems safer than exposing the MUSH service. I'm using AWS security groups on the MUSH host to prevent external access to that port.
Note: After some testing, I have walked this back and decided to just open it to the internet after the initial release. Shell accounts will still be an option. See this page for some details.
Setting Up Restricted Shell
The premise for setting up restricted accounts is as follows:
- Use rbash as a restricted shell
- Limit
$PATH
to a custom/bin
directory - Add symbolic links in custom
/bin
directory to a limited set of commands
If bash
is started with the name rbash
, the shell becomes restricted, meaning that a number of commands are prohibited. We'll enable that now with a symlink:
cd /bin sudo ln -s bash rbash
We'll also add a group
for the restricted users:
sudo groupadd mushusers
Creating Users
Now let's go through the process of creating a restricted user account:
sudo useradd -m -d /home/mushusers/basic_user -s /bin/rbash -N -g mushusers -c "Testing the restricted access shell" basic_user
The options above are as follows:
-m
→ creates a home directory-d
→ specifies a non-default location for the home directory-s
→ specifies the login shell to use-N
→ do not create a group with the same name as the user but add the user to the group specified by the-g
option-c
→ comment field
Linux systems use a few different files to configure a user's session in different scenarios:
.bashrc
.bash_profile
.profile
This is where the PATH
environment variable is located, which defines where to find executables. So we want to wipe any existing config files and create our own with a custom PATH
:
sudo rm -rf /home/mushusers/basic_user/.bash* sudo rm -rf /home/mushusers/basic_user/.profile sudo vi /home/mushusers/basic_user/.bashrc
Here's the full text of the .bashrc
file I created for restricted users:
PATH=$HOME/bin export PATH alias mush="telnet localhost 4201" echo echo " Welcome! This is a restricted shell. Type 'mush' to visit Parlor City." echo " You can also type 'passwd' to change your password. Type 'exit' to leave." echo
This same file can be used as .bash_profile
too:
sudo cp /home/mushusers/basic_user/.bashrc /home/mushusers/basic_user/.bash_profile
You can see that we define and export the PATH
variable above, which is limited to a single /bin
directory that we'll create now:
sudo mkdir /home/mushusers/basic_user/bin sudo ln -s /usr/bin/telnet /home/mushusers/basic_user/bin/telnet sudo ln -s /bin/passwd /home/mushusers/basic_user/bin/passwd
Within this directory we've created symlinks for only two commands:
passwd
to allow users to change their passwordtelnet
to allow them to connect to the MUSH
Note: "But they'll TELNET to other hosts!" you say. Keep reading.
Now we'll correct the owner and permissions and use chattr
to make the files immutable with the +i
attribute:
sudo chmod -R 750 /home/mushusers/basic_user sudo chown -R basic_user:mushusers /home/mushusers/basic_user sudo chattr +i /home/mushusers/basic_user/.bashrc sudo chattr +i /home/mushusers/basic_user/.bash_profile
That completes the configuration for the restricted shell user. The only thing left is to create a password for the account:
sudo passwd basic_user
Last Configuration Items
I also had to modify /etc/ssh/sshd_config
and to enable password authentication. Set PasswordAuthentication
to yes then restart the SSH service.
Earlier we gave restricted shell users access to the telnet
command to allow them to connect to the MUSH server locally. We want to prevent these users from making TELNET connections to remote servers, though.
The solution was to use iptables
to prevent users in the "mushusers" group from establishing outbound connections. The resulting command looked like this:
iptables -A OUTPUT -o eth0 -m owner --gid-owner 1001 -j REJECT
...where "1001" is the ID for the "mushusers" group we added the users to.
Because iptables
rules don't persist across reboots, I added this command to /etc/rc.d/rc.local
so that it will always run on startup. I also had to make this file executable:
sudo chmod +x /etc/rc.d/rc.local
That's it! The commands in the "Creating Users" section above can now be repeated to add more users. I will keep this article updated if I come across any further best practices for configuring restricted shell accounts.
Note: Since creating this tutorial I have provisioned TinyFugue as a MUSH client for users instead of telnet
. However, the principles are the same so I will leave the current commands as they are. One last note regarding TinyFugue for my own sake, it was necessary to install libncurses
to get TinyFugue working correctly:
sudo apt-get install libncurses5-dev libncursesw5-dev