Skip to content disloops

MUSH and Restricted Shell

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:

export PATH
alias mush="telnet localhost 4201"
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."

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 password
  • telnet 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

Leave a Reply

Your email address will not be published. Required fields are marked *