Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seiji/2716465 to your computer and use it in GitHub Desktop.
Save seiji/2716465 to your computer and use it in GitHub Desktop.
Make me a new CentOS linode

Make me a new CentOS linode

Notes:

  • Change, me, myhost, myip etc. to your username, hostname, ip address and so on.
  • Run all commands as root unless otherwise directed.
  • You might want to look at mounting /var and /home on separate partitions.
  • I've just allowed all members of the wheel group to operate as root. This is the height of laziness and highlights the fact that I'm just a developer that's stolen a sysadmin's woolly hat. It works fine, but the phrase "can do better" springs to mind.

Set the hostname

echo "HOSTNAME=myhost" >> /etc/sysconfig/network
hostname "myhost"
echo "myip myFQDN myhost" >> /etc/hosts

Note: add a reverse dns entry too if you plan to run a mail server.

Set the time zone

ln -sf /usr/share/zoneinfo/UTC /etc/localtime
vi /etc/sysconfig/clock    

You may want to choose GB to enable daylight saving time.

Add a new admin user

Add the user

useradd -m -G wheel me
passwd me

Edit sudoers

vim /etc/sudoers

Uncomment the line that allow users in the wheel group go mad:

## Allows people in group wheel to run all commands
%wheel	ALL=(ALL)	ALL

Test ssh and sudo access

Logout, and then try to login again, but as the new user:

ssh me@myip
sudo -i
exit

Assuming it all works, be kind to your fingers and add your pubkey to authorized keys:

mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "your public key here" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Be even kinder to yourself, and add something to the ssh config on your own machine:

vim ~/.ssh/config

...

Host myhost
HostName myip
User me

Update sshd config to disable root login and add some other restrictions

vim /etc/ssh/sshd_config

...

PermitRootLogin no
LoginGraceTime 30    
MaxAuthTries 3
MaxStartups 3:50:10

Restart the ssh daemon after making changes:

service sshd restart

Note RE running sshd on a non-standard port - you can do this, and I normally do this, but as we'll be using fail2ban or denyhosts to prevent brute-force attacks, doing so is really just going to help you reduce the number of alerts RE brute-force attacks and keep your logs clean, definitely worth doing, but not strictly necessary.

Basic firewall configuration

Run the following commands as root to create a basic set of firewall rules that will allow traffic on standard ports for http, https, smtp ftp etc., block anything else, and drop portscans. Note: I think I originally just took these from the following linode wiki page, which might be worth a look as it has some nice comments explaining why the rules are added: http://www.linode.com/wiki/index.php/CentOS_IPTables_sh.

First, flush any existing rules (shouldn't be any, but just in case):

iptables -F

Then create the rules:

iptables -A INPUT -i lo -j ACCEPT 
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 
iptables -A INPUT -s 10.0.0.0/255.0.0.0 -j DROP 
iptables -A INPUT -s 169.254.0.0/255.255.0.0 -j DROP 
iptables -A INPUT -s 172.16.0.0/255.240.0.0 -j DROP 
iptables -A INPUT -s 127.0.0.0/255.0.0.0 -j DROP 
iptables -A INPUT -s 224.0.0.0/240.0.0.0 -j DROP 
iptables -A INPUT -d 224.0.0.0/240.0.0.0 -j DROP 
iptables -A INPUT -s 240.0.0.0/248.0.0.0 -j DROP 
iptables -A INPUT -d 240.0.0.0/248.0.0.0 -j DROP 
iptables -A INPUT -s 0.0.0.0/255.0.0.0 -j DROP 
iptables -A INPUT -d 0.0.0.0/255.0.0.0 -j DROP 
iptables -A INPUT -d 239.255.255.0/255.255.255.0 -j DROP 
iptables -A INPUT -d 255.255.255.255 -j DROP 
iptables -A INPUT -p icmp -m icmp --icmp-type 17 -j DROP 
iptables -A INPUT -p icmp -m icmp --icmp-type 13 -j DROP 
iptables -A INPUT -p icmp -m icmp --icmp-type any -m limit --limit 1/sec -j ACCEPT 
iptables -A INPUT -m state --state INVALID -j DROP 
iptables -A INPUT -p tcp -m tcp --tcp-flags RST RST -m limit --limit 2/sec --limit-burst 2 -j ACCEPT 
iptables -A INPUT -m recent --rcheck --seconds 86400 --name portscan --rsource -j DROP 
iptables -A INPUT -m recent --remove --name portscan --rsource 
iptables -A INPUT -p tcp -m tcp --dport 139 -m recent --set --name portscan --rsource -j LOG --log-prefix "Portscan:" 
iptables -A INPUT -p tcp -m tcp --dport 139 -m recent --set --name portscan --rsource -j DROP
iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT 
iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable 
iptables -A FORWARD -m state --state INVALID -j DROP 
iptables -A FORWARD -m recent --rcheck --seconds 86400 --name portscan --rsource -j DROP 
iptables -A FORWARD -m recent --remove --name portscan --rsource 
iptables -A FORWARD -p tcp -m tcp --dport 139 -m recent --set --name portscan --rsource -j LOG --log-prefix "Portscan:" 
iptables -A FORWARD -p tcp -m tcp --dport 139 -m recent --set --name portscan --rsource -j DROP 
iptables -A FORWARD -j REJECT --reject-with icmp-port-unreachable 
iptables -A OUTPUT -m state --state INVALID -j DROP
iptables -A OUTPUT -o lo -j ACCEPT 
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 
iptables -A OUTPUT -p tcp -m tcp --dport 21 -j ACCEPT 
iptables -A OUTPUT -p tcp -m tcp --dport 25 -j ACCEPT 
iptables -A OUTPUT -p udp -m udp --dport 43 -j ACCEPT 
iptables -A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp -m tcp --dport 53 -j ACCEPT 
iptables -A OUTPUT -p udp -m udp --dport 67 -j ACCEPT 
iptables -A OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT 
iptables -A OUTPUT -p tcp -m tcp --dport 110 -j ACCEPT
iptables -A OUTPUT -p udp -m udp --dport 123 -j ACCEPT
iptables -A OUTPUT -p tcp -m tcp --dport 143 -j ACCEPT 
iptables -A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT 
iptables -A OUTPUT -p tcp -m tcp --dport 587 -j ACCEPT
iptables -A OUTPUT -p tcp -m tcp --dport 993 -j ACCEPT 
iptables -A OUTPUT -p tcp -m tcp --dport 995 -j ACCEPT 
iptables -A OUTPUT -p tcp -m tcp --dport 9418 -j ACCEPT
iptables -A OUTPUT -p tcp -m tcp --dport 22 -j ACCEPT 
iptables -A OUTPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT 
iptables -A OUTPUT -j REJECT --reject-with icmp-port-unreachable

Now, check it worked:

iptables -L

And open another terminal and try to ssh to the server.

Save the configuration, so it works after a restart of iptables:

service iptables save

And sanity check the saved file (can't be too cautious):

cat /etc/sysconfig/iptables

Restart iptables and again check that the rules have been applied:

service iptables restart
iptables -L

Note: you may see a failure message when you restart iptables, referring to a "security" table which doesn't exist. This is fine for our purposes, you can ignore it. It's a known issue caused by a security table being defined in the latest linux kernel, which iptables doesn't know about. Google to read more if you're interested.

Finally, make sure iptables starts on boot:

chkconfig iptables on

Apply all security updates

yum update

Some basic improvements to apache security

Note: this is far from exhaustive and just addresses a handful of well-documented issues. Don't assume this means your apache web server is "secure", even if you follow the optional steps to install and configure mod_security. You want "secure", hire an expert with indemnity insurance.

Disable TRACE and OPTIONS HTTP methods by adding the following to the end of the httpd.conf file:

vim /etc/httpd/conf/httpd.conf

...

TraceEnable off
<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{REQUEST_METHOD} ^OPTIONS
  RewriteRule .* - [F]
</IfModule>

Disable mod_status, mod_info, all proxy modules, all dav modules, all ldap modules and anything else you know you don't need by simply commenting them out.

Restrict the information apache provides with http responses by setting the ServerTokens directive to Product.

ServerTokens Prod

Deny access to files outside /var/www by default. Update the existing Directory directive for the root path, and add a new one for /var/www:

<Directory />
  Order deny,allow
  Deny from all
  Options None
  AllowOverride None
</Directory>

<Directory /var/www>
  Order allow,deny
  Allow from all
  Options FollowSymLinks
  AllowOverride None
</Directory>

Finally, disable SSLv2 and weak encryption ciphers:

vim /etc/httpd/conf.d/ssl.conf

...

SSLProtocol -ALL +SSLv3 +TLSv1

SSLCipherSuite ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM

Check the configuration, then start apache and configure it to start on boot:

apachectl configtest
service httpd start
chkconfig httpd on

Install and configure mod_security (optional, but a good idea)

Ok, so mod_security can cause problems when it rejects things that look suspicious but are perfectly normal within the realm of your web app. However, it really isn't that difficult to tweak the configuration and get things working if you do run into trouble, so you should at least give it a go. Install it via yum:

yum install mod_security

Reload the apache configuration and it should be working:

service httpd reload

Check if you need to fix a bug in the core rule set (I did, with mod_security v2.5.12). See http://comments.gmane.org/gmane.comp.apache.mod-security.user/7262 for the modification that needs to be made to file /etc/httpd/modsecurity.d/base_rules/modsecurity_crs_30_http_policy.conf

Then, check if you need to fix another bug, this time with the default data directory for mod_security. The default is the the apache log directory, which mod_security may not have permisson to write to (check /var/log/httpd/error_log for confirmation).

vim /etc/httpd/conf.d/mod_security.conf

...

SecDataDir /tmp

See https://bugzilla.redhat.com/show_bug.cgi?id=732450 and https://bugzilla.redhat.com/show_bug.cgi?id=569360 for more info.

For rails applications, I found myself removing a few rules that were causing problems, but which I also knew weren't necessary because of the built in XSS and CSRF protection in rails, and because I set PassengerFriendlyErrorPages off. The simplest solution was just to use the DirectoryMatch directive:

vim /etc/httpd/conf/httpd.conf

...

<DirectoryMatch /var/www/[^/]+/current/public>
  # Reomve some mod_security rules that are causing problems for 
  # rails apps, and are known to be unncessary for rails apps.
  SecRuleRemoveById 90008 # self-executing JavaScript functions
  SecRuleRemoveById 900030 # common XSS concatenation patterns
  SecRuleRemoveById 970901 # response status 5xx
</DirectoryMatch>

If you find yourself running into seemingly unsurmountable problems, you can use the SecRuleEngine directive to disable the rule engine per virtual host. Just update your vhost config and add "SecRuleEngine Off". You could alternatively disable the rule engine by default by editing /etc/httpd/conf.d/mod_security.conf

Install and configure either fail2ban or denyhosts to prevent brute-force attacks

You will need to add the EPEL repo to install fail2ban or denyhosts with yum

rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-5.noarch.rpm

fail2ban

yum install fail2ban

Whitelist at least one remote IP address which will never be banned:

vim /etc/fail2ban/jail.conf

...

ignoreip = 127.0.0.1 officeip homeip etc

Start the daemon

service fail2ban start

And configure it to start on boot

chkconfig fail2ban on

denyhosts

yum install denyhosts

Whitelist at least one remote IP address which will never be banned:

vim /var/lib/denyhosts/allowed-hosts

...

# We mustn't block localhost
127.0.0.1

# my office
officeip

# my house
homeip

Start the daemon

service denyhosts start

And configure it to start on boot

chkconfig denyhosts on

Test the configuration

Test that it works by then creating a number of failed ssh logon attempts. After 5 or so logon attempts (depends on syslog buffering), the server should start to reject tcp connections and the ssh client should appear to hang while it waits to report a connection timeout.

ssh foo@myip <- just keep repeating this until the connection is rejected

Tune some kernel parameters

Edit /etc/sysctl.conf and make sure the following parameters are set. If the entries don't exist, add them:

vim /etc/sysctl.conf

...

# Reboot after a kernel panic
kernel.panic = 10

# Enable TCP SYN Cookie Protection
net.ipv4.tcp_syncookies = 1

# Disable IP Source Routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

# Disable ICMP Redirect Acceptance
net.ipv4.conf.all.accept_redirects = 0

# Enable IP Spoofing Protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Don't allow outsiders to alter the routing tables
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0

# Don't pass traffic between networks or act as a router
net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Ignore ICMP Requests
net.ipv4.icmp_echo_ignore_all = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Enable Bad Error Message Protection
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Disable IPv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

Disable IPv6 networking

Most guides to disabling IPv6 tell you to disable the module. Don't do this. Disabling the module can cause problems for other things that depend on it. You've already disabled IPv6 in the kernel, that's sufficient. You can test this for yourself by trying ping6 localhost.

With IPv6 disabled in the kernel, you should update your network configuration to ignore it:

vim /etc/sysconfig/network

...

NETWORKING_IPV6=no

and confirm that the ip6tables daemon is not running and won't run on boot:

service ip6tables stop
chkconfig ip6tables off

If you plan to run postfix to send mail, you'll need to update the config file and tell it not to bother with IPv6:

vim /etc/postfix/main.cf

...

# Enable IPv4, and IPv6 if supported
inet_protocols = ipv4

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment