Steps to secure our VPS

This tutorial shows how to prepare and secure a Virtual Private Server (VPS) with Debian GNU / Linux. Before we begin, certain things are assumed:

  1. You have an intermediate level of familiarity with GNU / Linux.
  2. There is a VPS for personal use to which we have access via SSH.
  3. The VPS has the dedicated external ipv4 250.250.250.155 and our provider owns the 250.250.0.0/16 block. (1)
  4. In our VPS we will only have http, https and ssh services enabled for access from outside.
  5. External DNS will not be enabled since it is usually done in our provider's panel. (2)
  6. It will work as superuser.

Installation

As a first step, let's update the server and install some packages that we will need:

# aptitude update & aptitude safe-upgrade # aptitude -RvW install dropbear gesftpserver sslh iptables-persistent ulogd fail2ban nginx-light apache2-utils dnsutils telnet ghostscript poppler-utils zip unzip unrar-free p7zip-full less multitail tee mc

Configuration

Now we are going to create a work user. Working as root on a server is insecure, so we will first create a special user:

adduser operator usermod -aG sudo operator

The first command creates the operator user, the second adds it to the group sudo, which will allow you to run applications as root.

Adjust permissions for super users

As to work regularly we will use the user operator previously created, we need to adjust the command execution options as superuser, for which we execute the following command:

visudo

This command basically allows modifying the file / Etc / sudoers; in which we should contain these lines:

Defaults env_reset, timestamp_timeout = 0% sudo ALL = (ALL: ALL) ALL

In the first line the option is added to the default values timestamp_timeout which allows setting the expiration time (in minutes) of the password when the sudo command is executed. The default is 5, but this is sometimes unsafe for two reasons:

  1. If we inadvertently leave our computer logged in before the password expires, someone could execute a command as superuser without any restrictions.
  2. If, through ignorance, we execute an application or script that contains malicious code before the password expires, the application could have access to our system as superuser, without our explicit consent.

So to avoid risks, we have set the value to zero, that is, each time the sudo command is executed, the password will have to be entered. If a negative value is set as -1, the effect is that the password never expires, which would produce the opposite result of what we want.

The second line clarifies that the sudo group can execute any command on any computer, which is the usual thing, although it can be adjusted. (3) There are those who for convenience put the line in the following way to avoid having to type the password:

% sudo ALL = (ALL: ALL) NOPASSWD: ALL

However, as we explained before, this is risky, and therefore not recommended.

Disable restart

For security reasons, we will also disable the restart using the key combination Ctrl + Alt + Del, for which we must add this line in the file / etc / inittab:

ca: 12345: ctrlaltdel: / bin / echo "Ctrl + Alt + Del has been disabled."

Replace OpenSSH with DropBear

Most VPS come with OpenSSH installed, which is certainly very useful, but unless we need to exploit all the functionality of OpenSSH, there are lighter alternatives for a VPS, such as dropbear, which is usually sufficient for regular use. However, a drawback of this application is that it does not come with an integrated SFTP server, and that is why at the beginning we installed the package gesftpserver.

To configure Dropbear, we will modify the file / etc / default / dropbear so that it contains these two lines:

NO_START = 0 DROPBEAR_EXTRA_ARGS = "- w -p 127.0.0.1:22 -I 1200 -m"

The first line simply enables the service, and the second does several things:

  1. Avoid root access.
  2. It makes the service listen on port 22 of the local interface (we will explain why later).
  3. Sets the wait time (20 minutes).

SSLH

Port 22 (SSH) is well known and is generally one of the first that hackers try to breach, so we will use port 443 (SSL) instead. It happens that this port is used for secure browsing over HTTPS.

For this reason we will use the sslh package, which is nothing more than a multiplexer that analyzes the packets that arrive at port 443, and routes them internally to one service or another depending on whether the type of traffic is SSH or SSL.

SSLH cannot listen on an interface where another service is already listening, which is why we previously made Dropbear listen on the local interface.

Now what we need to do is indicate to sslh the interface and the port through which it should listen and where to redirect the packets depending on the type of service, and for this we will modify the configuration file / etc / default / sslh:

DAEMON = / usr / sbin / sslh DAEMON_OPTS = "- user sslh --listen 250.250.250.155:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:443 --pidfile / var / run / sslh / sslh .pid "RUN = yes

Finally, we restart the services:

service ssh stop && service dropbear start && service sslh restart

After the previous command, our secure session will probably be interrupted, in which case it is enough to log in again, but this time with the work user and using port 443. If the session is not interrupted, it is advisable to close it and start again. with the appropriate values.

If everything works correctly, we can continue working as root and if we wish, uninstall OpenSSH:

sudo su - aptitude -r purge openssh-server

firewalls

The next thing we will do is separate the logs from the firewall into the separate file /var/log/firewall.log to facilitate further analysis, which is why we installed the ulogd package at startup. For this we will edit the file /etc/logd.conf to adjust the relevant section:

[LOGEMU] file = "/ var / log / firewall.log" sync = 1

Next, we will modify the record rotation file / etc / logrotate / ulogd to keep a daily rotation (with date) and save compressed salvoes in the directory / var / log / ulog /:

/var/log/ulog/*.log /var/log/firewall.log {daily dateext missingok compress delaycompress sharedscripts create 640 root adm postrotate /etc/init.d/ulogd reload mv /var/log/firewall.log-* .gz / var / log / ulog / endscript}

So we will create the netfilter rules by executing the following:

IPT = $ (which iptables) IPEXT = 250.250.250.155 IPEXTBLK = 250.250.0.0 / 16 IPBCAST = 255.255.255.255 $ IPT -F $ IPT -X $ IPT -Z $ IPT -A INPUT -i lo -j ACCEPT $ IPT - P INPUT DROP $ IPT -P FORWARD DROP $ IPT -P OUTPUT ACCEPT $ IPT -A INPUT -m state --state INVALID -j ULOG --ulog-prefix IN_INVALID $ IPT -A INPUT -p igmp -j ULOG --ulog -prefix IN_IGMP $ IPT -A INPUT -m pkttype --pkt-type broadcast -j ULOG --ulog-prefix IN_BCAST $ IPT -A INPUT -m pkttype --pkt-type multicast -j ULOG --ulog-prefix IN_MCAST $ IPT -A FORWARD -j ULOG --ulog-prefix FORWARD $ IPT -N ICMP_IN $ IPT -A INPUT!  -i lo -p icmp -j ICMP_IN $ IPT -A ICMP_IN -p icmp -f -j ULOG --ulog-prefix IN_ICMP_FRAGMENTED $ IPT -A ICMP_IN -p icmp -m icmp -m length!  --length 28: 1322 -j ULOG --ulog-prefix IN_ICMP_INVALIDSIZE $ IPT -A ICMP_IN -p icmp -m icmp -m hashlimit --hashlimit-above 4 / sec --hashlimit-mode srcip --hashlimit-srcmask 24 - -hashlimit-name icmpflood -j ULOG --ulog-prefix IN_ICMP_FLOOD $ IPT -A ICMP_IN -p icmp -m icmp -m hashlimit --hashlimit-upto 64kb / min --hashlimit-mode srcip --hashlimit-srcmask 24 - hashlimit-name icmpattack -j ULOG --ulog-prefix IN_ICMP_FLOOD $ IPT -A ICMP_IN -p icmp -m icmp -m u32!  --u32 "0x4 & 0x3fff = 0x0" -j ULOG --ulog-prefix IN_ICMP_ATTACK $ IPT -A ICMP_IN -p icmp -m icmp!  --icmp-type echo-request -m state --state NEW -j ULOG --ulog-prefix IN_ICMP_INVALID $ IPT -A ICMP_IN -p icmp -m icmp --icmp-type echo-request -j ULOG --ulog- prefix IN_ICMP $ IPT -A ICMP_IN -p icmp -m icmp --icmp-type echo-request -m limit --limit 1 / sec --limit-burst 4 -j ACCEPT $ IPT -A ICMP_IN -p icmp -m icmp --icmp-type echo-reply -m limit --limit 2 / sec --limit-burst 4 -j ACCEPT $ IPT -A ICMP_IN -p icmp -m icmp --icmp-type destination-unreachable -m limit - limit 2 / sec --limit-burst 4 -j ACCEPT $ IPT -A ICMP_IN -p icmp -m icmp --icmp-type time-exceeded -m limit --limit 2 / sec --limit-burst 4 -j ACCEPT $ IPT -A ICMP_IN -p icmp -m icmp --icmp-type parameter-problem -m limit --limit 2 / sec --limit-burst 4 -j ACCEPT $ IPT -A ICMP_IN -j RETURN $ IPT -N UDP_IN $ IPT -A INPUT!  -i lo -p udp -j UDP_IN $ IPT -A UDP_IN!  -i lo!  -p udp -f -j ULOG --ulog-prefix IN_UDP_FRAGMENTED $ IPT -A UDP_IN -p udp -m udp --sport 53 -m length!  --length 28: 576 -j ULOG --ulog-prefix IN_UDP_DNS_INVALIDSIZE $ IPT -A UDP_IN -p udp -m udp --dport 53 -m -state --state NEW -j ULOG --ulog-prefix IN_UDP_DNSREQUEST $ IPT - A UDP_IN -p udp -m udp --dport 53 -m -state --state NEW -j REJECT --reject-with icmp-port-unreachable $ IPT -A UDP_IN -p udp -m udp!  --sport 53!  -s $ IPEXTBLK!  -d $ IPBCAST -m state --state NEW -j ULOG --ulog-prefix IN_UDP $ IPT -A UDP_IN -p udp -m udp -m state --state ESTABLISHED, RELATED -j ACCEPT $ IPT -A UDP_IN -j RETURN $ IPT -N TCP_IN $ IPT -A INPUT!  -i lo -p tcp -j TCP_IN $ IPT -A TCP_IN!  -i lo!  -p tcp -f -j ULOG --ulog-prefix IN_TCP_FRAGMENTED $ IPT -A TCP_IN -p tcp -m tcp --sport 53 -m state --state ESTABLISHED, RELATED -m length!  --length 513: 1500 -j ULOG --ulog-prefix IN_TCP_DNS_INVALIDSIZE $ IPT -A TCP_IN -p tcp -m tcp --dport 53 -m state --state NEW -j ULOG --ulog-prefix IN_TCP_DNS $ IPT -A TCP_IN -p tcp -m tcp --dport 53 -m state --state NEW -j REJECT --reject-with icmp-port-unreachable $ IPT -A TCP_IN -p tcp -m tcp -m multiport!  --dports 80,443 -m state --state NEW -j ULOG --ulog-prefix IN_TCP $ IPT -A TCP_IN -p tcp -m tcp -m multiport --dports 80,443 -m state --state NEW -m hashlimit - hashlimit-upto 4 / sec --hashlimit-burst 16 --hashlimit-mode srcip --hashlimit-name navreq -j ACCEPT $ IPT -A TCP_IN -p tcp -m tcp -m multiport --dports 80,443 -m state - state ESTABLISHED -m connlimit!  --connlimit-above 16 -j ACCEPT $ IPT -A TCP_IN -p tcp -m tcp -m multiport! 

With the previous configuration, our VPS should be reasonably secured, but if we wish we can secure it a little more, for which we can use some more advanced rules.

Not all VPS allow the installation of extra modules for netfilter, but a very useful one is psd, which allows you to avoid port scans. Unfortunately, this module is not integrated into netfilter by default, so it is necessary to install certain packages and then build the module:

aptitude -RvW install iptables-dev xtables-addons-source module-assistant module-assistant --verbose --text-mode auto-install xtables-addons-source

Once the above is done, we can add a rule like this:

iptables -A INPUT -m psd --psd-weight-threshold 15 --psd-delay-threshold 2000 --psd-lo-port-weight 3 --psd-hi-ports-weight 1 -j ULOG --ulog- prefix IN_PORTSCAN

The above rule basically means that we will create a counter that will be incremented by 3 each time an attempt is made to access a port lower than 1024 and by 1 each time an attempt is made to access a port higher than 1023, and when this counter reaches 15 in a period of less than 20 seconds, the packages will be registered by ulog as an attempt at portscan. The packets could still be discarded at once, but in this case we intend to use fail2ban, which we will configure later.

Once the rules are created, we must take certain precautions to make them persistent, otherwise we will lose them when the server is restarted. There are several ways to accomplish this; In this tutorial we will use the iptables-persistent package that we installed at the beginning, which stores the rules in /etc/iptables/rules.v4 y /etc/iptables/rules.v6 for ipv6.

iptables-save> /etc/iptables/rules.v4

In fact, although the use of ipv6 in Cuba is not yet widespread, we could well create some basic rules:

IPT = $ (which ip6tables) $ IPT -P INPUT DROP $ IPT -P FORWARD DROP $ IPT -P OUTPUT ACCEPT $ IPT -A INPUT -i lo -j ACCEPT $ IPT -A INPUT! -i lo -m state --state ESTABLISHED, RELATED -j ACCEPT unset IPT

These rules can also be made persistent:

ip6tables-save> /etc/iptables/rules.v6

Finally for greater security, we clean the registry of the firewall and restart the services:

echo -n> /var/log/firewall.log service logrotate restart service ulogd restart service iptables-persistent restart

Nginx

We will use Nginx as a web server, because VPSs tend to have a reduced amount of RAM compared to a real server, so generally it is convenient to have something lighter than Apache.

Before configuring Nginx, we will create a certificate (no password) for use over HTTPS:

cd / etc / nginx openssl genrsa -des3 -out cert.key 4096 cp -v cert.key cert.key.original openssl req -new -key cert.key -out cert.csr openssl rsa -in cert.key.original - out cert.key openssl x509 -req -days 365 -in cert.csr -signkey cert.key -out cert.crt

Once this is done, we will create a password file for the user "elusuario":

htpasswd -c .htpasswd the user

Next, we will modify the file / etc / nginx / sites-available / default to set the default site preferences. It could look like this:

server {server_name localhost; index index.html index.htm default.html default.htm; root / var / www; location / {# set the order of verification and the page to load, if the URI is not found try_files $ uri $ uri / /index.html; }} server {listen 127.0.0.1:443; server_name localhost; index index.html index.htm default.html default.htm; root / var / www; ssl on; ssl_certificate cert.crt; ssl_certificate_key cert.key; ssl_session_timeout 5m; # Enable HTTPS only over TLS (more secure than SSL) ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # give preference to high-strength ciphers [HIGH], # move medium-strength ciphers [MEDIUM] to the end of the list, # disable low-strength ciphers [LOW] (40 and 56 bits) # disable ciphers with export algorithms [EXP] # disable null ciphers [eNULL], without authentication [aNULL], SSL (versions 2 and 3) and DSS (only allow keys up to 1024 bits) ssl_ciphers HIGH: + MEDIUM:! LOW:! EXP: ! aNULL:! eNULL:! SSLv3:! SSLv2:! DSS; # Prefer the server's encryption methods (by default the client's are used) ssl_prefer_server_ciphers on; location / {# enable authentication auth_basic "Login"; auth_basic_user_file /etc/nginx/.htpasswd; # set the order of verification and the page code to load, if the URI try_files $ uri $ uri / = 404 is not found; # allow the creation of an index for authenticated users autoindex on; autoindex_exact_size off; autoindex_localtime on; }}

We check that the configuration is correct:

nginx -t

Finally, we restart the service:

service nginx restart

Fail2Ban

Before starting to configure Fail2Ban, for greater security we stop the service and clean the registry:

fail2ban-client stop echo -n> /var/log/fail2ban.log

Next, we create the configuration file /etc/fail2ban/jail.local with the following custom content:

# Custom configuration file /etc/fail2ban/jail.local # [DEFAULT] findtime = 43200; 12 hours bantime = 86400; 1 day maxretry = 3; ban will take effect after the 4th attempt [ssh] enabled = false [nginx-auth] enabled = true filter = nginx-auth action = iptables-multiport [name = NoAuthFailures, port = "http, https"] logpath = / var / log / nginx * / * error * .log [nginx-badbots] enabled = true filter = apache-badbots action = iptables-multiport [name = BadBots, port = "http, https"] logpath = / var / log / nginx * /*access*.log bantime = 604800; 1 week maxretry = 0 [nginx-login] enabled = true filter = nginx-login action = iptables-multiport [name = NoLoginFailures, port = "http, https"] logpath = / var / log / nginx * / * access *. log bantime = 1800; 30 minutes [nginx-noscript] enabled = true action = iptables-multiport [name = NoScript, port = "http, https"] filter = nginx-noscript logpath = /var/log/nginx*/*access*.log maxretry = 0 [nginx-proxy] enabled = true action = iptables-multiport [name = NoProxy, port = "http, https"] filter = nginx-proxy logpath = /var/log/nginx*/*access*.log bantime = 604800 ; 1 week maxretry = 0 [firewall] enabled = true action = iptables-multiport [name = Firewall] filter = firewall logpath = /var/log/firewall.log maxretry = 0

Once this is done, we create in the directory /etc/fail2ban/filters.d/ the following files:

# /etc/fail2ban/filter.d/nginx-auth.conf # Auth filter # Blocks IPs that fail to authenticate using basic authentication # [Definition] failregex = no user / password was provided for basic authentication. * client: user. * was not found in. * client: user. * password mismatch. * client: ignoreregex =
# /etc/fail2ban/filter.d/nginx-login.conf # Login filter # Blocks IPs that fail to authenticate using web application's log in page # Scan access log for HTTP 200 + POST / sessions => failed log in # [Definition ] failregex = ^ -. * POST / sessions HTTP / 1 \ .. "200 ignoreregex =
# /etc/fail2ban/filter.d/nginx-noscript.conf # Noscript filter # Block IPs trying to execute scripts such as .php, .pl, .exe and other funny scripts. # Matches eg # 192.168.1.1 - - "GET /something.php # [Definition] failregex = ^ -. * GET. * (\. Php | \ .asp | \ .exe | \ .pl | \ .cgi | \ scgi) ignoreregex =
# /etc/fail2ban/filter.d/proxy.conf # Proxy filter # Block IPs trying to use server as proxy. # Matches eg # 192.168.1.1 - - "GET http://www.something.com/ # [Definition] failregex = ^ -. * GET http. * Ignoreregex =
# /etc/fail2ban/filter.d/firewall.conf # Firewall filter # [Definition] failregex = ^. * IN_ (INVALID | PORTSCAN | UDP | TCP |). * SRC = . * $ ignoreregex =

Finally, we start the service and load the configuration:

fail2ban-service -b fail2ban-client reload

CHECKOUT

As a last step, we can view the records with tail -f o multitail –follow-all. In fact, the latter application offers the advantage that it allows you to view multiple files at the same time and provides basic syntax highlighting.

If an email account is not configured in the VPS, it is advisable to disable a warning message that appears when starting multitail, for which we will execute the following command:

echo "check_mail: 0"> ~ / .multitailrc

In fact, we could well make an alias (4) to view the logs quickly with a short command, for example, "flog":

alias flog = 'multitail --follow-all /var/log/firewall.log /var/log/fail2ban.log'

1) These are fictitious values.
2) Enabling other services is easy once you understand how it works.
3) For more details, run man sudoers.
4) Optionally could be added to ~ / .bash_aliases file


Leave a Comment

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

*

*

  1. Responsible for the data: Miguel Ángel Gatón
  2. Purpose of the data: Control SPAM, comment management.
  3. Legitimation: Your consent
  4. Communication of the data: The data will not be communicated to third parties except by legal obligation.
  5. Data storage: Database hosted by Occentus Networks (EU)
  6. Rights: At any time you can limit, recover and delete your information.

  1.   msx said

    There are some interesting things, +1

  2.   yukiteru said

    @Hugo this line in the configuration:

    ssl_protocols SSLv3 TLSv1;

    I would take the SSLv3 out of it because that protocol is no longer secure, even in Debian Jessie, many services have been configured to avoid using that protocol for that reason.

    Information on the topic here:

    https://www.linode.com/docs/security/security-patches/disabling-sslv3-for-poodle
    http://disablessl3.com/

    1.    Hugo said

      The idea was not really to offer the main services over HTTPS, but to explain how to use port 443 for SSH without losing the possibility of using it for HTTPS if necessary, but thanks for the warning.

      Anyway I have updated the article to modify the nginx configuration a bit and incidentally include some comments to clarify things a little more with this of the encryption mechanisms, and to fix some minor errors.

  3.   Daniel PZ said

    Thank you very much for this great tutorial, now I will put it into practice! :D, Keep it up DesdeLinux, they always surprise me, Greetings from Peru.

  4.   Ñandekuera said

    Thank you very much for sharing.

  5.   fernando said

    very good guide and it comes from pearls now that I started in this blog but even more so now that I am about to mount my first vps and still with many problems but this article has taken me out of many doubts, thanks and greetings