Table of Contents

Initial Setup

Let’s login to our new machine and take some initial steps to secure our system. For this article I’m going to assume your username is ubuntu .

If you need to, setup your sudoers file by adding the following lines to /etc/sudoers :

1 ubuntu ALL =( ALL:ALL ) ALL # put this in the "User privilege specification" section

Edit your ~/.ssh/authorized_keys and put your public key inside it. Make sure you can login without a password now once your key is in place.

Open up /etc/ssh/sshd_config and make sure these lines exist to secure SSH:

1 # Only allow version 2 communications, version 1 has known vulnerabilities 2 Protocol 2 3 # Disable root login over ssh 4 PermitRootLogin no 5 # Load authorized keys files from a users home directory 6 AuthorizedKeysFile %h/.ssh/authorized_keys 7 # Don't allow empty passwords to be used to authenticate 8 PermitEmptyPasswords no 9 # Disable password auth, you must use ssh keys 10 PasswordAuthentication no

Keep your current session open and restart sshd:

1 sudo service ssh restart

Make sure you can login from another terminal. If you can, move on.

Now we need to update and upgrade to make sure all of our packages are up to date and install two pre-requisites for later in the article: build-essential and ntp.

1 sudo apt-get update 2 sudo apt-get install build-essential ntp 3 sudo apt-get upgrade 4 sudo reboot

Setting up iptables and Fail2Ban

Fail2Ban

1 sudo apt-get install fail2ban

Open up the fail2ban config and change the ban time, destemail, and maxretry /etc/fail2ban/jail.conf :

1 [ DEFAULT ] 2 ignoreip = 127 .0.0.1/8 3 bantime = 3600 4 maxretry = 2 5 destemail = [email protected] 6 action = % ( action_mw ) s 7 8 [ ssh ] 9 10 enabled = true 11 port = ssh 12 filter = sshd 13 logpath = /var/log/auth.log 14 maxretry = 2

Now restart fail2ban.

1 sudo service fail2ban restart

If you try and login from another machine and fail, you should see the ip in iptables.

1 # sudo iptables -L 2 Chain fail2ban-ssh (1 references) 3 target prot opt source destination 4 DROP all -- li203-XX.members.linode.com anywhere 5 RETURN all -- anywhere anywhere

iptables Rules

Here are my default iptables rules, it opens up port 80 and 443 for HTTP/HTTPS communication, and allows port 22. We also allow ping and then log all denied calls and then reject everything else. If you have other services you need to run, such as a game server or something else, you’ll have to add the rules to open up the ports in the iptables config.

/etc/iptables.up.rules

1 *filter 2 3 # Accepts all established inbound connections 4 -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 5 6 # Allows all outbound traffic 7 # You could modify this to only allow certain traffic 8 -A OUTPUT -j ACCEPT 9 10 # Allows HTTP and HTTPS connections from anywhere (the normal ports for websites) 11 -A INPUT -p tcp --dport 443 -j ACCEPT 12 -A INPUT -p tcp --dport 80 -j ACCEPT 13 # Allows SSH connections for script kiddies 14 # THE -dport NUMBER IS THE SAME ONE YOU SET UP IN THE SSHD_CONFIG FILE 15 -A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT 16 17 # Now you should read up on iptables rules and consider whether ssh access 18 # for everyone is really desired. Most likely you will only allow access from certain IPs. 19 20 # Allow ping 21 -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT 22 23 # log iptables denied calls (access via 'dmesg' command) 24 -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7 25 26 # Reject all other inbound - default deny unless explicitly allowed policy: 27 -A INPUT -j REJECT 28 -A FORWARD -j REJECT 29 30 COMMIT

We can load that up into iptables:

1 sudo iptables-restore < /etc/iptables.up.rules

Make sure it loads on boot by putting it into the if-up scripts: /etc/network/if-up.d/iptables

1 #!/bin/sh 2 iptables-restore /etc/iptables.up.rules

Now make it executable:

1 chmod +x /etc/network/if-up.d/iptables

Rebooting here is optional, I usually reboot after major changes to make sure everything boots up properly.

If you’re getting hit by scanners or brute-force attacks, you’ll see a line similar to this in your /var/log/syslog :

1 Jan 18 03:30:37 localhost kernel: [ 79.631680] iptables denied: IN=eth0 OUT= MAC=04:01:01:40:70:01:00:12:f2:c6:e8:00:08:00 SRC=87.13.110.30 DST=192.34.XX.XX LEN=64 TOS=0x00 PREC=0x00 TTL=34 ID=57021 DF PROTO=TCP SPT=1253 DPT=135 WINDOW=53760 RES=0x00 SYN URGP=0

Read only shared memory

A common exploit vector is going through shared memory (which can let you change the UID of running programs and other malicious actions). It can also be used as a place to drop files once an initial breakin has been made. An example of one such exploit is available here.

Open /etc/fstab/ :

1 tmpfs /dev/shm tmpfs defaults,ro 0 0

Once you do this you need to reboot.

Setting up Bastille Linux

The Bastille Hardening program “locks down” an operating system, proactively configuring the system for increased security and decreasing its susceptibility to compromise. Bastille can also assess a system’s current state of hardening, granularly reporting on each of the security settings with which it works.

Bastille: Installation and Setup

1 sudo apt-get install bastille # choose Internet site for postfix 2 # configure bastille 3 sudo bastille

After you run that command you’ll be prompted to configure your system, here are the options I chose:

Configuring Bastille

File permissions module: Yes (suid)

Disable SUID for mount/umount: Yes

Disable SUID on ping: Yes

Disable clear-text r-protocols that use IP-based authentication? Yes

Enforce password aging? No (situation dependent, I have no users accessing my machines except me, and I only allow ssh keys)

Default umask: Yes

Umask: 077

Disable root login on tty’s 1-6: No

Password protect GRUB prompt: No (situation dependent, I’m on a VPS and would like to get support in case I need it)

Password protect su mode: Yes

default-deny on tcp-wrappers and xinetd? No

Ensure telnet doesn’t run? Yes

Ensure FTP does not run? Yes

display authorized use message? No (situation dependent, if you had other users, Yes)

Put limits on system resource usage? Yes

Restrict console access to group of users? Yes (then choose root)

Add additional logging? Yes

Setup remote logging if you have a remote log host, I don’t so I answered No

Setup process accounting? Yes

Disable acpid? Yes

Deactivate nfs + samba? Yes (situation dependent)

Stop sendmail from running in daemon mode? No (I have this firewalled off, so I’m not concerned)

Deactivate apache? Yes

Disable printing? Yes

TMPDIR/TMP scripts? No (if a multi-user system, yes)

Packet filtering script? No (we configured the firewall previously)

Finished? YES! & reboot

You can verify some of these changes by testing them out, for instance, the SUID change on ping:

Bastille: Verifying changes

1 [email protected]:~$ ping google.com 2 ping: icmp open socket: Operation not permitted 3 [email protected]:~$ sudo ping google.com 4 PING google.com ( 74 .125.228.72 ) 56 ( 84 ) bytes of data. 5 64 bytes from iad23s07-in-f8.1e100.net ( 74 .125.228.72 ) : icmp_req = 1 ttl = 55 time = 9 .06 ms 6 ^C 7 --- google.com ping statistics --- 8 1 packets transmitted, 1 received, 0 % packet loss, time 0ms 9 rtt min/avg/max/mdev = 9 .067/9.067/9.067/0.000 ms

Sysctl hardening

Since our machine isn’t running as a router and is going to be running as an application/web server, there are additional steps we can take to secure the machine. Many of these are from the NSA’s security guide, which you can read in its entirety here.

/etc/sysctl.conf http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf Source

1 # Protect ICMP attacks 2 net.ipv4.icmp_echo_ignore_broadcasts = 1 3 4 # Turn on protection for bad icmp error messages 5 net.ipv4.icmp_ignore_bogus_error_responses = 1 6 7 # Turn on syncookies for SYN flood attack protection 8 net.ipv4.tcp_syncookies = 1 9 10 # Log suspcicious packets, such as spoofed, source-routed, and redirect 11 net.ipv4.conf.all.log_martians = 1 12 net.ipv4.conf.default.log_martians = 1 13 14 # Disables these ipv4 features, not very legitimate uses 15 net.ipv4.conf.all.accept_source_route = 0 16 net.ipv4.conf.default.accept_source_route = 0 17 18 # Enables RFC-reccomended source validation (dont use on a router) 19 net.ipv4.conf.all.rp_filter = 1 20 net.ipv4.conf.default.rp_filter = 1 21 22 # Make sure no one can alter the routing tables 23 net.ipv4.conf.all.accept_redirects = 0 24 net.ipv4.conf.default.accept_redirects = 0 25 net.ipv4.conf.all.secure_redirects = 0 26 net.ipv4.conf.default.secure_redirects = 0 27 28 # Host only (we're not a router) 29 net.ipv4.ip_forward = 0 30 net.ipv4.conf.all.send_redirects = 0 31 net.ipv4.conf.default.send_redirects = 0 32 33 34 # Turn on execshild 35 kernel.exec-shield = 1 36 kernel.randomize_va_space = 1 37 38 # Tune IPv6 39 net.ipv6.conf.default.router_solicitations = 0 40 net.ipv6.conf.default.accept_ra_rtr_pref = 0 41 net.ipv6.conf.default.accept_ra_pinfo = 0 42 net.ipv6.conf.default.accept_ra_defrtr = 0 43 net.ipv6.conf.default.autoconf = 0 44 net.ipv6.conf.default.dad_transmits = 0 45 net.ipv6.conf.default.max_addresses = 1 46 47 # Optimization for port usefor LBs 48 # Increase system file descriptor limit 49 fs.file-max = 65535 50 51 # Allow for more PIDs (to reduce rollover problems); may break some programs 32768 52 kernel.pid_max = 65536 53 54 # Increase system IP port limits 55 net.ipv4.ip_local_port_range = 2000 65000 56 57 # Increase TCP max buffer size setable using setsockopt() 58 net.ipv4.tcp_rmem = 4096 87380 8388608 59 net.ipv4.tcp_wmem = 4096 87380 8388608 60 61 # Increase Linux auto tuning TCP buffer limits 62 # min, default, and max number of bytes to use 63 # set max to at least 4MB, or higher if you use very high BDP paths 64 net.core.rmem_max = 8388608 65 net.core.wmem_max = 8388608 66 net.core.netdev_max_backlog = 5000 67 net.ipv4.tcp_window_scaling = 1

After making these changes you should reboot.

Setting up a chroot environment

We’ll be setting up a chroot environment to run our web server and applications in. Chroot’s provide isolation from the rest of the operating system, so even in the event of a application compromise, damage can be mitigated.

chroot: Installation and Setup

1 sudo apt-get install debootstrap dchroot

Now add this to your /etc/schroot/schroot.conf file, precise is the release of Ubuntu I’m using, so change it if you need to:

/etc/schroot/schroot.conf

1 [ precise ] 2 description = Ubuntu Precise LTS 3 location = /var/chroot 4 priority = 3 5 users = ubuntu 6 groups = sbuild 7 root-groups = root

Now bootstrap the chroot with a minimal Ubuntu installation:

1 sudo debootstrap --variant = buildd --arch amd64 precise /var/chroot/ http://mirror.anl.gov/pub/ubuntu/ 2 sudo cp /etc/resolv.conf /var/chroot/etc/resolv.conf 3 sudo mount -o bind /proc /var/chroot/proc 4 sudo chroot /var/chroot/ 5 apt-get install ubuntu-minimal 6 apt-get update

Add the following to /etc/apt/sources.list inside the chroot:

1 deb http://archive.ubuntu.com/ubuntu precise main 2 deb http://archive.ubuntu.com/ubuntu precise-updates main 3 deb http://security.ubuntu.com/ubuntu precise-security main 4 deb http://archive.ubuntu.com/ubuntu precise universe 5 deb http://archive.ubuntu.com/ubuntu precise-updates universe

Let’s test out our chroot and install nginx inside of it:

1 apt-get update 2 apt-get install nginx

Securing nginx inside the chroot

First thing we will do is add a www user for nginx to run under: Adding a application user

1 sudo chroot /var/chroot 2 useradd www -d /home/www 3 mkdir /home/www 4 chown -R www.www /home/www

Open up /etc/nginx/nginx.conf and make sure you change user to www inside the chroot:

1 user www ;

We can now start nginx inside the chroot:

1 sudo chroot /var/chroot 2 service nginx start

Now if you go to http://your_vm_ip/ you should see “Welcome to nginx!” running inside your fancy new chroot.

We also need to setup ssh to run inside the chroot so we can deploy our applications more easily.

Chroot: sshd

1 sudo chroot /var/chroot 2 apt-get install openssh-server udev

Since we already have SSH for the main host running on 22, we’re going to run SSH for the chroot on port 2222. We’ll copy over our config from outside the chroot to the chroot.

sshd config

1 sudo cp /etc/ssh/sshd_config /var/chroot/etc/ssh/sshd_config

Now open the config and change the bind port to 2222.

We also need to add the rules to our firewall script: /etc/iptables.up.rules

1 # Chroot ssh 2 -A INPUT -p tcp -m state --state NEW --dport 2222 -j ACCEPT

Now make a startup script for chroot-precise in /etc/init.d/chroot-precise: /etc/init.d/chroot-precise`

1 mount -o bind /proc /var/chroot/proc 2 mount -o bind /dev /var/chroot/dev 3 mount -o bind /sys /var/chroot/sys 4 mount -o bind /dev/pts /var/chroot/dev/pts 5 chroot /var/chroot service nginx start 6 chroot /var/chroot service ssh start

Set it to executable and to start at boot:

1 sudo chmod +x /etc/init.d/chroot-precise 2 sudo update-rc.d chroot-precise defaults

Next is to put your public key inside the .ssh/authorized_keys file for the www user inside the chroot so you can ssh and deploy your applications.

If you want, you can test your server and reboot it now to ensure nginx and ssh boot up properly. If it’s not running right now, you start it: sudo /etc/init.d/chroot-precise .

You should now be able to ssh into your chroot and main server without a password.

Extras

I would like to also mention the GRSecurity kernel patch. I had tried several times to install this (two different versions were released while I was writing this) and both make the kernel unable to compile. Hopefully they’ll fix these bugs and I’ll be able to update this article with notes on setting GRSecurity up as well.

I hope this article proved useful to anyone trying to secure a Ubuntu system, and if you liked it please share it!