This hardening guide, and the operating system (OS) configuration it produces, is in early beta and will change. The baseline is currently version 0.1.2. 18F is currently testing this baseline for deployment into production.
There are many recommended controls that are already in place with given a fresh install of either Ubuntu 12.04 LTS or Ubuntu 14.04 LTS. These controls are fully itemized at TBD and are covered by our an open source compliance testing suite we are currently working on.
Controls were implemented on both an Ubuntu 12.04 box downloaded from the VagrantCloud and the default 640-bit Ubuntu 14.04 Amazon Machine Image (AMI).
There might be additional controls necessary for your system at the OS level, above and beyond the following. Please consult with your cybersecurity and DevOps teams.
We strongly encourage the community to help us improve this baseline given an ever-changing risk environment. We believe there is rarely any security in obscurity and that there will always be a greater level of expertise on the outside of team than inside. By doing this work in the open, we will more quickly and effectively identifiy flaws or potential improvements.
We've edited this guide to address both the Vagrant and Amazon Web Services (AWS) use cases. As we improve the baseline, we will likely seperate the guide based on the deployment environment. For the time being, differences between Vagrant and AWS are noted in-line.
The guide is currently written presuming intermediate familarity with Linux, the command line, Vagrant/AWS, and system administration in general. References are provided were applicable to provide additional background.
These are controls to implement in a production environment. They may not be currently appropriate for a development environment that is in constant change. Some potential workarounds are discussed at the end of the guide. [TBD]
If you are doing this work via Vagrant, before you do your first vagrant up, you might have a version mistmatch between the Guest Additions installed on the box vs your install of VirtualBox. Let's fix that. Go to the same folder as the Vagrantfile you're about to use.
vagrant plugin install vagrant-vbguest
vagrant up
During the upgrade, you'll get a note that the XFree86 Window system failed to install. You won't need it.
If you are doing this work via AWS, start with an Ubuntu 14.04 AMI that uses paravirtualization. Tests have not yet been completed using hardware-assisted virtualization (HVM) based AMIs, but we do not at this point anticipate any issues. Interested in testing? Assign an issue to yourself.
Before we do anything, let's make sure we're all patched up.
apt-get update
apt-get upgrade
Y
Grab a snack, this will take a bit.
References:
When you create your fresh new Ubuntu box via Vagrant or AWS, you only have "sda" (or "xvda") - a physical volume for booting the OS. In Vagrant, there may be a volume dedicated for itself. Don't touch those or your may break your box.
You first need to create a physical disk which will be labeled "sdb" - see the b? This is the second physical disk with "sda" being the first one.
In order to create sdb, you need to add it through your VM manager. If you use VirtualBox, here's a walkthrough.
In the AWS device namespace, it becomes problematic if you occupy the sdb - sde device namespace. Within your instance, you may see these mapped to xvdb - xvde respectively. If you select sdf - sdl when creating your instance, these problems can be avoided. The rest of the partition guidance in this section is written from the perspective of Vagrant. If you're in AWS, when creating an additional EBS device, select sdf - sdl when launching an additional EBS device with your EC2 instance, and then anticipate seeing xvdf - xvdl in the below commands instead of sdb.
You can now can add partitions to sdb. Check to see if it's there.
fdisk -l
In this listing, you should now see:
Disk /dev/sdb
Jump into fdisk to start your partitioning!
fdisk /dev/sdb
Hit m for a list of commands. We'll go with n to start making a new partition. If you can live with 4 partitions of this disk, primary partitions are the way to go.
Command (m for help): n
Partition type:
p primary (0 primary, 0 extended, 4 free)
e extended
Select (default p): p
Partition number (1-4, default 1): 1
On what cylinder will the partition start? Rarely a good idea to set something non-default. Just hit enter to move on.
First sector (2048-52428799, default 2048):
Last sector, +sectors or +size{K,M,G} (2048-52428799, default 52428799):
Determining the last sector also determines how big the partition is. We have 30GB to play with, let's make each 6GB.
Last sector, +sectors or +size{K,M,G} (2048-52428799, default 52428799): +5G
Hit p to see what happened.
Command (m for help): p
You should now see /dev/sdb1 listed!
Rinse and repeat until you have sdb1 -> sdb4 in the dialog listed by the p command. Warning: you have to write this config to disk! Hit w.
Command (m for help): w
This will also exit fdisk. Let's take a last look to confirm our work:
fdisk -l
(...)
Device Boot Start End Blocks Id System
/dev/sdb1 2048 10487807 5242880 83 Linux
/dev/sdb2 10487808 20973567 5242880 83 Linux
/dev/sdb3 20973568 31459327 5242880 83 Linux
/dev/sdb4 31459328 41945087 5242880 83 Linux
Looks great! But all that's happened is that you've created device listing within the OS. Ubuntu still doesn't think these are physical volumes ready for use.
Reference: A Beginner's Guide To LVM
The Logical Volume Management (LVM) app can take these devices and make partitions. The first step is to create physical volumes (PV).
pvcreate /dev/xvdb1 /dev/xvdb2 /dev/xvdb3 /dev/xvdb4
Check your work.
pvdisplay
Now we can create a volume group (VG) to contain our logical volumes (LV). We need to give our VG a name - securefolders.
vgcreate securefolders /dev/xvdb1 /dev/xvdb2 /dev/xvdb3 /dev/xvdb4
If this is confusing, check out this diagram from Wikipedia and this StackExchange article
The order of abstraction is PV > VG > LV.
Make a logical volume - this is the last abstraction, and where we will actually mount our folders. I'll begin with a LV for /tmp, which I'll call temp.
lvcreate --name temp --size 5G securefolders
If you go back and look at vgdisplay at this point, you should see that your Free PE has now dropped.
Abstractions are over, so let's get an actual filesystem going! At the moment, ext4 is the latest and greatest, so we'll use that.
mkfs.ext4 /dev/securefolders/temp
We now have some safety checks before we start mounting. A key configuration file fstab will be altered, so let's make a backup.
cp /etc/fstab /etc/fstab.$(date +%Y-%m-%d)
To be extra careful, let's make a backup of all the files we're going to re-mount. I've heard rsync can preserve permissions better than cp. Go to the top level and make some backupfolders first.
mkdir homeBackup
mkdir varBackup
rsync -aXS /home/* /homeBackup
rsync -aXS /var/* /varBackup
Ok, we finally have a thing we can mount to! Let's tackle /tmp first.
mount /dev/securefolders/temp /tmp
df -H
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/precise64-root 79G 2.2G 73G 3% /
udev 174M 4.0K 174M 1% /dev
tmpfs 74M 308K 73M 1% /run
none 5.0M 0 5.0M 0% /run/lock
none 183M 0 183M 0% /run/shm
/dev/sda1 228M 25M 192M 12% /boot
/vagrant 233G 169G 65G 73% /vagrant
/dev/mapper/securefolders-temp 10G 280M 9.2G 3% /tmp
Let's put some security options on that folder.
mount -o remount,nodev,nosuid,noexec /tmp
But what happens if we reboot? We don't want to deal with creating a shell script for this. This should be in the baseline. There's a file called fstab that will cover it for us.
vi /etc/fstab
Go to the end and add the following.
/dev/securefolders/temp /tmp ext4 defaults,rw,nodev,nosuid,noexec 0 2
Nice - after a reboot, everything will mount correctly with your new security options.
With all the concepts in the clear let's bundle things up for the other assets that will be in securefolders VG.
lvcreate --name variables --size 5G securefolders
lvcreate --name audits --size 5G securefolders
lvcreate --name house --size 5G securefolders
mkfs.ext4 /dev/securefolders/variables
mkfs.ext4 /dev/securefolders/audits
mkfs.ext4 /dev/securefolders/house
Let's break things up here. First, get /var mounted.
mount /dev/securefolders/variables /var
Pausing here, we want to bind mount /var/tmp to /tmp. Beyond inheriting previous security modifications of /tmp, this keeps things tidy.
Since we still have the new OS smell, /var/tmp may not exist yet. Same with /var/log/audit. We'll make them both and do necessary binding.
cd var
mkdir /var/tmp
mount --bind /tmp /var/tmp
mkdir log
cd log
mkdir audit
Check your binding.
mount | grep -e "^/tmp" | grep /var/tmp
Just like everything else, we need to modify our /etc/fstab to have this persist beyond a reboot.
We'll add the following:
/tmp /var/tmp none bind 0 0
Finish up the mounting.
mount /dev/securefolders/audits /var/log/audit
mount /dev/securefolders/house /home
Look under the hood.
df -H
(...)
/dev/mapper/securefolders-temp 5.4G 214M 4.9G 5% /tmp
/dev/mapper/securefolders-house 5.4G 214M 4.9G 5% /home
/dev/mapper/securefolders-variables 5.4G 682M 4.5G 14% /var
/dev/mapper/securefolders-audits 5.4G 214M 4.9G 5% /var/log/audit
One more secure modification in this area - see /run/shm above? That powers part of the temporary filesystem. Let's lock it down.
mount -o remount,noexec,nosuid,nodev /run/shm
One more edit to /etc/fstab to bake it in. Add:
none /run/shm tmpfs defaults,nodev,noexec,nosuid 0 0
References: Need to find some
Every installed application creates an potential attack surface. A hardened OS reduces this surface to the absolute minimum for system functionality.
One of the places we can reduce the attack surface is in a configuration file stored in /etc/modprobe.d
cd /etc/modprobe.d
touch 18Fhardened.conf
vi 18Fhardened.conf
Add:
#Applications
install cramfs /bin/true
install freevxfs /bin/true
install jffs2 /bin/true
install hfs /bin/true
install hfsplus /bin/true
install squashfs /bin/true
install udf /bin/true
# Protocols
install dccp /bin/true
install sctp /bin/true
install rds /bin/true
install tipc /bin/true
References: Ubuntu and GRUB
Boot loaders need to be protected. Anyone who is not root doesn't need to write to this file. Likely everything is already in order, but just to be sure:
chmod og-wx /boot/grub/grub.cfg
We don't want anyone messing with booting, so let's create a password.
grub-mkpasswd-pbkdf2
Be sure to keep this password somewhere safe. You'll also get an encrypted version of the password. Hang on to it as we need to jump in to configuration settings again.
vim /etc/grub.d/40_custom
Add:
set superusers="INSERT USER HERE"
password_pbkdf2 INSERT USER HERE <encrypted-password>
EOF
Sub out with the actual value you just got, otherwise the next command will fail. This is the value that starts with grub.pbkdf2.sha512
Save the file and then use:
update-grub
Unless this OS is powering a router, we can harden how it handles ICMP redirects.
/etc/sysctl.conf controls these settings.
vim /etc/sysctl.conf
You'll find all the controls already here, but commented out. You'll want to remove the # for the following, or add these lines if they're not there:
# Spoof protection
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
# Do not accept ICMP redirects (prevent MITM attacks)
net.ipv4.conf.all.accept_redirects=0
net.ipv6.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv6.conf.default.accept_redirects=0
net.ipv4.conf.all.secure_redirects=0
net.ipv4.conf.default.secure_redirects=0
# Do not send ICMP redirects (we are not a router)
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.send_redirects=0
# Do not accept IP source route packets (we are not a router)
net.ipv4.conf.all.accept_source_route=0
net.ipv6.conf.all.accept_source_route=0
net.ipv4.conf.default.accept_source_route=0
net.ipv6.conf.default.accept_source_route=0
# Log packets from Mars
net.ipv4.conf.all.log_martians=1
net.ipv4.conf.default.log_martians=1
Update your kernel parameters to match - for each of these lines enter:
/sbin/sysctl -w [INSERT LINE HERE W/ VALUE]
Then, flush!
/sbin/sysctl -w net.ipv4.route.flush=1
/sbin/sysctl -w net.ipv6.route.flush=1
Reference: Notes about auditing configuration
Audit strategy is highly environment and application specific. In the near future, we will post some overall best practices here, liley including a more standardized configuration for /etc/audit/auditd.conf
auditd is great, but it can't audit processes that run before it starts - or can it?
vi /etc/default/grub
Then modify the following line to read:
GRUB_CMDLINE_LINUX="audit=1"
run:
update-grub
By default, you won't capture audit events when a user changes system date and time, or when users change user accts or passwords. This ain't no TARDIS and you're no Doctor. Let's capture those events by modifying /etc/audit/audit.rules.
vi /etc/audit/audit.rules
Add:
Date/time:
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change
-a always,exit -F arch=b32 -S adjtimex -S settimeofday -S stime -k time-change
-a always,exit -F arch=b64 -S clock_settime -k time-change
-a always,exit -F arch=b32 -S clock_settime -k time-change
-w /etc/localtime -p wa -k time-change
User/passwords:
-w /etc/group -p wa -k identity
-w /etc/passwd -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/security/opasswd -p wa -k identity
Network stuff:
-a exit,always -F arch=b64 -S sethostname -S setdomainname -k system-locale
-a exit,always -F arch=b32 -S sethostname -S setdomainname -k system-locale
-w /etc/issue -p wa -k system-locale
-w /etc/issue.net -p wa -k system-locale
-w /etc/hosts -p wa -k system-locale
-w /etc/network -p wa -k system-locale
SELinux (likely you're using AppArmor instead, but just in case you pull SELinux packages from Debian, best to have this already listed)
-w /etc/selinux/ -p wa -k MAC-policy
Login and logout:
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins
-w /var/log/tallylog -p wa -k logins
Permission modifications:
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
Unauthorized access:
-a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k access
-a always,exit -F arch=b32 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k access
-a always,exit -F arch=b64 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k access
-a always,exit -F arch=b32 -S creat -S open -S openat -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k access
Collect filesystem mounts:
-a always,exit -F arch=b32 -S mount -F auid>=500 -F auid!=4294967295 -k mounts
-a always,exit -F arch=b64 -S mount -F auid>=500 -F auid!=4294967295 -k mounts
File deletion:
-a always,exit -F arch=b64 -S unlink -S unlinkat -S rename -S renameat -F auid>=500 -F auid!=4294967295 -k delete
-a always,exit -F arch=b32 -S unlink -S unlinkat -S rename -S renameat -F auid>=500 -F auid!=4294967295 -k delete
Change to sysadmin scope:
-w /etc/sudoers -p wa -k scope
Kernel loading:
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-a always,exit arch=b64 -S init_module -S delete_module -k modules
Make audit config immutable
-e 2
Only root should modify what system jobs cron runs.
chown root:root /etc/crontab
chmod og-rwx /etc/crontab
chown root:root /etc/cron.hourly
chmod og-rwx /etc/cron.hourly
chown root:root /etc/cron.daily
chmod og-rwx /etc/cron.daily
chown root:root /etc/cron.weekly
chmod og-rwx /etc/cron.weekly
chown root:root /etc/cron.monthly
chmod og-rwx /etc/cron.monthly
chown root:root /etc/cron.d
chmod og-rwx /etc/cron.d
It's easier to manage a whitelist then a blacklist. Make the following additional modifications.
[As an aside, this guidance is a little off IMHO. If neither *.deny or *.allow files exist, access is automatically restricted to root. In the case of cron, this is how Ubuntu 14L is right out of the box.]
/bin/rm /etc/at.deny
touch /etc/cron.allow
touch /etc/at.allow
Modify both files so they have "root" listed. Then:
chmod og-rwx /etc/cron.allow
chmod og-rwx /etc/at.allow
chown root:root /etc/cron.allow
chown root:root /etc/at.allow
Script-kiddies and crackers should find no safe harbor here. Let's prevent common cracking by improving our password polcies.
Install a helpful module:
apt-get install libpam-cracklib
This module will automatically add a line to /etc/pam.d/common-password with some default settings. It will read:
password requisite pam_cracklib.so retry=3 minlen=8 difok=3
Our current recommended settings are more stringent. Let's also remove difok - it's ok if characters from old passswords repeat as long as there is significant entropy.
password requisite pam_cracklib.so retry=3 minlen=24 dcredit=-2 ucredit=-2 ocredit=-2 lcredit=-2
We can prevent password reuse by adding remember=5 to the next line:
password [success=1 default=ignore] pam_unix.so obscure use_authtok try_first_pass sha512 remember=5
Lockouts /etc/pam.d/login
auth required pam_tally2.so onerr=fail audit silent deny=5 unlock_time=900
Password expiration using /etc/login.defs
PASS_MAX_DAYS 90
PASS_MIN_DAYS 7
PASS_WARN_AGE 10
Modify a file at...
/etc/ssh/sshd_config
X11Forwarding no
MaxAuthTries 4
PermitRootLogin no
PermitEmptyPasswords no
PermitUserEnvironment no
Ciphers aes128-ctr,aes192-ctr,aes256-ctr
ClientAliveInterval 600
ClientAliveCountMax 0
Lock the file permissions down:
chown root:root /etc/ssh/sshd_config
chmod 600 /etc/ssh/sshd_config
Add one then correct permissions
chmod 644 /etc/motd
chmod 644 /etc/issue
chmod 644 /etc/issue.net
Modified config files
/etc/default/grub
/etc/audit/audit.rules
/etc/sysctl.conf
/etc/pam.d/common-password
/etc/pam.d/login
/etc/ssh/sshd_config
/etc/login.defs
Questions? Email [email protected].