Hardening Ubuntu 12.04 Desktop edition for hosting a web server

As a part of my Operating System security course, I was asked to harden a Ubuntu Desktop edition for hosting a web server. I used Ubuntu 12.04 as a guest machine in Virtualbox. Its better to install packages like openssh-server and vim before you start executing the below given commands.

Removing all the unwanted applications

Remove ubuntu-desktop and all the gnome packages. *This is not required if you are using a server edition*

$ sudo apt-get remove gnome-* 
$ sudo apt-get remove alacarte app-install-data-commercial apport-gtk apturl at-spi binfmt-support bluez-gnome brasero brltty-x11 capplets-data cli-common compiz compiz-core compiz-fusion-plugins-extra compiz-fusion-plugins-main compiz-gnome compiz-plugins compizconfig-backend-gconf contact-lookup-applet dcraw deskbar-applet desktop-file-utils dmz-cursor-theme doc-base docbook-xml ekiga eog espeak espeak-data evince evolution evolution-common evolution-data-server evolution-data-server-common evolution-exchange evolution-plugins evolution-webcal example-content f-spot fast-user-switch-applet file-roller firefox firefox-3.0 firefox-3.0-branding firefox-3.0-gnome-support firefox-gnome-support gamin gcalctool gconf-editor gconf2 gconf2-common gdebi gdm gdm-guest-session gedit gedit-common ggzcore-bin gimp gimp-data gksu gstreamer0.10-alsa gstreamer0.10-gnomevfs gstreamer0.10-plugins-base gstreamer0.10-plugins-base-apps gstreamer0.10-plugins-good gstreamer0.10-pulseaudio gstreamer0.10-schroedinger gstreamer0.10-tools gstreamer0.10-x gtk2-engines gtk2-engines-murrine gtk2-engines-pixbuf gucharmap guile-1.8-libs gvfs gvfs-backends gvfs-bin gvfs-fuse human-icon-theme human-theme hwtest hwtest-gtk jockey-gtk language-selector libart2.0-cil libasound2-plugins libatspi1.0-0 libavahi-glib1 libavahi-gobject0 libavahi-ui0 libavc1394-0 libbabl-0.0-0 libbeagle1 libbonobo2-0 libbonobo2-common libbonoboui2-0 libbonoboui2-common libcairo-perl libcairomm-1.0-1 libcamel1.2-14 libcanberra-gnome libcanberra-gtk-module libcanberra-gtk0 libcanberra0 libcdio-cdda0 libcdio-paranoia0 libcdio7 libcompizconfig0 libcryptui0 libdecoration0 libdeskbar-tracker libdmx1 libdv4 libebackend1.2-0 libebook1.2-9 libecal1.2-7 libedata-book1.2-2 libedata-cal1.2-6 libedataserver1.2-11 libedataserverui1.2-8 libeel2-2 libeel2-data libegroupwise1.2-13 libespeak1 libexchange-storage1.2-3 libexempi3 libflickrnet2.1.5-cil libfreezethaw-perl libgadu3 libgail-gnome-module libgamin0 libgconf2-4 libgconf2.0-cil libgdata-google1.2-1 libgdata1.2-1 libgdiplus libgegl-0.0-0 libggz2 libggzcore9 libggzmod4 libgimp2.0 libgksu2-0 libglade2-0 libglade2.0-cil libglew1.5 libglib-perl libglib2.0-cil libglibmm-2.4-1c2a libglitz-glx1 libglitz1 libgmime-2.0-2a libgmime2.2-cil libgnome-desktop-2-7 libgnome-keyring0 libgnome-keyring1.0-cil libgnome-mag2 libgnome-media0 libgnome-menu2 libgnome-pilot2 libgnome-speech7 libgnome-vfs2.0-cil libgnome-window-settings1 libgnome2-0 libgnome2-canvas-perl libgnome2-common libgnome2-perl libgnome2-vfs-perl libgnome2.0-cil libgnomecanvas2-0 libgnomecanvas2-common libgnomecups1.0-1 libgnomekbd-common libgnomekbd3 libgnomekbdui3 libgnomeprint2.2-0 libgnomeprint2.2-data libgnomeprintui2.2-0 libgnomeprintui2.2-common libgnomeui-0 libgnomeui-common libgnomevfs2-0 libgnomevfs2-bin libgnomevfs2-common libgnomevfs2-extra libgp11-0 libgpod3 libgtk-vnc-1.0-0 libgtk2-perl libgtk2.0-cil libgtkglext1 libgtkhtml-editor-common libgtkhtml-editor0 libgtkhtml2-0 libgtkhtml3.14-19 libgtkmm-2.4-1c2a libgtksourceview-common libgtksourceview1.0-0 libgtksourceview2.0-0 libgtksourceview2.0-common libgtkspell0 libgtop2-7 libgtop2-common libgucharmap7 libgvfscommon0 libgweather-common libgweather1 libhesiod0 libidl0 libiec61883-0 libjpeg-progs libkpathsea4 liblaunchpad-integration1 liblircclient0 liblpint-bonobo0 libmbca0 libmeanwhile1 libmetacity0 libmldbm-perl libmono-addins-gui0.2-cil libmono-addins0.2-cil libmono-cairo1.0-cil libmono-cairo2.0-cil libmono-corlib1.0-cil libmono-corlib2.0-cil libmono-data-tds1.0-cil libmono-data-tds2.0-cil libmono-i18n1.0-cil libmono-i18n2.0-cil libmono-security1.0-cil libmono-security2.0-cil libmono-sharpzip0.84-cil libmono-sharpzip2.84-cil libmono-sqlite2.0-cil libmono-system-data1.0-cil libmono-system-data2.0-cil libmono-system-web1.0-cil libmono-system-web2.0-cil libmono-system1.0-cil libmono-system2.0-cil libmono0 libmono1.0-cil libmono2.0-cil libnautilus-burn4 libnautilus-extension1 libndesk-dbus-glib1.0-cil libndesk-dbus1.0-cil libnet-dbus-perl libnotify1 liboil0.3 liboobs-1-4 libopal-2.2 libopenobex1 liborbit2 libpam-gnome-keyring libpanel-applet2-0 libpangomm-1.4-1 libpisock9 libpisync1 libpolkit-gnome0 libpoppler-glib3 libportaudio0 libpt-1.10.10 libpt-1.10.10-plugins-alsa libpt-1.10.10-plugins-v4l libpt-1.10.10-plugins-v4l2 libpulse-browse0 libpulsecore5 libpurple-bin libpurple0 librarian0 librsvg2-common libschroedinger-1.0-0 libscim8c2a libsexy2 libshout3 libsilc-1.1-2 libsndfile1 libsoup2.4-1 libspeexdsp1 libsqlite0 libstartup-notification0 libtie-ixhash-perl libtotem-plparser12 libtracker-gtk0 libtrackerclient0 libuuid-perl libv4l-0 libvte-common libvte9 libwnck-common libwnck22 libwv-1.2-3 libx11-xcb1 libxevie1 libxml-twig-perl libxml-xpath-perl libxml2-utils libxres1 libzephyr3 metacity metacity-common mobile-broadband-provider-info mono-common mono-gac mono-jit mono-runtime mousetweaks mtools nautilus nautilus-cd-burner nautilus-data nautilus-sendto nautilus-share network-manager-gnome notification-daemon obex-data-server onboard pidgin pidgin-data pidgin-otr pkg-config policykit-gnome pulseaudio pulseaudio-esound-compat pulseaudio-module-gconf pulseaudio-module-hal pulseaudio-module-x11 pulseaudio-utils python-beagle python-brlapi python-cairo python-gconf python-gdata python-glade2 python-gmenu python-gnome2 python-gnome2-desktop python-gnomecanvas python-gst0.10 python-gtk2 python-gtkhtml2 python-gtksourceview2 python-launchpad-integration python-notify python-numeric python-pkg-resources python-pyatspi python-pyorbit python-rdflib python-sexy python-virtkey python-vte rarian-compat rhythmbox rss-glx scim scim-bridge-agent scim-bridge-client-gtk scim-gtk2-immodule scim-modules-socket screen-resolution-extra screensaver-default-images seahorse seahorse-plugins sgml-data software-properties-gtk sqlite sqlite3 ssh-askpass-gnome synaptic syslinux system-config-printer-gnome system-tools-backends tangerine-icon-theme tomboy totem totem-common totem-gstreamer totem-mozilla totem-plugins tracker tracker-search-tool tracker-utils transmission-common transmission-gtk tsclient ubufox ubuntu-artwork ubuntu-desktop ubuntu-docs ubuntu-gdm-themes ubuntu-sounds ubuntu-system-service ubuntu-wallpapers untex update-manager update-notifier usb-creator vinagre vino whois wv xbase-clients xbitmaps xdg-user-dirs-gtk xsane xsane-common xscreensaver xscreensaver-data xscreensaver-gl xsltproc xterm xulrunner-1.9 xulrunner-1.9-gnome-support yelp zenity
$ sudo apt-get remove ubuntu-desktop

Removing the GUI and switching to console mode

Now we have to modify grub config. Open /etc/default/grub with vim (install vim if you haven’t yet -> sudo apt-get install vim)




Also comment GRUB_HIDDEN_TIMEOUT=0 This line is for un-hiding the GRUB menu
Now we will upgrade GRUB configuration

$ sudo update-grub

Ubuntu 12.04 Desktop edition use lightdm for GUI. We need to disable the same

$ sudo update-rc.d -f lightdm remove

Now restart your machine.

Lets start hardening Ubuntu

Install Bastille Linux. The Bastille Linux project aims to provide an interactive tool for the purpose of performing additional security hardening measures to increase the over-all security, and decrease the susceptibility of compromise for your Ubuntu system. This guide is designed to assist in the installation, and execution of the Bastille Linux tool for the purpose of hardening the security of your Ubuntu system.

$ sudo apt-get install bastille

Then run

$ bastille -c

Bastille will do major hardening. Still there are some steps we need to follow in-order to harden it more.

Step 1:  A regular user should not have write access to system files. Worst case you can run a

find -user OTHERSUSERNAME -writable

to search the system for any potentially writable files, but that shouldn’t be an issue.
Step 2: Removing read access from /proc and also remove execution permission from various suid set system binaries like ping, passwd, ifconfig, nmap, scp, who, finger, ssh etc if you want people to remain semi-anonymous. Its best to do a scan of SUID files across the whole system and disable as many as possible, or remove execute/read permission for non root users.

$ find / -perm -4000 -o -perm -2000 -print

Step 3: chmod these files to to 0700 if you are allowing ssh access to several users


Step 4: Enable sticky bit (chmod +t) for preventing directory listing in /home/var/www/public_html and all other highly sensible data directories.

/root 0700 
/boot 0700
/home 1711
/etc 0711
/tmp 1777 or 1733
/var/www/public_html 1755
/var/www/public_html/files 0644
/media and /mnt 0700

Step 5: Disable Compilers in-order not to compile Kernel or other exploits on the server

chmod 750 /usr/bin/gcc
chmod 750 /usr/bin/*++*
chmod 750 /usr/bin/*cc*
chmod 750 /usr/bin/as
chmod 750 /usr/bin/mysqlaccess

Securing SSH

Open /etc/ssh/sshd_config

  • Disable ‘password based login‘ and only permit ‘public key authentication‘.
  • Disable Root login ‘PermitRootLogin yes‘ -> ‘PermitRootLogin no
  • Reduce the ‘LoginGraceTime‘ to 120
  • Disable ‘X11Forwarding‘, ‘X11Forwarding yes‘ -> ‘X11Forwarding no

Root Notification

Edit .bashrc under /root to get notified by email when someone logs in as root and add the following:

$ echo 'ALERT - Root Shell Access (Server Name) on:' `date` `who` | mail-s "Alert: Root Access from `who | cut -d"(" -f2 | cut -d")" -f1`" YOUR_EMAIL_ID

Choosing a secure password

Ubuntu uses per default sha512 hashed passwords. sha512 is a 128bit hash and nowadays not too secure anymore. There is support for an alternate hash, blowfish. This has proven quite secure in openbsd.
To enable it, you have to install libpam-unix2.

$ sudo apt-get install libpam-unix2

Next replace all references of with in all files under /etc/pam.d
Note: The number of references in the files depends on installed pam modules.
In common-password add the following:

password required nullok use_authok obscure min=9 max=72     blowfish

To enable password checks when changing passwords, install libpam-passwdqc and add the following to your /etc/pam.d/common-password:

$ sudo apt-get install libpam-passwdqc

edit /etc/pam.d/common-password and add

password required max=72 similar=deny enforce=everyone retry=2 ask_oldauthtok=update check_oldauthtok

Note: To finalize the changes, all passwords have to be reentered!!
If you restart at this point, you will be unable to log in again!
Set a new password with the passwd command ( can be the same as before, but it has to be reset to use the new hash!)

Problems: For those of you in trouble after a reboot:
you can boot to your system without a password, if you append a init=/bin/bash to your grub kernel parameter.
To enter that, type “e” at the grup prompt and you can edit the entry.

IMPORTANT: This does only secure Ubuntu passwords! You still can log in with things like knoppix or other methods like described above!
For a fully secured system you have to encrypt hard drives!

There is a quite simple way to test or to change to the blowfish hashing without changing all old passwords. Simply add above the old lines the following lines

in /etc/pam.d/common-account

account sufficient
account required


auth sufficient nullok
auth required nullok_secure


session sufficient
session required

As the authentication with the modules ist sufficient the blowfish hashed passwords work, but if something fail, the old ( module will do their job.

Checking for Rootkits

Install chkrootkits

$ sudo apt-get install chkrootkits

You can run it with the following command: ./chkrootkit
Add it to crontab to schedule daily automatic scans in the system:
vim /etc/cron.daily/

./chkrootkit | mail -s "Daily chkrootkit from Server Name"
chmod 755 /etc/cron.daily/

Hardening Kernel (/etc/sysctl.conf)

sysctl is an interface that allows you to make changes to a running Linux kernel. With /etc/sysctl.conf you can configure various Linux networking and system settings such as:

  • Limit network-transmitted configuration for IPv4
  • Limit network-transmitted configuration for IPv6
  • Turn on execshield protection
  • Prevent against the common ‘syn flood attack’
  • Turn on source IP address verification
  • Prevents a cracker from using a spoofing attack against the IP address of the server.
  • Logs several types of suspicious packets, such as spoofed packets, source-routed packets, and redirects.

Sample /etc/sysctl.conf

Edit /etc/sysctl.conf and update it as follows. The file is documented with comments. However, I recommend reading the official Linux kernel sysctl tuning help file (see below):

# The following is suitable for dedicated web server, mail, ftp server etc. 
# ---------------------------------------
# BOOLEAN Values:
# a) 0 (zero) - disabled / no / false
# b) Non zero - enabled / yes / true
# --------------------------------------
# Controls IP packet forwarding
net.ipv4.ip_forward = 0

# Controls source route verification
net.ipv4.conf.default.rp_filter = 1

# Do not accept source routing
net.ipv4.conf.default.accept_source_route = 0

# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0

# Controls whether core dumps will append the PID to the core filename
# Useful for debugging multi-threaded applications
kernel.core_uses_pid = 1

# Controls the use of TCP syncookies
#net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 2

########## IPv4 networking start ##############
# Send redirects, if router, but this is just server
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Accept packets with SRR option? No
net.ipv4.conf.all.accept_source_route = 0

# Accept Redirects? No, this is not router
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0

# Log packets with impossible addresses to kernel log? yes
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.secure_redirects = 0

# Ignore all ICMP ECHO and TIMESTAMP requests sent to it via broadcast/multicast
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Prevent against the common 'syn flood attack'
net.ipv4.tcp_syncookies = 1

# Enable source validation by reversed path, as specified in RFC1812
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

#Enable ExecShield protection
kernel.exec-shield = 1
kernel.randomize_va_space = 1

# TCP and memory optimization 
# increase TCP max buffer size setable using setsockopt()
#net.ipv4.tcp_rmem = 4096 87380 8388608
#net.ipv4.tcp_wmem = 4096 87380 8388608

# increase Linux auto tuning TCP buffer limits
#net.core.rmem_max = 8388608
#net.core.wmem_max = 8388608
#net.core.netdev_max_backlog = 5000
#net.ipv4.tcp_window_scaling = 1

# increase system file descriptor limit    
fs.file-max = 65535

#Allow for more PIDs 
kernel.pid_max = 65536

#Increase system IP port limits
net.ipv4.ip_local_port_range = 2000 65000

Apache Hardening

Edit /etc/apache2/apache.conf
Turn off these variables:

TraceEnable off

Edit /etc/php5/fpm/php.ini


disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,


disable_functions = exec, passthru, shell_exec, system, proc_open, popen, curl_exec, curl_multi_exec, parse_ini_file, show_source

The Apache web server is a crucial part of the website infrastructure. It has a number of built in features that can improve your website resistance to attacks

Do not allow browsing outside the document root

Allowing browsing outside the document root is inviting trouble. Unless you have a specific need to allow it, disable this feature. First, you’ll need to edit the document root Directory entry like so (/etc/apache2/http.conf)

ServerName localhost
ServerSignature Off
ServerTokens Prod
<Directory />
Order Deny, Allow
Deny from all
Options None
AllowOverride None

Now, if you need to add options to any directory within the document root, you will have to add a new Directory entry for each one.

Disable Apache Signature and/or Apache Banner

Apache Signature or Apache Banner is basically the same thing. It is an application name together with version name that is printed when performing a web request. Nobody actually needs this information at all, but it is enabled by default. You need to alter the Apache configuration file to disable it.
Add these lines to this file /etc/apache2/apache2.conf

ServerSignature Off
ServerTokens ProductOnly

The Trace HTTP Request

HTTP TRACE request is used to echo back all received information. It can be tricked to print HTTP cookies and as a result steal HTTP session. Basically this request can be used as part of the Cross Site Scripting attack, or XSS. It is recommended to disable it as a security precaution.

Add the following to the web-server’s configuration file /etc/apache2/apache2.conf

TraceEnable off

Disable directory indexing

You can fix this problem by disabling the Apache autoindex module. In some Apache installations it is called In Ubuntu, you just need to remove the following files:


So you can do it running the following commands:

rm -f /etc/apache2/mods-enabled/autoindex.load
rm -f /etc/apache2/mods-enabled/autoindex.conf

Disable WebDAV

WebDAV is a file access protocol created over HTTP protocol. It allows you to upload and download files, and change file contents from the website. This service is required only in very rare cases.
You can fix this problem by disabling Apache dav, dav_fs and dav_lock modules. In Ubuntu you just need to remove the following files:


So you can do it running the following commands:

rm -f /etc/apache2/mods-enabled/dav.load
rm -f /etc/apache2/mods-enabled/dav_fs.conf
rm -f /etc/apache2/mods-enabled/dav_fs.load
rm -f /etc/apache2/mods-enabled/dav_lock.load

Create a chroot’ed Apache environment

Chroot is a kind of virtual environment supported operating systems such as Linux and FreeBSD. When an application is executed in chrooted environment it has no access to the parent disk and to other resources. You can follow this link to create a chroot’ed Apache environment

Additional Steps

If your webserver runs together with MySQL server it brings additional potential security problem. MySQL can read any files located on you server including the one located in different chrooted environments. It happens because of the FILE permission. By default only MySQL root has it.


Resetting the Root Password

We’ve to killall the demon process of mysql i.e mysqld

$ sudo /etc/init.d/mysql stop

Restart mysqld with --skip-grant-tables and --skip-network, to connect without a password and with all privileges and also to prevent remote clients from connecting.

$ sudo mysqld --skip-grant-tables --skip-networking &

Now connect to MySQL client

$ mysql

Reset the root account password using this command:

mysql> UPDATE mysql.user SET Password=PASSWORD('YOUR_NEW_ROOT_PASSWORD') WHERE User='root';

exit mysql and restart the mysql service

$ sudo service mysql restart

Now you can login as a root user in MySQL client.

Securing MySQL

mysql> USE mysql;

Get rid of the test database, which is accessible to anyone from anywhere

msyql> select * from db where Db like 'test%';
msyql> drop database test;
mysql> delete from db where Db like 'test%';

Remove the anonymous users

msyql> show grants for ''@'localhost';
msyql> revoke on all *.* from ''@'localhost';
mysql> delete from user where User='' and Host='localhost'; 
mysql> flush privileges;

Lower system privileges
To protect your database, make sure that the file directory in which the MySQL database is actually stored is owned by the user “mysql” and the group “mysql”.

ls -l /var/lib/mysql

In addition, ensure that only the user “mysql” and “root” have access to the directory /var/lib/mysql.
The mysql binaries, which reside under the /usr/bin/ directory, should be owned by “root” or the specific system “mysql” user. Other users should not have write access to these files.

ls -l /usr/bin/my*

Remove Anonymous and obsolete accounts
To check whether this is the case,

mysql> select * from mysql.user where user="";

If it returns any user(s), immediately drop that user.


Disable or restrict remote access
Edit /etc/mysql/my.cnf file and add these lines to disable remote MySQL login and listen only to the localhost


Lower DB privileges
To disable the usage of the “SHOW DATABASES” command, the following parameter should be added in the [mysqld] section of the /etc/mysql/my.cnf


Enable Logging
It is recommended that you enable transaction logging, by adding the following line to [mysqld] section of the /etc/msyql/my.cnf file

log =/var/log/mylogfile

Remove History
We should remove the content of the MySQL history file (~/.mysql_history), where all executed SQL commands are stored (especially passwords, which are stored as plain text):

$ cat /dev/null > ~/.mysql_history

Automatically updating the machine

$ sudo apt-get install unattended-upgrades
$ sudo dpkg-reconfigure unattended-upgrades

NOTE: This may not contain all the security measures. I would like to request the reader to add comments about the security measures which are not mentioned in this post. Your suggestions and feedbacks are always welcomed. Thanks in advance 🙂