Tag Archives: hardening

OpenSSL on CentOS

How to install latest version of OpenSSL on CentOS?

Hi there, today I would like to show you how to install latest version of OpenSSL (1.1.0g) on CentOS 7

Do I need latest version of OpenSSL?

In general - you don't. Default version is doing great job and it's secure. I needed it for compiling Apache HTTP with HTTP/2 support. Default version of OpenSSL installed on CentOS (1.0.1e) does not support it yet, hence the need of using latest version instead.

If you need it for any other reason, this tutorial is for you:)

How to check current version of OpenSSL?

In order to check current version of installed package you need to execute following command:

openssl version

It will print out version of installed package like OpenSSL 1.0.1e-fips 11 Feb 2013

How to install latest version of OpenSSL?

I compile OpenSSL from source code. In order to compile it successfully you need to install some tools that will help you compile it:

sudo yum install libtool perl-core zlib-devel -y

It will install compiler and few other libraries that are required to compile OpenSSL.

Next download latest version of OpenSSL source code. I like to use releases page on GitHub. I choose the version without FIPS simply because I don't need compatibility with it. And I think that it's a bit more secure to have OpenSSL without FIPS, as fixes are usually included much faster in regular version than in FIPS version. If you want to read more about it, use this link.

In order to download source code, use following command:

curl -O -L https://github.com/openssl/openssl/archive/OpenSSL_1_1_0g.tar.gz

Source code comes in compressed package. In order to decompress it use following command:

tar -zxvf OpenSSL_1_1_0g.tar.gz
cd openssl-OpenSSL_1_1_0g

Now it's time to configure and compile OpenSSL:

./config --prefix=/usr/local/openssl --openssldir=/usr/local/openssl shared zlib
make
make test

prefix and openssldir sets the output paths for OpenSSL. shared will force crating shared libraries and zlib means that compression will be performed by using zlib library

It is worth to run the tests to see if there are any unexpected errors. If there are any, you need to fix them before installing library.

In order to install library you need to execute:

sudo make install

I usually delete all source files to keep system clean after installation. However sources of OpenSSL are required to compile other tools such us Apache, Nginx etc., so I don't remove them.

Add new version to PATH

After the installation you will probably want to check the version of OpenSSL but it will print out old version. Why? Because it's also installed on your server. I rarely override packages installed via yum. The reason is that when there is new version of OpenSSL and you will install it via yum, it will simply override compiled version, and you will have to recompile it again.

Instead of overriding files I personally like to create new profile entry and force the system to use compiled version of OpenSSL.

In order to do that, create following file:

sudo vi /etc/profile.d/openssl.sh

and paste there following content:

# /etc/profile.d/openssl.sh
pathmunge /usr/local/openssl/bin

Save the file and reload your shell, for instance log out and log in again. Then you can check the version of your OpenSSL client. Or maybe...

Link libraries

Or maybe you will get an error with loading shared libraries? In order to fix that problem we need to create an entry in ldconfig.

Create following file:

sudo vi /etc/ld.so.conf.d/openssl-1.1.0g.conf

And paste there following contents:

# /etc/ld.so/conf.d/openssl-1.1.0g.conf
/usr/local/openssl/lib

We simply told the dynamic linker to include new libraries. After creating the file you need to reload linker by using following command:

sudo ldconfig -v

And volia! Check the version of your OpenSSL now. It should print out OpenSSL 1.1.0g 2 Nov 2017

How to configure SSH on CentOS

How to configure secure SSH on CentOS

Hello everyone! In this tutorial I will show you how to increase server security by tuning up configuration of SSH.

Before you begin

There are basically two requirements for this tutorial:

  1. You need to have working SSH keys. You need to be able to login to your server by using them. After completing this tutorial, SSH keys will be the only way to access server. If you won't be able to login by using them, well, you will lose access to your server. In order to add user and configure keys you can follow this tutorial.
  2. Make sure that at least one of the users is in wheel group (has access to sudo). Root should not have access to login via SSH. So if you will block this option and you won't have any sudo user, you won't be able to do much on the server. Follow this tutorial in order to configure sudo.

Disable password authentication for SSH on CentOS

Login to Your server/Vagrant Box and open SSH daemon configuration file:

sudo vi /etc/ssh/sshd_config

Now we need to find the line for password authentication and change it to:

PasswordAuthentication no

Unfortunately, disabling this option can still lead to password authentication by using PAM-based authentication. In order to fully disable authentication with password, make sure that PAM is also disabled:

ChallengeResponseAuthentication no

Also we need to make sure that this line is uncommented. It will  enable SSH login by using public key:

PubkeyAuthentication yes

Save the file and exit from the editor. In order to apply changes, you need to restart SSH daemon:

service sshd restart

After that, try to open new SSH session in new window. Do not logout from your current session! If you won't be able to login with new session, you can undo the changes with existing session. If you will be able to successfully login, you can proceed.

How to secure SSH on CentOS even more?

There are still some things that will help you improve SSH security. Edit the same configuration file as before. Below You will find the configuration options that I usually use for SSH.

Disable root login

PermitRootLogin no

This option will disable root login via ssh. So it means that from now on you won't be able to login to your server as root via ssh.

Allow only specific users to be able to login via SSH

AllowUsers developer

By default you are able to login as any user that is created inside the system. It can be easily limited to particular users. Just give space separated list after AllowUsers. It might not be present in your config, so you need to add this line (for instance at the end of the file).

AllowUsers developer vagrant

Enable protocol 2 for ssh

Protocol 2

This option is set by default in most CentOS installation, but just make sure that there's no version 1 instead. It's less secure protocol.

Ignore rhost

IgnoreRhosts yes

It will disable insecure access via RSH.

Disable login for users with empty passwords

PermitEmptyPasswords no

This line will disable login for users that have empty passwords. Make sure that your account has password set, before changing that!

Enable strict mode for ssh

StrictModes yes

SSH will check users's permission in their home directory before accepting login. It should be set to yes because users may leave their directory or files world-writable. Again, this might be tricky. It's the best to change that, restart SSHD daemon and try to login from new session. If you have any problems, you can undo this change with existing session. If you have any issues with that, try to set valid permissions for your .ssh directory and files inside. Also set valid username and group for your files:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/*
chown -R YOUR_USERNAME:YOUR_USERNAME ~/.ssh

Disable other authentication methods

GSSAPIAuthentication no
KerberosAuthentication no

If you don't plan to login with GSS API, or Kerberos you can disable them as well.

Disable X11 Forwarding

X11Forwarding no

If you don't use X11 you can safely disable it as well.

Show last login

PrintLastLog yes

Nice feature is to show last successful login after you will login via SSH.

Restart SSH daemon

Remember that after any changes inside the file You need to restart sshd daemon:

sudo service sshd restart

SSH crypto

In addition to changes above that should be applied, you can increase SSH security even more by configuring ciphers and available algorithms (thanks to @Amar for the suggestion:)

This is usually safe to execute, but you must remember that not all algorithms are supported by various tool. Here you can find great chart showing, which tools support given algorithms. But let's be honest, most of you is probably using OpenSSH which supports all the changes I will present here. However if you are using different tool and you won't be able to login to your server, check with the page and enable additional algorithms.

These config options will probably not be listed in your config file. You need to just add them somewhere, like at the end of the file.

Configure server authentication

HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key

Server must confirm the identity to the client. There are bunch of algorithms available, but this is the list of most secure.

This might be present in your configuration file, also there might be more not commented lines with HostKeys. Leave only these two enabled and comment out the rest.

Configure key exchange

KexAlgorithms curve25519-sha256@libssh.org

There are many more key exchange algorithms, but this is probably the most secure.

Configure ciphers

Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com

Ciphers are used to encrypt the data. As with key exchange, there are multiple algorithms. These are the safest.

MACs - Message Authentication Codes

MACs hmac-ripemd160,hmac-ripemd160-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128@openssh.com,umac-128-etm@openssh.com

MACs are used for data integrity. Again, line above contains the safest algorithms only.

After these changes, don't forget to restart sshd daemon.

Easier way?

You can use our Ansible LAMP on Steroids project to make configuration of your server easier!

If you don't know what Ansible is, check our tutorial first.

Clone our repository and setup your server faster with LAMP on steroids.

CentOS, users, groups, sudo and SSH keys

Create sudo users and groups in CentOS

I will show you how to organise groups and users in CentOS.

As an example we will create account for user named developer. The purpose of this account is:

  • logging via SSH instead of using root account
  • access to sudo command for management tasks
  • write access to website files
  • read access to logs

Create list with available groups

If you are on fresh system it's handy to create a list with all available groups. It's also handy to check which users belongs to that groups. It might be helpful after some time when you need to decide if given group was at the beginning or can it be safely removed. getent command can help you with that.

getent group > /etc/initial-group-list
cat /etc/initial-group-list

Remove group from the system

If you need to remove group from CentOS simply use following command:

sudo groupdel NAME_OF_THE_GROUP_TO_DELETE

Create new group

In order to create group you need to use groupadd command:

sudo groupadd NAME_OF_THE_NEW_GROUP

I usually add group named www (or www-data, whatever works for you). To this group I add php daemons, nginx workers etc. It makes life easier with writing to files. In order to create such group execute following command:

sudo groupadd www

Create list with available users

Same like with group, I like to have list of initial users. In order to create such list you can use getent too:

getent passwd > /etc/initial-users-list
cat /etc/initial-users-list

Delete user from CentOS

In case you would like to remove any user from the system, use following command:

sudo userdel -r USERNAME_TO_REMOVE

-r flag will remove also his home directory. If you wish to delete the user, but to keep his files, omit this flag.

Create new user in CentOS

Let's create new user developer that we mentioned at the beginning:

sudo adduser developer

and create the password for his account:

sudo passwd developer

If you want to add developer user to www group created before use usermod command:

sudo usermod -g www developer

If you want to add this user sudo powers (and you should if you want to use this user instead of root), add it to wheel group. wheel group is special group in CentOS configured in sudoers file. Whoever belongs to this group can have sudo powers.

sudo usermod -g wheel developer

Optional parameters to useradd command

There are lot of additional parameters for useradd command but there are two especially useful.

First one is helpful when you don't want to create user home directory. It means that user will not have it's own place under /home directory to store it's files. This option is helpful when you are creating user for system service like Apache httpd for instance. So in order to create user with no home directory use --no-create-home:

sudo useradd httpd --no-create-home

Another useful feature is to specify shell of given user. It's nice if you want to cut of possibility to login to the system via SSH for instance. Add --shell /sbin/nologin to disable login for given user, like so:

sudo useradd httpd --shell /sbin/nologin --no-create-home

You can use --shell and --no-create-home parameters separately:)

 

How to setup SSH keys for new created user?

Each user should have RSA key-pair. It makes life easier and you should use it if you want to login to different servers, use GIT etc. In order to create such user key-pair you first need to login to user you created. Most probably you are using root account to execute all commands, but you should never ever login via SSH as a root.

It's much better to create separate user for system management and use only this account. Login via SSH to your server to account your created. In my case it's developer user so my command looks like this:

ssh developer@IP_OF_THE_SERVER_HERE

Once you'll be logged in (after providing the password), you can create RSA key pair. Execute following command:

ssh-keygen -t rsa -b 4096

-t rsa means that it will be RSA key, but this is standard for creating SSH keys. Fun part is with strength of the key -b 4096. By default it's 1024 bits, but to make it harder to break I usually provides 4096. It's not necessary, but you should do that. Some services requires key length to be minimum 2048, but it's better to create even longer one.

Generator will ask you some questions, but you should generally confirm them with enter and leave the defaults. When it comes to SSH on the server, I usually don't set the password. It makes life easier in automated scripts etc.

After that private and public key should be generated as expected. You can find them in ~/.ssh directory.

Add authorized key to user

In order to login with SSH keys to the server, instead of using password you need to add authorized key to developer user. In my opinion it's must have feature as using password login is super risky. Again, been there, done that, I was hacked, even when my password was strong. With SSH logging even strongest bruteforce attack will fail:)

You need to add your key to ~/.ssh/authorized_keys on the server. If You have ssh-copy-id command available just execute:

ssh-copy-id developer@IP_OF_YOUR_SERVER

Make sure that you are executing this command from your computer, not from the server. If you don't have SSH key created locally, you can generate it in the same way as on the server, by using ssh-keygen command.

If uou don't have ssh-copy-id available (for instance from Windows), you can do it manually.

ssh developer@IP_OF_YOUR_SERVER
cd ~/.ssh
vi authorized_keys
//Press "i" to enter in input mode, paste there your code (usually it's right click of the mouse) and :wq (colon, w, q) it will save and quit from vi 
chmod 600 authorized_keys

So here how it goes:

  1. ssh to the server as usual with password.
  2. Change location to .ssh directory.
  3. Create authorized_keys file with vi
  4. Paste there your local public key, save the file and quit
  5. Set permissions on authorized_keys.

Test ssh login with keys

Now You can try to log in with Your key.

ssh developer@IP_OF_YOUR_SERVER -i path/to/your/PRIVATE/key/file

You shouldn't be prompted for your account password!

Easier way?

If you don't want to spend your precious time executing each of these commands by hand, you can use Ansible and our LAMP on steroids project to speed things up!

If you don't know what Ansible is - you can read our tutorial about it here.

LAMP on steroids project is available on GitHub here.

Iptables for CentOS

How to secure server with iptables?

Hi there! In this tutorial I would like to show you how to increase server security by using iptables as a firewall. To be honest, not many people are actually using iptables or any firewall. I think that this is bad practice, because you they allow all traffic to go in and out. You should always limit the possible entry points to your server.

Firewalld vs iptables

Since CentOS 7, we have new tool called firewalld. This is not actually an alternative to iptables. firewalld is a wrapper for iptables. Many people say, that it's easier to use than iptables, but to be honest I believe that it's not flexible enough. Maybe I'm wrong, but I'd love to see some advanced example, how to transform iptables rules below to firewalld 🙂  If you want to use firewalld instead of iptables, unfortunately you need to read different tutorial. Here is great article about firewalld from DigitalOcean.

How to install iptables on CentOS7?

Before we will install iptables, we need to get rid of firewalld first :

sudo yum remove firewalld -y

Next, we can install iptables:

sudo yum install iptables iptables-services -y

iptables-services is simple script that will help us save and restore firewall rules.

Secure iptables rules for CentOS

First, let's check if there are any rules by executing following command:

sudo iptables -S

If you will get following output:

-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

It means that you allow all traffic, both incoming and outgoing to your server. However if you have anything more than output above, copy it to separate file as a backup.

The easiest way of adding rules is by editing iptables rules file. Open the file, or create one if it doesn't exists:

sudo vi /etc/sysconfig/iptables

I will describe whole file line by line, but at the bottom of this post you can find whole content that I'm using for iptables.

Opening and closing tags

*filter

File must contains two indicators:

  • start of the ruleset *filter
  • end of the ruleset COMMIT

You need to have both in order to get iptables configured properly. Between these two lines, you can add iptables rules.

Clear all existing rules

-X
-F
-Z

At the very beginning I'd like to clear whole rules. In other words - enable all traffic. The reason is that I want to be able to execute that file over and over again, and I will always set the rules that I have in file. No other rules will be applied (for instance rules added by command line).

Allowing loopback

-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -j REJECT
-A OUTPUT -d 127.0.0.0/8 -j REJECT

Next thing is to allow all loopbacks. Those are local connection and blocking them might cause errors in some connections. In addition we will block those which doesn't use lo0.

Keep established connections

-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

All connections that are active now, should remain untouched. It will prevent from interruption of services.

PING command

-A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
-A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT

In most cases you will need to be able to ping server. These rules will allow two things. First - you'll be able to ping your own server. Second - you will be able to execute ping from your server. Both are usually needed and quite useful.

Protection from PING of Death attack

-N PING_OF_DEATH
-A PING_OF_DEATH -p icmp --icmp-type echo-request -m hashlimit --hashlimit 1/s --hashlimit-burst 10 --hashlimit-htable-expire 300000 --hashlimit-mode srcip --hashlimit-name t_PING_OF_DEATH -j RETURN
-A PING_OF_DEATH -j DROP
-A INPUT -p icmp --icmp-type echo-request -j PING_OF_DEATH

Ping is cool, however you might get attacked with Ping of Death attack. Here is simple protection.

Prevent some nasty attacks

-N PORTSCAN
-A PORTSCAN -p tcp --tcp-flags ACK,FIN FIN -j DROP
-A PORTSCAN -p tcp --tcp-flags ACK,PSH PSH -j DROP
-A PORTSCAN -p tcp --tcp-flags ACK,URG URG -j DROP
-A PORTSCAN -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
-A PORTSCAN -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
-A PORTSCAN -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL ALL -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL NONE -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP
-A INPUT -f -j DROP
-A INPUT -p tcp ! --syn -m state --state NEW -j DROP

This is really nice piece of rules that will prevent port scanning, SYN flood attacks, invalid packages, malformed XMAS packets, NULL packets, etc.

UDP traffic

-A INPUT -p udp --sport 53 -j ACCEPT
-A OUTPUT -p udp --dport 53 -j ACCEPT
-A INPUT -p udp --sport 123 -j ACCEPT
-A OUTPUT -p udp --dport 123 -j ACCEPT

I enable usually only ports for outgoing traffic (from our server to outside world). There are two ports that I'd like to open:

  • 53 - DNS port. It's a must if you want to use curl or yum. If you will have it closed, you will not resolve any domain name.
  • 123 - NTP port. If you are using chrony or ntpd, you need to enable that port to allow NTP deamon synchronisation.

TCP traffic

# Open TCP ports for incoming traffic
-A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
-A INPUT -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
-A INPUT -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT

# Open TCP ports for outgoing traffic
-A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT

With TCP it's more complicated, but it's not that hard. First, you need to think what traffic you need to access from your server (outgoing traffic). I usually allow only SSH, HTTP and HTTPS traffic. Yum requires HTTP and HTTPS ports for pulling new packages. You will need it also for wget or curl. SSH is not mandatory, but if you want to pull packages from git via ssh protocol, you will need it as well.

I usually enable the same for incoming traffic. If you have httpd or nginx installed, you need to enable port 80. If you are using SSL for HTTPS, you need to enable 443 also. In addition to these two ports you must enable port 22 for SSH. If you will block this, you won't be able to get access to your server!

Block everything else

-A INPUT -j DROP
-A FORWARD -j DROP
-A OUTPUT -j DROP

At the very end, before closing COMMIT tag I add these three rules. So everything that was not specified above will be dropped. Both incoming and outgoing traffic.

How to apply rules?

There are two ways how you can apply the rules. First, save the changes in iptables file. First method is not permanent method. It's good way of testing your firewall before saving them permanently. If anything will go wrong, you can just restart the server and you will have all traffic open. Make sure that you check SSH access with these rules. Log out and try to login after applying rules.

So non permanent way of applying rules is:

sudo iptables-restore < /etc/sysconfig/iptables

Try to check rules with iptables -S to see the difference:) Check if everything is working fine. If so, you can set them permanently. After each server restart, rules will be applied automatically.

sudo systemctl start iptables.service
sudo systemctl enable iptables.service

If you want to reload rules, simply edit the file, add what you need and restart iptables service:

sudo systemctl restart iptables.service

You can use our Ansible LAMP on Steroids project to make configuration of your server easier!

It is based on Ansible. If you don't know what Ansible is, check our tutorial first.

Clone our repository and setup your server faster with LAMP on steroids.

Whole content of iptables rules

*filter

# Clear all iptables rules (everything is open)
-X
-F
-Z

# Allow loopback interface (lo0) and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -d 127.0.0.0/8 -j REJECT
-A OUTPUT -d 127.0.0.0/8 -j REJECT

# Keep all established connections
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# Allow ping
-A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
-A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT

# Protect from ping of death
-N PING_OF_DEATH
-A PING_OF_DEATH -p icmp --icmp-type echo-request -m hashlimit --hashlimit 1/s --hashlimit-burst 10 --hashlimit-htable-expire 300000 --hashlimit-mode srcip --hashlimit-name t_PING_OF_DEATH -j RETURN
-A PING_OF_DEATH -j DROP
-A INPUT -p icmp --icmp-type echo-request -j PING_OF_DEATH

# Prevent port scanning
-N PORTSCAN
-A PORTSCAN -p tcp --tcp-flags ACK,FIN FIN -j DROP
-A PORTSCAN -p tcp --tcp-flags ACK,PSH PSH -j DROP
-A PORTSCAN -p tcp --tcp-flags ACK,URG URG -j DROP
-A PORTSCAN -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
-A PORTSCAN -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
-A PORTSCAN -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL ALL -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL NONE -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -j DROP
-A PORTSCAN -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP

# Drop fragmented packages
-A INPUT -f -j DROP

# SYN packets check
-A INPUT -p tcp ! --syn -m state --state NEW -j DROP

# Open ports for outgoing UDP traffic
-A INPUT -p udp --sport 53 -j ACCEPT
-A OUTPUT -p udp --dport 53 -j ACCEPT
-A INPUT -p udp --sport 123 -j ACCEPT
-A OUTPUT -p udp --dport 123 -j ACCEPT


# Open TCP ports for incoming traffic
-A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
-A INPUT -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
-A INPUT -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT

# Open TCP ports for outgoing traffic
-A INPUT -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
-A INPUT -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT


# Drop all other traffic
-A INPUT -j DROP
-A FORWARD -j DROP
-A OUTPUT -j DROP

COMMIT

What's next?

We secured our system with basic firewall. That will increase the security of our server. In one of the next episodes we will change the configuration of our SSH and therefore make it more secure.

As always You can use our Ansible playbook for faster provisioning of our server. You can find it on GitHub.

Install PHP from source on CentOS

PHP – how to install from source on CentOS

In this part we will cover LAMP part of our webserver - PHP. So we will learn how to install PHP from source. - Why from source?  - You might ask. It's the same reason as with Apache. On most of the systems it's not possible to install latest version of PHP with yum or apt-get. Installation from source is relatively easy, but it might take some time. But if You are following with our series of how to setup webserver, you should have some experience with compiling Apache from source.

LAMP on steroids

This is part of our series LAMP on steroids. Check the links below to learn how to setup awesome webserver!

  1. Choosing VPS
  2. Install EPEL
  3. Install and configure Apache HTTPD server
  4. Harden Apache with ModSecurity and OWASP Core Rule Set
  5. Install and configure PHP
  6. Install and configure MySQL server
  7. Configure firewall based on iptables
  8. Create developer user and setup SSH key-pair
  9. Configure SSH
  10. Install and configure Varnish to speed up websites
  11. More to come...

PHP - install from source - how to?

We will use our Vagrant box of course, with installed Apache and ModSecurity. SSH to the server first if You are not there already and we can begin:)

Prerequirements

First we need to install some libraries that are necessary to install PHP:

yum install bzip2-devel curl-devel libjpeg-devel libpng-devel freetype-devel libc-client-devel.i686 libc-client-devel libmcrypt-devel -y

Download and unpack sources

Go to php.net download website and pick latest version of PHP. In our case it's 5.6.6, but this tutorial should work for any higher version.

cd ~/sources
wget -O php-5.6.6.tar.gz http://pl1.php.net/get/php-5.6.6.tar.gz/from/this/mirror
tar -zxvf php-5.6.6.tar.gz
cd php-5.6.6

Compile PHP from source

Now it's probably the hardest part of compiling PHP. You must provide the ./configure options and choose which modules do You want to install. For lots of needs the commands below will be sufficient, but if You need any particular library I suggest to check the PHP extensions list and find out installation options.

Commands below will enable required and basic extensions like curl, ftp, GD, IMAP, MySQL, PDO, etc. Two important things for this tutorial are --enable-opcache and --enable-fpm. We will use PHP OPCache that comes with newer versions of PHP and will use FPM instead of Apache mod_php.

./configure --enable-bcmath --with-bz2 --enable-calendar --with-curl --enable-exif --enable-ftp --with-gd --with-jpeg-dir --with-png-dir --with-freetype-dir --enable-gd-native-ttf --with-imap --with-imap-ssl --with-kerberos --enable-mbstring --with-mcrypt --with-mhash --with-mysql --with-mysqli --with-openssl --with-pcre-regex --with-pdo-mysql --with-zlib-dir --with-regex --enable-sysvsem --enable-sysvshm --enable-sysvmsg --enable-soap --enable-sockets --with-xmlrpc --enable-zip --with-zlib --enable-inline-optimization --enable-mbregex --enable-opcache --enable-fpm --prefix=/usr/local/php
make
make install

Notice, that it might take lot of time. Much longer than Apache compilation. After make install You should find PHP installed in /usr/local/php directory.

PHP Configuration

PHP-FPM setup

Before we will be able to run PHP from Apache we need to setup PHP-FPM worker. After installation there should be PHP-FPM default configuration file in installation directory. We will alter the file and then change it a bit.

cd /usr/local/php/etc
mkdir fpm.d
cp php-fpm.conf.default php-fpm.conf
vi php-fpm.conf

We need to  uncomment/change these lines:

include=etc/fpm.d/*.conf
pid = /var/run/php-fpm.pid
error_log = log/php-fpm.log

COPY EVERYTHING UNDER Pool Definitions TO CLIPBOARD AND REMOVE IT FROM php-fpm.conf FILE
;;;;;;;;;;;;;;;;;;;;
; Pool Definitions ;
;;;;;;;;;;;;;;;;;;;;

include=/etc/fpm.d/*.conf - by default there is one pool defined inside php-fpm.conf file. The best way to solve it is the same way as we solved Apache vhosts. We will include each pool in separate directory. In php-fpm.conf file one pool is already defined. We need to delete it from this file and put it inside fpm.d directory. We will have better control over the pools. The easiest way is just to Cut it from this file and paste it into new one.

Now let's create the file inside fpm.d for our example.com domain:

cd fpm.d
vi example.com.conf

PASTE TEXT FROM CLIPBOARD HERE AND CHANGE THESE LINES:

[www] -> [example_com] //Must be unique per file
user = apache
group = www
listen = 127.0.0.1:9000 //Port must be unique per file
catch_workers_output = yes
slowlog = /var/www/example.com/logs/php-fpm.slow.log
request_slowlog_timeout = 30s
php_flag[display_errors] = off
php_admin_value[error_log] = /var/www/example.com/logs/php-fpm.error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 64M
php_admin_value[open_basedir] = /var/www/example.com/htdocs

Each pool must have different name. So we need to change it from [www] to something else, for instance to domain name. It'll be easier to find the issues inside log files.

We set user and group to the same user as apache to have access to files.

Port will be different per pool. Standard way is to start from port 9000. Next will be 9001 etc.

We will catch errors and log them to file. In addition we set logging for  slow requests.

Nice part is that we can overwrite the settings from php.ini here. So we can overwrite error_log or memory_limit for instance. We should also set open_basedir so PHP will have access only to files inside our htdocs directory. Our server will be more secure with this setting.

php.ini and OPCache configuration

Second thing is php.ini file. After installation  php.ini file should located in /usr/local/php/lib. This is only the location. After compiling from source You won't anything there so we need to copy it from uncompressed sources.

cd /usr/local/php/lib
cp ~/sources/php-5.6.6/php.ini-development ./php.ini
vi php.ini

This is pretty large file with lot of configuration settings. Fortunately we only need to change some of the options:

short_open_tag = On
open_basedir = /var/www
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
expose_php = Off
max_execution_time = 30
memory_limit = 64M
date.timezone = Europe/Warsaw
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off
display_startup_errors = Off
log_errors = On
post_max_size = 5M
upload_max_filesize = 4M

opcache.enable=1
opcache.memory_consumption=64
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=7000
opcache.validate_timestamps=0 ;set this to 1 on production server
opcache.fast_shutdown=1

So we set few things here, enable <? tag, limit access to files from PHP level, disabled dangerous functions, adjusts timezone, security, max execution times, errors etc. In addition we have enable OPCache for PHP.

Each one of these options are well commented inside php.ini file. If You don't like the settings here or You need something else, feel free to change it for Your purposes.

Useful shell scripts for PHP

/etc/init.d/php-fpm

As You probably remember during Apache setup we create script so we can use service command to start / stop Apache process. Now we will do the same for PHP-FPM

With PHP source code there comes ready script for that purpose.

cd /etc/init.d
cp ~/sources/php-5.6.6/sapi/fpm/init.d.php-fpm php-fpm
vi php-fpm

Now we need to setup configuration for the file:

prefix=/usr/local/php
exec_prefix=${prefix}

php_fpm_BIN=${exec_prefix}/sbin/php-fpm
php_fpm_CONF=${prefix}/etc/php-fpm.conf
php_fpm_PID=/var/run/php-fpm.pid

Save the file and add executable permission.

chmod +x php-fpm
servcie php-fpm status
service php-fpm start
servcie php-fpm status

After that we should have php-fpm process up and running!

Add PHP to $PATH

We can do one more thing to make our life easier:) Add PHP executable to PATH, so we'll be able to call php command from every directory.

echo 'pathmunge /usr/local/php/bin' > /etc/profile.d/php.sh

Execute such command, log out, log in and You'll be able to execute:

php -v

Setup Apache for PHP-FPM

Now is the time to finally setup Apache for .php files. Let's edit one of the Virtual Hosts now.

vi /usr/local/apache2/conf/vhosts/example.com.conf

<VirtualHost *80>
    ServerName example.com

    <LocationMatch "^/(.*\.php(/.*)?)$">
        ProxyPass fcgi://127.0.0.1:9000/var/www/example.com/htdocs/$1
    </LocationMatch>

////Rest of the file below

So basically we need to proxy all files with .php extension to our PHP-FPM process.  Also we need to restart Apache and make sure PHP-FPM is running httpd server:

service php-fpm start
service httpd restart

How to test if PHP is working?

We need to test if our PHP installation works. The easiest way to debug and check what's going on would be to create test.php file inside our /var/www directory.

vi /var/www/example.com/htdocs/test.php

and paste phpinfo() function there:

<?php

phpinfo();

Save the file and open the file in Your browser, assuming that your vagrant setup is correct. For instance http://example.com/test.php or 192.168.99.99/test.php

If everything is OK you should get information about PHP installation. Well done!

What's next

We are one step closer to our LAMP server. The only thing we are missing now is MySQL which we will install in upcoming episodes.

If You are running Ansible for provisioning You can find everything from this series inside my GitHub.

Hardening Apache with Mod Security

Apache hardening with mod_security

In this part of setup complete webserver we will harden Apache with popular mod_security. Mod_security is a small module that works like application firewall. It protect the app before most common attacks and vulnerabilities. It's good to have such thing on the webserver.

I assume that You have Apache already installed, if not check out previous part - How to install Apache on CentOS?

LAMP on steroids

This is part of our series LAMP on steroids. Check the links below to learn how to setup awesome webserver!

  1. Choosing VPS
  2. Install EPEL
  3. Install and configure Apache HTTPD server
  4. Harden Apache with ModSecurity and OWASP Core Rule Set
  5. Install and configure PHP
  6. Install and configure MySQL server
  7. Configure firewall based on iptables
  8. Create developer user and setup SSH key-pair
  9. Configure SSH
  10. Install and configure Varnish to speed up websites
  11. More to come...

How to install mod_security on Apache httpd server

First thing is installation of required tools. We need them to compile mod_security.

yum install automake libtool libxml2-devel

Next thing that we need is to download and decompress mod_security. Download links can be found on official mod_security page.

cd ~/sources
wget https://www.modsecurity.org/tarball/2.9.0/modsecurity-2.9.0.tar.gz
tar -zxvf modsecurity-2.9.0.tar.gz

Now it's time to compile mod_security. While ./confgure we need to pass paths to axps and apr binaries. All binaries should be inside bin directory in apache installation path.

cd modsecurity-2.9.0
./autogen.sh
./configure --with-apxs=/usr/local/apache2/bin/apxs --with-apr=/usr/local/apache2/bin/apr-1-config --with-apu=/usr/local/apache2/bin/apu-1-config
make
make install
cp /usr/local/modsecurity/lib/mod_security2.so /usr/local/apache2/modules

If there was no error mod_security is ready to use. It can be found in /usr/local/modsecurity We need to copy generated .so file to apache extension directory.

ModSecurity and OWASP rules

mod_security is nothing without the rules that tells what attacks should be blocked. Fortunately there is a great package with lot of rules provided by OWASP. We will use such rules package to harden Apache HTTPD. So let's download, unzip and copy the rules to apache configuration directory.

cd ~/sources
wget -O owasp.tar.gz https://github.com/SpiderLabs/owasp-modsecurity-crs/tarball/master
mkdir /usr/local/apache2/conf/crs
tar -zxvf owasp.tar.gz -C /usr/local/apache2/conf/crs --strip 1
cd /usr/local/apache2/conf/crs
cp modsecurity_crs_10_setup.conf.example modsecurity_crs_10_setup.conf

Now we are ready to use ModSecurity!

ModSecurity configuration

In previous article we added httpd-security.conf file with some basic rules that improves Apache security.  We will modify this file to load mod_security with OWASP rules and add some basic configuration. You need to know that mod_security is pretty large module with tons of configuration option. You can find them in ModSecurity reference manual.

vi /usr/local/apache2/conf/extra/httpd-security.conf

Once You open the file add these lines somewhere in the file:

LoadModule security2_module modules/mod_security2.so

<IfModule security2_module>
      Include conf/crs/modsecurity_crs_10_setup.conf
      Include conf/crs/base_rules/*.conf
      # Include conf/crs/experimental_rules/*.conf
      # Include conf/crs/optional_rules/*.conf

      SecRuleEngine On
      SecRequestBodyAccess On
      SecResponseBodyAccess On 
      SecResponseBodyMimeType text/plain text/html text/xml application/octet-stream
      SecDataDir /tmp

      # Debug log
      SecDebugLog /usr/local/apache2/logs/modsec_debug.log
      SecDebugLogLevel 3

      SecAuditEngine RelevantOnly
      SecAuditLogRelevantStatus ^2-5
      SecAuditLogParts ABCIFHZ
      SecAuditLogType Serial
      SecAuditLog /usr/local/apache2/logs/modsec_audit.log
</IfModule>

So from the top:

  • First we need to load mod_security module.
  • Next are rules from OWASP that we will include to ModSecurity. We need to include the setup and base rules. OWASP core rule set comes with lot more features that are marked as optional or experimental. We can enable those rules, but we also need to remember that it might not play well with our website. It's rather testing by trial and error then one rule will work well on every website. But in general including base_rules is OK.
  • SecRuleEngine enables detection and blocking of malicious attacks.
  • SecRequestBodyAccess enable inspection of data transported request  bodies
  • SeResponseBodyAccess buffer response bodies matched by SecResponseBodyMimeType
  • SecDataDir working directory for ModSecurity temporary purposes
  • Next thing is Debug log. By default all error logs goes to apache error log, but we can set different path to debug log. Best practice would be to change it per domain inside particular VirtualHost file. In previous article we setup directory structure and we have logs directory there. It would be wise to used it for debug log as well.
  • Audit Log is complementary log for Debug log. It has detail information about every error. It's disabled by default so we need to enable it and turn on logging relevant (warnings and errors) issues. Next options are for configuration the audit log. In general there are lot more of discussing at this topic.

If You want to learn more about how to setup and read mod_security logs, here is really great article about mod_security logging by Infosec Institue.

Now we just need to save the file restart apache and our httpd server has better security.

service httpd restart

What's next?

If You are following our series, You should have now part of LAMP stack (Linux Apache MySQL PHP). Apache is secured with mod_security.

Small note to those who would like to install mod_evasive as well to increase the security. To be honest, it's really not worth to install it on Apache. Why? Because when You run multiple instances via MPM mod_evasive doesn't share the info between the MPM instances. It means that one instance of apache can block the attacker but others wont. So if You have many MPM workers mod_evasive is just useless.

As always, if You are using Ansible for server provisioning You can use ready playbook, that will cover everything in this series. You can find it on GitHub.

In next episode we will add P to our LAMP server.

How to install apache from source on CentOS

How to install Apache 2.4.23 on CentOS from source

Hi there! Today I'd like to show you how I install and configure Apache httpd on CentOS. I like to have it installed in minimal and secure way. First part is about installation, second about configuration. Last part is about setting up Virtual Hosts.

How to install Apache httpd on CentOS - easy way

There are two ways to install Apache httpd on CentOS. First is with yum and it is the simplest version:

sudo yum install httpd -y

Volia! You have httpd installed. However, if you check the version:

httpd -v

You will most probably get 2.4.6 version or slightly newer. If you check Apache website, you will note, that they have 2.4.23 version available. So, if you want to have access to latest features such as HTTP/2 support or latest bugfixes, you will have to try more difficult method which is installing Apache httpd  from source.

How to install Apache httpd from source on CentOS?

Installing and compiling software from the source code might sound scary. But trust me, it's not. It takes more time to have Apache httpd up and running, than installing it with yum. However it comes with some benefits that I will mention during this tutorial.

Remove old Apache httpd

Before you will even begin, make sure that you don't have Apache httpd installed. In general you don't want to have two version installed on the same machine, unless you are doing some A/B testing or testing versions on different ports etc. But for this tutorial I want to start clean, so I remove installed Apache httpd:

sudo yum remove httpd -y

Compile and install Apache httpd

Required tools for building

You need to install some tools that will help us compile Apache. It's basic stuff like compiler, required libraries etc:

sudo yum install autoconf libtool openssl-devel pcre-devel -y

Download and unpack source code

Next thing that you need are packages with source files. For compiling Apache, you will need 3 different packages - httpd itself, apr and apr-util. Last two are Apache Runtime libraries. They are required for Apache httpd. When you install Apache httpd with yum they come as dependencies.

There are two ways of getting them. Either you can clone them from git repository or you can download them as  tar.gz package. I must say that as I love git, for downloading sources I prefer downloading compressed package. Why? When you clone repository it usually downloads whole history, branches etc. Apache is for instance around 290MB of files. Compressed package is about 10MB. So it is much faster to download just required files instead of cloning whole repo.

I like to download packages from GitHub releases. Here are the links to the packages:

Click on tar.gz icon, copy the link to package and download them with curl or wget. Or simply copy commands below:

curl -O -L https://github.com/apache/httpd/archive/2.4.23.tar.gz
curl -O -L https://github.com/apache/apr/archive/1.5.2.tar.gz
curl -O -L https://github.com/apache/apr-util/archive/1.5.4.tar.gz

Unpack downloaded sources:

tar -zxvf 2.4.23.tar.gz
tar -zxvf 1.5.2.tar.gz
tar -zxvf 1.5.4.tar.gz

APR and APR-Util

Apache requires APR library to be present in the system. You can manually compile and install APR and APR-util first and then do the same with httpd. But I prefer to do it in one shot. First you need to copy the source codes to correct directory:

cp -r apr-1.5.2 httpd-2.4.23/srclib/apr
cp -r apr-util-1.5.4 httpd-2.4.23/srclib/apr-util

It's important to not to include version number in APR directories. If you just copy apr-1.5.2 without changing the name, it will give you a warning about missing apr directory.

Compilation

Now you are ready to compile Apache httpd. It's important that you should not use root user for compilation. It can lead to serious security issues. I described it more on my other tutorial about installing GIT. In short words, imagine that you downloaded package from wrong source with malicious code. If you would compile it as root user, anything can happen to your server. Including cutting of your root access. I'm not saying that it's not possible to compile packages as root, because it is. It's just not safe. If you want to create separate user with sudo powers, you can read this tutorial.

So get inside httpd directory and compile your Apache httpd version:

cd httpd-2.4.23
./buildconf
./configure --enable-ssl --enable-so --with-mpm=event --witn-included-apr --prefix=/usr/local/apache2
make

First command ./buildconf will build ./configure file required for configuration of the build.

./configure command will setup everything for compilation of Apache httpd. Here are the options that I use:

  • --enable-ssl will build Apache with SSL support, so you can enable HTTPS on your websites.
  • --enable-so will enable dynamically loaded modules. So you can enable and disable modules without recompilation (I will describe modules in configuration part)
  • --with-mpm will set multiprocessing modules for Apache. I'm using event, but you can use worker or prefork instead. event works best for me and I think that it is mpm that will give you most performance.
  • --with-included-apr It will use APR library that you copied to srclib directory
  • --prefix is the installation path for Apache httpd compiled package

Whole process might take a while. It depends how fast your server is.

Installation

After it's compiled you can install it. For that you need sudo or root account:

sudo make install

Apache should be installed in the directory you specified with --prefix option.

Cleanup

Last thing you can do now is to remove downloaded files. You won't need them now. It's not mandatory, but it's nice to keep server clean.

rm -rf 1.5.2.tar.gz 1.5.4.tar.gz 2.4.23.tar.gz apr-1.5.2 apr-util-1.5.4 httpd-2.4.23

Set system scripts for Apache httpd

Before I will show you how I configure my Apache httpd server, I want to show you two really helpful scripts.

Add Apache httpd to $PATH

If you try to type httpd -v in your command line, it will result in command not found. That's because httpd is not on your $PATH. I'd like to have all executables from Apache available from everywhere. In order to achieve that, create file

sudo vi /etc/profile.d/httpd.sh

and paste there following contents:

pathmunge /usr/local/apache2/bin

Save the file, log out and log in from your current session to reload your profile. After that you should be able to use httpd -v command:)

SystemD entry

Second really useful script is SystemD entry. It will allow you to start, restart and stop Apache httpd from systemctl. You need to create another file:

sudo vi /etc/systemd/system/httpd.service

and paste there following contents:

[Unit]
Description=The Apache HTTP Server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/apache2/bin/apachectl -k start
ExecReload=/usr/local/apache2/bin/apachectl -k graceful
ExecStop=/usr/local/apache2/bin/apachectl -k graceful-stop
PIDFile=/usr/local/apache2/logs/httpd.pid
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Save the file and reload the systemctl daemon

sudo systemctl daemon-reload

Now you can try to start your Apache httpd server with following command:

sudo systemctl start httpd

It should start properly. If you will have any warnings, don't bother with them now. I will show you proper configuration in next step.

Once it's up and running you can try to type your server IP address in your browser like http://43.184.89.190/ and check if you see It works! message:) If so, you have Apache httpd running fine!

How to configure Apache httpd properly

Although Apache httpd is working and you can use it's default configuration it's good to tune it up a bit. It's always nice to gain additional milliseconds and security.

Apache user and group

Before I will show you how to change configuration I usually create additional user and group for httpd daemon. It's good practice from security side. Each service should operate as separate user. It limits possible damage during attacks, httpd exploitation etc.

If you want to learn more about creating user and groups I recommend reading this tutorial. Here I'll just simply create group and user without shell.

sudo groupadd www
sudo useradd httpd -g www --no-create-home --shell /sbin/nologin

You can change the names as you wish. I like to use www group instead of httpd group for example. I usually add there other services as well, like nginx or php-fpm.

Configuration of httpd.conf

httpd.conf is main Apache httpd configuration file. You should start by editing this file:

sudo vi /usr/local/apache2/conf/httpd.conf

There are couple of options that we should set. Just scroll the file and edit what you need. Values in code blocks are the values that I use:

ServerRoot

ServerRoot /usr/local/apache2

Make sure that ServerRoots direct to the same path as you set via --prefix during .configure It needs to point to installation directory.

Port

Listen 80

This is Apache port under which it should listen for incoming connections.

Modules

LoadModule .....

List of modules is pretty long. Some of the are disabled (they have # at the beginning of the line). Some of them are enabled. You can leave them as they are. Eventually you can enable additional modules.

You should know, that the more modules are enabled the  "slower" Apache httpd is. I'm not saying that it's super slow, but you can google for some benchmarks showing different configurations.

Here is what I like to do. First of all, I comment out all modules = everything is disabled. I enable only that modules that I really use + the modules that are required for proper functioning of Apache httpd. It has few benefits - Apache is faster, eats less resources (CPU and RAM) and it's more resistant for given attacks. Usually when new security issue pops out, it's rather connected to one of the modules, than whole httpd. So you can have more chances to avoid potential security risk with having some stuff disabled.

So here is what I'm using. First part are essential modules that must be enabled for proper Apache functioning under UNIX systems:

LoadModule authz_core_module modules/mod_authz_core.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule dir_module modules/mod_dir.so

Next thing are optional modules that you might want or might not want to enable.

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

I'm using php-fpm for running PHP on my servers. I'm using proxy for that purpose. More about that topic in next part of the article.

LoadModule alias_module modules/mod_alias.so

It allows Alias command. Super useful when you are running WordPress with composer and wpackagist for setting up alias to wp-content directory.

LoadModule access_compat_module modules/mod_access_compat.so

Also useful for WordPress pages. With Apache 2.4 Order command is disabled by default in favor of Require. However some WordPress plugins are still using Order. If you have any issues with that, just enabled access_compat module

LoadModule rewrite_module modules/mod_rewrite.so

Most popular module for almost all CMSes and Framworks. It allows pretty urls and so on. I still wonder why it's note enabled by default?

And that is the whole list of the modules I'm using. Sometimes when I can see that website is not working properly I check error logs. You can figure out that there is some module that is required. Simply enable it, restart the server and check if it's working fine.

User and group

User httpd
Group www

User and group that you created in previous step. It should be set to corresponding values.

ServerName

ServerName localhost

Simplest to set it to localhost here. It will suppress the warning during Apache start for default value.

DirectoryIndex

DirectoryIndex index.php index.html

This is the file that should be loaded when accessing directory. If you are using PHP, you should add index.php at first place, like in example above. However if you just using node.js or plain HTML, index.html is enough.

Save the changes to file and try to restart httpd.

sudo systemctl restart httpd

Check if everything is working fine. That was just some basic configuration. Here are some additional parameters that will increase security on the server. They are not present in httpd.conf file, so you need to add them manually. I like to add them at the end of the file.

ServerTokens

ServerTokens prod

By default in headers that are full information about Apache version, PHP version etc. I set it to prod, so it will only show, that it's powered by Apache. No versions etc.

ServerSignature

ServerSignature off

Disable server signature in internal Apache document's footer. It will hide Apache version in those files as well

FileETag

FileETag none

Disable tagging files with tags. Usually there are additional headers added with information that should not be visible outside (ie. Inode)

Remember that after the changes you need to restart Apache. Otherwise changes won't be applied!

 

MPM configuration

Last thing from configuration I would like to show you is enabling MPM settings. At the bottom of httpd.conf you need to uncomment the line:

Include conf/extra/httpd-mpm.conf

It will enable advanced Apache httpd MPM configuration and it will override the defaults.

Now edit enabled file:

sudo vi /usr/local/apache2/conf/extra/httpd-mpm.conf

There are configuration for each MPM module, so make sure that you are configuring correct values. I enabled event mode, so this is the section I care about:


StartServers 5
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 0

Important thing that you need to know - there is no one config to rule them all. What I want to say that configuration that works on one server, will work on second server. There are dozens of factors like application specific, CPU and RAM, traffic etc. I encourage you to play around with these values to find the optimal settings for your server.

Here is great post that explains in depth MPM configuration. I have two advice's about performance tuning.

First one, that you probably don't even need to change anything if you have regular website without huge amount of traffic. It's just good to know what settings are applied by mpm configuration. When httpd-mpm.conf file was commented out, you didn't have any idea what the settings are. Once it's enabled, you at least know what is configured.

Second advice is - do it slowly. Performance tuning is lengthy process, and as I said, it highly depends on various factors. I like to change one settings, like StartServers for instance and I wait day or two and monitor response times, CPU and RAM usage etc. Sometimes even if you increase something you won't see a difference in response time, but you will get higher CPU usage. Then you just can rollback the changes. If you modify 3 or 4 values at one time, it's hard to say which comes with best (or any) result.

Remember to restart Apache httpd after making changes:)

 

Setting up Virtual Hosts for websites

I want to show you how I setup VirtualHosts for my websites.  There are tons of different ways of storing Vhost files. Some of people like to keep everything in one file, some likes symbolic links etc. It actually doesn't really matter how you do it. But it's nice to keep it consistent and easy to manage.

Root directory

First I make sure that I have /var/www directory present in my system. If you don't have one, create it. Make sure that it has root as an owner and 755 permissions.

sudo mkdir /var/www
sudo chmod 755 /var/www
sudo chown root:root /var/www

Website directories

Second thing are websites directories. I create such directory, even if I have only one website on my server. For directory name I'm using domain name. In each directory I create two subdirectories - htdocs for storing website files and logs for storing logs. So for instance:

sudo mkdir /var/www/blacksaildivision.com
sudo mkdir /var/www/blacksaildivision.com/htdocs
sudo mkdir /var/www/blacksaildivision.com/logs

Directory ownership

When it comes to ownership to main directory I set root:root. When it comes to subdirectories I set owner as developer or any other user account (other than root) that can clone repositories, use files etc. As group, I'm using www group. As you might remember Apache httpd is added to this group as well.

sudo chown root:root /var/www/blacksaildivision.com
sudo chown developer:www /var/www/blacksaildivision.com/htdocs
sudo chown developer:www /var/www/blacksaildivision.com/logs

Directory permissions

Permissions to website directory is the same as for /var/www. For htdocs and logs I'm using 2775. So each user in www group will be able to write to these files. 2 at the beginning means that every new subdirectory created will have the same ownership as parent directory (developer:www)

sudo chmod 755 /var/www/blacksaildivision.com
sudo chmod 2775 /var/www/blacksaildivision.com/htdocs
sudo chmod 2775 /var/www/blacksaildivision.com/logs

 VirtualHost file

Once you have your directory structure ready, you can create VirtualHost files. I create one file per domain. All files are stored in conf/extra directory with the same name scheme as other files there. So let's create file there:

sudo vi /usr/local/apache2/conf/extra/httpd-vhost-blacksaildivision.com.conf

and paste there following contents:

<VirtualHost *:80>
ServerName blacksaildivision.com

# Directory settings
DocumentRoot /var/www/blacksaildivision.com/htdocs
<Directory /var/www/blacksaildivision.com/htdocs>
AllowOverride All
Require all granted
Options +FollowSymLinks -Indexes -Includes
</Directory>

# PHP-FPM settings
ProxyPassMatch "^/(.*\.php(/.*)?)$" "fcgi://127.0.0.1:9000/var/www/blacksaildivision.com/htdocs"

# Logging
ErrorLog "/var/www/blacksaildivision.com/logs/httpd-error.log"
CustomLog "/var/www/blacksaildivision.com/logs/httpd-access.log" common

</VirtualHost>

File has few sections. Everything is wrapped with VirtualHost block with Apache httpd port.

ServerName stands for domain name. Each file has to have different domain name to avoid conflicts.

Next are directory config. DocumentRoot is basically the path to directory you created before. Now you need to specify settings of this directory. AllowOverride All means that I'm allowing .htaccess files to override all settings. Require all granted is a must here if you want to have access to this directory. Last thing are Options. I disable Indexes and Includes for security reasons. FollowSymLinks is usually required by frameworks and CMSes.

I'm using PHP in PHP-FPM mode, so I need to pass all requests to PHP files via Proxy to FPM daemon.

Last parts are logs. Error log will contain information about errors. Custom log is like access log. All requests to your domain will be stored there.

Save the changes to the file. Now you need to include this file in main Apache httpd config file. So edit it:

sudo vi /usr/local/apache2/conf/httpd.conf

and at the end of the file include newly created file:

Include conf/extra/httpd-vhost-blacksaildivision.com.conf

Restart Apache httpd daemon and try to access your website. It should work fine. If not, please check httpd-error.log in logs directory for possible errors.

 

 Run Apache httpd on system start

Last thing is to add Apache httpd daemon to start with system boot. So after server start/restart  httpd will run automatically:

sudo systemctl enable httpd

So that's it. You have fully working Apache httpd in latest version installed on your system 🙂 This process might take some time, but you will have full control over httpd.

As always you can use our LampOnSteroids project (based on Ansible) to speed and automate everything up!