The Elliquiy LAMP Stack: General Security

Started by Vekseid, March 23, 2009, 05:54:25 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Vekseid

The Elliquiy LAMP Stack

1: Introduction and Overview
2: General Configuration
3: General Security
4: IPTables configuration
5: Postfix configuration
6: ntp configuration
7: Apache compilation and configuration
8: MySQL compilation and configuration
9: PHP compilation and configuration
10: Conclusion and future plans




General Security

This document covers the 'general' security configuration and setup for Elliquiy. IPTables has its own section, and SELinux will have two or three as well after I am fully comfortable with it. Security considerations for specific programs - Postfix, NTP, MySQL, Apache, PHP and eventually Nginx go into their own sections.

It is important to remember that security is a process and a mode of thinking. It is not enough to assume that a server is secure just because you followed a checklist. Is your own system secure? What about political, legal, or financial issues? How much do you rely on DNS and in what ways? Even if this setup is considered 'hardened', it does little good if the applications can be compromised.

If you are using Windows, I highly recommend getting PuTTY and placing Pageant in your startup. I enter the passphrase for my keys once per bootup, and I'm good to go, no further worries about passwords save for when I su to root. : ) Likewise, for Linux or BSD you use ssh-agent. The power of ssh-agent and pageant is in simplifying better security - you only need a few keys (generally) to control a lot of machines, to say nothing of accounts. Your own system security is, of course, important, but that is outside the scope of this documentation.

Creating a public-private key pair is fairly simple. In Windows, just use puttygen to make a pair - save each. You don't specifically need a passphrase, but it makes it more secure and keeping the fingerwork simple is what pageant is for. In *nix, ssh-keygen -t rsa -b 4096.

RSA versus DSA

According to FIPS-186-2, DSA keys must be 1024 bits long, and security is dependent on the subgroup size, which by spec is 160 bits and provides 80 bits worth of security. This latter number is what the ssh protocol currently uses, regardless of generated bit size. The FIPS-186-3 draft specifies 2048/224 and 3072/256 keysizes, for 112 and 128 bits of security respectively, but for now at least, generating larger keysizes for use with ssh provides no additional security.

Since 1024 bit keys are not recommended beyond 2010 and I'm writing this in 2009, you should use RSA keys. A 4,096 bit key should last until quantum computing forces us to use something different entirely.



SSH

The first order of business when securing a machine is securing root and ssh.

I delete the administrative user my host added and create my own - adduser someadminname. I give it access to the adm, src, and staff groups and go to town. I add my public key to ~/.ssh/authorized_keys for the user (actually I have it in /etc/skel/.ssh/authorized_keys >_>).

Next I add wheel and nosu groups addgroup --gid 70 wheel, addgroup -gid 71 nosu, add my admin and root to the wheel group and everyone capable of logging in to nosu. I also edit /etc/pam.d/su to uncomment the auth       required   pam_wheel.so group=wheel and auth       required   pam_wheel.so deny group=nosu lines. This essentially forces only my 'secret' administrative user to be allowed to su to root, and only my admin and root can su at all. So, in theory at least, in order to remotely grab the root account on my server, a person needs 1: My rsa key, 2: My passphrase to that key, 3: The name of the admin account, and 4: The password to root. Rubber hose and black bag cryptanalysis are of course concerns, but are outside the scope of this document, though despite the seeming amusement I may actually address them to certain degrees in future posts if there is interest.

Make sure you can sign into your admin account with the key and su to root with it before modifying sshd_config.

In /etc/ssh/sshd_config:
1) Set Port to a non-standard port to help free yourself of botnet troubles. They may be futile, but the logspam is exceedingly annoying.
2) Set ListenAddress to listen only to your primary (or otherwise chosen) IP address. If you have five ips it may not be to your benefit to accept traffic on the obvious one.
3) Set PermitRootLogin to no
4) Set PasswordAuthentication to no

/etc/ssh and everything within it can be safely hidden from the outside world - chmod -R o-rwx /etc/ssh/

If feeling paranoid, restart ssh after each step and make sure you can still sign in properly. Revert changes that don't work. We will then be done with ssh, at least specifically.



Hosts


# /etc/hosts.deny
ALL EXCEPT in.smtpd, sshd, httpd: ALL



# /etc/hosts.allow
ALL: localhost, 127.0.0.1


I am going to have four ports open here - 25, 80, 443, and my chosen sshd port. I might add pop3 later but I've grown to like the simplicity of alpine >_>. Note that hosts.deny/allow only actually apply to tcp connections for programs that use the tcpwrapper library. Apache is not one of these, but I leave it in there just in case I decide to dump Apache for something else that uses tcpwrappers later. If you are going to add pop3 for just yourself, you may wish to actually specify your current ip address in hosts.allow instead of letting anyone try to get your mail.

MySQL uses the wrappers by default. Naturally, if you are running a specific MySQL server, you will want to permit access appropriately.



Users

As I mentioned, I add my public key to /etc/skel/.ssh/authorized_keys - this makes handling things a lot simpler. Whether or not you want to actually do this will depend at least in part on how many other people are going to be accessing the server. For me, there are potentially two people I may let touch it - I can modify it for the accounts I give them access to as appropriate : )

I set umask 027 in /root/.bashrc and my admin's ~/.bashrc. It's also important to add umask 022 to /etc/skel/.bashrc - since we will be using a fastcgi setup and we want people to be able to read the files that our php scripts create (usually). chmod o-r home prevents people from easily spying other usernames.

It is important to set limits appropriately. My /etc/security/limits.conf looks like so, except sekret is not my admin name.


# /etc/security/limits.conf
#<domain>      <type>  <item>         <value>
#

*               -       core            0
*               -       nproc           300
*               -       nofile          16384
mysql           -       memlock         -
mysql           -       nofile          16384
mysql           -       sigpending      -
mysql           -       nproc           -
root            -       memlock         -
root            -       nproc           -
root            -       sigpending      -
root            -       nofile          16384
sekret          -       memlock         -
sekret          -       nproc           -
sekret          -       sigpending      -
sekret          -       nofile          16384


core files are prevented to keep them from littering all over the place, especially while we toy with things - they can provide details about the system that we do not want others to know. Playing with and optimizing php configurations can rapidly generate core files if this is not set. If something is breaking, we can manually arrange for core files to be created and inspect them that way.

If you are running mpm_prefork, you may want to permit apache ('www-data') to use more than 300 processes, depending on your configuration. memlock is for enabling large-pages support in mysql, likewise. It's important to give root and your admin account access as well (or at least add the appropriate ulimits in /root/.bashrc) because finding out days later that MySQL reverted to standard memory access because you restarted mysqld from root is annoying.



Syslog

I hate the standard syslog.conf file. Mine follows.


#  /etc/syslog.conf     Configuration file for syslogd.

# Log by service

auth,authpriv.*                 /var/log/auth.log
cron.*                          -/var/log/cron.log
daemon.*                        -/var/log/daemon.log
kern.*;kern.!=debug;kern.!=info -/var/log/kern.log
#ftp.*                          -/var/log/ftp.log
#lpr.*                          -/var/log/lpr.log
#mail.*                         -/var/log/mail.log
#news.*                         -/var/log/news.log
#uucp.*                         -/var/log/uucp.log
syslog.*                        -/var/log/sys.log
user.*                          -/var/log/user.log
kern.=debug                     -/var/log/iptables.log
kern.=info                      -/var/log/hackers.log
local0.=debug                   -/var/log/elliquiy-suhosin.log
local0.=info                    -/var/log/bluemoon-suhosin.log
local0.=notice                  -/var/log/ewrimo-suhosin.log
local0.=warning                 -/var/log/personal-suhosin.log
local0.err                      -/var/log/suhosin.log
#local1.*                        -/var/log/local1.log
#local2.*                        -/var/log/local2.log
#local3.*                        -/var/log/local3.log
#local4.*                        -/var/log/local4.log
#local5.*                        -/var/log/local5.log
#local6.*                        -/var/log/local6.log
#local7.*                       -/var/log/local7.log

# Log by severity

*.err;local0.none               /var/log/error.log
*.=warn;local0.none             -/var/log/warning.log

# Split up mail logs appropriately.

mail.=notice;mail.=debug        -/var/log/mail.notice
mail.=info                      -/var/log/mail.info
mail.warn                       -/var/log/mail.warn

# Emergencies are sent to everybody logged in.

*.emerg                         *


Fairly straightforward segregation. I have two target logs generated by iptables - one is a standard script, the other triggers when someone is very likely up to no good. I'll let you guess which is which >_> Note that since this is the kernel log, other messages will show up in these logs as well. Ideally there would be an option to force iptables to use its own facility.

Suhosin is set to the local0 facility by the master php.ini file. Local php.ini files are used to pick the exact level - Elliquiy gets one, Blue Moon, Ewrimo, and PrP. All others are crowded into .err.

Since we generate - and receive - a significant amount of e-mail traffic, mail gets split up explicitly.

syslog.conf does not need to be world-readable - it's safe to do chmod o-rx for it.



SetUID/SetGID

I run find / -perm -4000 to get a list of setuid binaries. Since this is a server I'm running remotely, I can be pretty liberal in terms of what doesn't get suid:


chmod 755 /bin/mount
chmod 755 /bin/umount
chmod 755 /bin/ping
chmod 755 /bin/ping6
chmod 755 /usr/bin/chfn
chmod 755 /usr/bin/chsh
chmod 755 /usr/bin/gpasswd
chmod 755 /usr/bin/newgrp
chmod 755 /usr/bin/mtr
chmod 755 /usr/lib/openssh/ssh-keysign
chmod 755 /usr/lib/eject/dmcrypt-get-device


Running find / -user root -perm -4000 (note adding -user root) now returns:

/usr/lib/apache2/suexec
/usr/lib/pt_chown
/usr/bin/sudoedit
/usr/bin/passwd
/usr/bin/sudo
/bin/su


You may want to run find / -group root -perm -2000 to see if anything is setgid root besides directories. Directories a la /var/cache/man are fine, this just causes subdirectories to inherit the parent group.

If you are using nginx and skipping cronolog, you don't need apache or sudo and can thus eliminate them. pt_chown, su and passwd are required, however.



TTYs

gettys 1-6 each take up RAM. We don't like that - if someone has to work with our maching, they only need one to start and if they really need to, they can undo the following edit to add more.

In /etc/inittab, comment out additional gettys:

#2:23:respawn:/sbin/getty 38400 tty2
#3:23:respawn:/sbin/getty 38400 tty3
#4:23:respawn:/sbin/getty 38400 tty4
#5:23:respawn:/sbin/getty 38400 tty5
#6:23:respawn:/sbin/getty 38400 tty6


In /etc/securetty I comment out everything but console and tty1-12. I leave 2-12 uncommented in the off chance that it becomes more convenient for whomever has to get hands-on to just uncomment the lines in inittab.



/etc/sysctl.conf

The title says it all, doesn't it? : )

Frozentux has an excellent overview of iptables net variables.


# /etc/sysctl.conf - Configuration file for setting system variables

# Uncomment the next two lines to enable Spoof protection (reverse-path filter)
# Turn on Source Address Verification in all interfaces to prevent some spoofing attacks
# This is on, since we are not a router.
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1

# Ignore ICMP broadcasts. There are few legitimate uses for broadcast echos, so if you have such a reason you probably already know to disable this when you need to anyway.
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Ignore bogus ICMP errors. As a counterpoint to 'log everything', we want to log as little of it as possible. If that makes any sense.
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Do not accept ICMP redirects. As with icmp broadcasts, we don't want to allow this sort of craftiness unless we really do mean it.
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Accept ICMP redirects only for gateways listed in our default gateway list (enabled by default)
net.ipv4.conf.all.secure_redirects = 1

# Do not send ICMP redirects (we are not a router)
net.ipv4.conf.all.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

# Log Martian Packets
net.ipv4.conf.all.log_martians = 1

# Maximum number of open files permited. I leave this commented out for now as I do not particularly need it.
#fs.file-max = 262144

# Likewise I leave overcommit_memory at the default. If you do set it to 2, be sure that you have a decent-sized swap enabled, else allocation will fail as you run low on memory.
#vm.overcommit_memory = 2
#vm.overcommit_ratio = 50

# shmall is set to shmmax - shmall is in pages, which are 4kb for this system.
# vm.nr_hugepages is set for MySQL's large-pages support, and needs at least as
# much shared memory allocatable (hugepagesize * nr_hugepages) in order to work.
kernel.shmmax=1073741824
kernel.shmall=524288
vm.nr_hugepages = 256


You can set nr_hugepages live, however, be sure to increase the number of hugepages slowly on an active system, as it takes time to free up contiguous multi-megabyte blocks of RAM.



Program Pruning

It's a good idea to get rid of junk you don't need, so to speak. I specifically focus on server processes here, but libraries and other potentially unsupported programs can also be risks if a way is found to load them.

1) DHCP - this is a server, not a home machine.
2) Since this is a single machine, I remove nfs/portmap. Not everyone will want to do this of course.
3) at gets removed because nothing uses it.
4) apcid because this is a server. Power management? Seriously.
5) Some VPS templates also install the bind daemon and/or nscd, neither of which I have a particular use for. I use EveryDNS for name resolution - at least for now - and trust my host's nameservers. They take up a pretty significant amount of resources and we don't have a lot to spare.
6) /etc/inetd.conf has identd enabled by default - which freaked me out the first time I saw it, so I killed it with fire >_>



About SELinux

SELinux really deserves its own discussion piece, or better, a set of them. As a fundamentally different way of handling security, it is not necessarily a good idea just to dive in without understanding it. It's easy to talk about how it works, creating a superior, more comprehensive policy is something I'm still tackling for Elliquiy proper, so I don't want to specifically recommend it just yet.

Still, ignoring the actual handling of policies themselves, a few tweaks are needed, and they are not necessarily bad ones to implement even without SELinux:

1) /etc/default/rcS - set FSCKFIX=yes - this is probably a good idea in general for a remote host where you are using a fully journaled filesystem such as ext3. The Debian wiki also suggests EDITMOTD=no, though the man page says this is depreciated. It causes no harm to add, however.
pb[2)[/b] If you have a bout of insanity and intend to enable strict mode without understanding SELinux a lot better than I currently do, the wiki also instructs to make /etc/motd a genuine file and remove /var/run/motd, as well as commenting the motd editing lines in /etc/init.d/bootmisc.sh.
3) Remove the shadow and gshadow backups from /etc/cron.daily/standard, as cron will not have access to these files.

If you are using tmpfs partitions along with SELinux, you will want to add context="system_u:object_r:tmp_t:s0" to tmpfs mount options.

Again, I hope to revisit SELinux in the future after coming up with a superior and more general policy configuration rather than just recommending people to enable it. Obviously, just diving in with audit2allow is not a wise security policy.

That said, if you enable SELinux and suddenly see avc errors exploding, make sure SELinux did not decide to remove some policies for you before you go crazy with audit2allow. There are a lot of default policies for various packages in /usr/share/selinux/default/


semodule -i /usr/share/selinux/default/apache.pp
semodule -i /usr/share/selinux/default/mysql.pp
semodule -i /usr/share/selinux/default/ntp.pp


For example, or whatever happens to be missing from semodule -l. You will almost certainly have to do some of your own policy generation, however.