Introduction
The public internet is like a hurricane and your webserver is like sticking your head outside in the middle.
This was what I was told by someone when I noticed that my fairly new server was getting hit by script kiddies. He was trying to assure me that everything was fine.
Unfortunately, I had made a few mistakes when setting up the server, so I was vulnerable. He, and others, helped me fix the problems, and when I was done, he said,
Once that’s done, memorize all the procedures you did and help anyone who hasn’t seen the light.
I am not any good at memorization, so I am writing this post/tutorial instead.
Prerequisites
This tutorial assumes a basic set of knowledge:
- The user must be comfortable using a command-line interface.
- The user must be comfortable in their operating system of choice.
- The user must know how to install software in their operating system of choice.
- The user must know what
root
is. - The user must be running a system that is Unix-like.
- The user must know the basics of using
ssh
, including creating and setting up keys. - The user must also know how to restart a service on their server.
I run Arch Linux on my server and my desktop, so I will use Arch-specific commands where appropriate. That said, Arch Linux is not the best Linux distro to use as a server distro for a beginning sysadmin!
Also, the first thing that every new sysadmin needs to understand, in my opinion, is ports. Before you attempt to run your own webserver, make sure you understand what ports are and their basic usages.
I also recommend that you read this tutorial in its entirety before starting! You do not want to miss details!
Conventions
In this post, I will use a #
character at the beginning of all commands that
must be run as root
, and a $
character for commands that must be run as the
user that will be set up during the process.
Change root
Password
Assuming you are not trying to run a server out of your house, which is not a
good idea, you are probably renting a server from a cloud provider. And when you
receive access, you are probably going to be given root
access through
ssh
with a password.
So log into your server, probably as root
. Then, change the password for
root
:
# passwd root
When prompted, enter a new password. When I first made my server, I generated a random password that was over 100 characters long.
If I had not done that, my server would have been successfully hacked. As it is, it might have been, but probably not. But it only would have been hacked because I made mistakes.
Setting Up a Firewall
We have to set up a firewall to block access to all of the ports that you won’t use, so let’s do that.
First, pick a random port between 1000 and 65535. Then, install nftables
on
your server. In Arch, you can do that with the following command:
# pacman -S nftables
Then, open the file /etc/nftables.conf
and insert the following contents:
#!/usr/bin/nft -f
# ipv4/ipv6 Simple & Safe Firewall
# you can find examples in /usr/share/nftables/
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# allow established/related connections
ct state {established, related} accept
# early drop of invalid connections
ct state invalid drop
# allow from loopback
iifname lo accept
# allow icmp
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# allow ssh and web
tcp dport { 22, 80, 443, <random_port_number> } accept
# everything else
counter reject with icmpx type port-unreachable
}
chain forward {
type filter hook forward priority 0;
drop
}
chain output {
type filter hook output priority 0;
}
}
where <random_port_number>
is changed to the random number you just picked.
Then enable and start the nftables
service. The details of doing so will
depend on the OS you picked, but for Arch Linux, using systemd
, the commands
are:
# systemctl enable nftables
# systemctl start nftables
Locking Down ssh
The next thing we need to do is to get rid of root
ssh
access.
Add a New User
Next, you need to add a new user:
# useradd -a <username>
Be sure to replace <username>
as appropriate.
Next, set a password for the user:
# passwd <username>
Check that you can login through ssh
as the user that you have just created,
with the password that you have set.
Give the User sudo
Permissions
Next, we are going to give that user the ability to run sudo
.
First, install sudo
. In Arch, that is as simple as:
# pacman -S sudo
But it may be different for your server.
Next, edit the sudoers
file. The command to do so is:
# visudo
On Arch, for some reason, you have to use nano
to edit the file, like so:
# EDITOR=nano visudo
In that file you will find two lines that say:
## Uncomment to allow members of group wheel to execute any command
# %wheel ALL=(ALL) ALL
The above two lines might substitute sudo
for wheel
.
Uncomment the line, like so:
## Uncomment to allow members of group wheel to execute any command
%wheel ALL=(ALL) ALL
Now, before you save the file and leave, take note of whether it says wheel
or
sudo
. That’s important later.
Save the file and close it. If it closes successfully, you can move on.
Now we have to add the user to the group that can run sudo
. If the file you
just edited used wheel
, run the following command:
# usermod -a -G wheel <username>
and if it used sudo
, run the following command:
# usermod -a -G sudo <username>
In both cases, replace <username>
as appropriate.
Once that is done, use your user’s ssh
session to run the following command:
$ sudo su -
In theory, the command su -
should also work here.
When prompted, enter your user’s password.
You should get a root
shell. If you did, run the following command:
# exit
You have successfully given sudo
access to your user.
Make ssh
Use Public Keys
Make sure you have, on your home computer, already set up an ssh
key
pair.
Once that is done, login to your user on the server, not root
.
Run the following commands:
$ mkdir .ssh
$ touch .ssh/authorized_keys
$ chmod -R 700 .ssh
$ chmod 600 .ssh/authorized_keys
Those four commands create and set up a file that will hold your public key,
which will allow you to ssh
into the server without a password.
On your home computer, find the file that holds the public key of the key pair
you just set up. It should be at ~/.ssh/<key_name>.pub
.
Open that file in a text editor.
Make sure you open the file with the .pub
extension! If you do not, then
you may copy the secret key, which would compromise the security of the key
pair!
Copy the text in that file, and then on your user’s ssh
session, run the
following:
$ nano .ssh/authorized_keys
Paste the text you just copied from your public key. Save the file, and close it.
Next, on your root
ssh
session (open a new one if you already closed the
previous one), open the /etc/ssh/sshd_config
file in a text editor.
You should see a line that contains either:
PubKeyAuthentication yes
or:
PubKeyAuthentication no
The line may also be commented out. If it is, just uncomment it first.
Make sure it says:
PubKeyAuthentication yes
Save the file, and close it.
Restart the ssh
service on your server. On Arch Linux, which uses systemd
,
the command to do so is:
# systemctl restart sshd
Log out of your user’s ssh
session. Then try to ssh
back in.
When doing this, DO NOT LOG OUT OF YOUR root
SESSION! Instead, create a
new ssh
session for your user.
If it asks you for a password, something went wrong, but if it doesn’t, you have
successfully set up your ssh
keys for logging into the server.
Disable Password Login on ssh
Once you have completed the above, you can now try to disable password login on
ssh
.
On your root
ssh
session, open the /etc/ssh/sshd_config
file in a text
editor. Find the line that says something like:
ChallengeResponseAuthentication no
Make sure it looks exactly like the above (i.e., it’s not commented out and it
does not say yes
).
Then, find the line that says something like:
PasswordAuthentication yes
Make sure it says:
PasswordAuthentication no
and that it is not commented out.
Save and close the file, and then restart the ssh
service. Again, on Arch
Linux, the command is:
# systemctl restart sshd
Log out of your user’s ssh
session and try logging back in again. If it
fails, something went wrong, and you should not continue until it works.
Disable root
Login on ssh
When you have completed the above, in your root
ssh
session, open the
/etc/ssh/sshd_config
file in a text editor again and find the line that says
something like:
PermitRootLogin yes
Make sure it says:
PermitRootLogin no
and that it is not commented out.
Save and close the file, and restart the ssh
service.
Do NOT close your root
ssh
session yet! First, log out of your user’s
session, and log back in. If you can get in, and successfully get a root
shell
from running the following command:
$ sudo su -
then you are ready to log out of your root
ssh
session.
Switch ssh
Port
It is a good idea to not use the standard port 22 for ssh
, so we will fix
that.
In an ssh
session, run the following command:
$ sudo su -
When you have a root
shell, open the /etc/ssh/sshd_config
file again and
find the line that looks like:
# Port 22
Uncomment it and set it to the random port number you picked in the firewall section.
Then restart the ssh
service.
Now, do NOT log off your current ssh
session. Instead, open a new
session with the following command:
$ ssh -p <port_number> <username>@<server_ip>
making sure to replace <port_number>
, <username>
, and <server_ip>
appropriately.
If you successfully log in, you can log out the first session. From now on, that
will be the command you need to run to ssh
into your server. I would suggest
setting an alias.
Do not reveal the ssh
port. It won’t have much effect if you do, but it will
increase the amount of script kiddies that try to compromise it.
Set Up an ssh
Tarpit
Next, we are going to set up endlessh
as an ssh
tarpit on port
22.
Log into your server by ssh
, install git
, and clone the endlessh
repo.
You should be able to build and install it with these commands:
$ make
$ sudo make install
If you can’t build it, make sure you have make
and a C compiler installed.
Then set up endlessh
as a service with your init system. On Arch, this is done
by setting up a systemd
service.
On Arch, create and open the file /etc/systemd/system/endlessh.service
. Put
the following contents into it:
[Unit]
Description = an ssh tarpit
After = network.target
[Service]
ExecStart = /usr/local/bin/endlessh -s
[Install]
WantedBy = multi-user.target
Then save and close the file. Then create and open /etc/endlessh/config
and
insert the following contents:
# The port on which to listen for new SSH connections.
Port 22
# The endless banner is sent one line at a time. This is the delay
# in milliseconds between individual lines.
Delay 10000
# The length of each line is randomized. This controls the maximum
# length of each line. Shorter lines may keep clients on for longer if
# they give up after a certain number of bytes.
MaxLineLength 32
# Maximum number of connections to accept at a time. Connections beyond
# this are not immediately rejected, but will wait in the queue.
MaxClients 4096
# Set the detail level for the log.
# 0 = Quiet
# 1 = Standard, useful log messages
# 2 = Very noisy debugging information
LogLevel 1
# Set the family of the listening socket
# 0 = Use IPv4 Mapped IPv6 (Both v4 and v6, default)
# 4 = Use IPv4 only
# 6 = Use IPv6 only
BindFamily 0
These, plus the -s
arg to endlessh
makes it listen on port 22 and sends the
logs to syslog.
Enable and start the service with the following commands:
$ sudo systemctl enable endlessh
$ sudo systemctl start endlessh
You can run the following command to see if the service started successfully:
sudo journalctl -xe
Setting Up a Server Program
The next thing to do is to set up your server program. I use nginx
, and I used
a service, nginxconfig.io
, to generate the configs for nginx
and my
websites.
All of my websites are static websites, which means that all of the pages in the sites are static web pages. This, in turn, means that I am not running anything complex in my server, such as PHP or CGI scripts.
In other words, the only things my server program needs to is establish TLS connections and serve files. Oh, and since I also provide compressed versions of every file, it needs to be able to recognize and serve those too.
This greatly simplifies the security set up of my server, and I would suggest you do the same.
If you want to run a server that serves more than static websites, you will need to do more to make sure your server is secure!
Unfortunately, because my websites are simple, I can’t help you with your own website; it could be entirely different. I just suggest taking the time to learn how to configure your server program of choice.
However, for the sake of the visitors to your website, be sure to set up
https
! That was why we left port 443 open. (Port 80 is for insecure
http
.) In fact, if you can set up your server to automatically redirect
all http
traffic to https
, you should.
If you do set up https
, you need a certificate. You can get a free one
from Let’s Encrypt.
Miscellaneous
These are items I added after this post was published:
- Add
fail2ban
to ensure attackers don’t get many chances.
Conclusion
After all this, you should have a server that will withstand attempts by the hurricane of relatively unskilled bad actors on the Internet. However, it probably will not withstand attempts by three-letter agencies.
How to do that is well above my skill level.