Tag Archives: SSH

Ansible tutorial - part 1

Ansible tutorial – part 1

What you will learn in part 1 of this tutorial? What is Ansible? How to install Ansible on Windows/Linux/MacOS? How to prepare test environment for Ansible? Intro to Inventory files. First connection to the server.

A short word about Ansible tutorial

This is multipart series about Ansible, great tool for provisioning and configuration management. You will learn how to create useful roles and use Ansible in efficient way. You will also pick up some good practices:)

If you like tl;dr take a look at shorter veresion of this tutorial on GitHub: https://github.com/blacksaildivision/ansible-tutorial
There are also some examples!

Part 1: Intro to Ansible and Inventory files
Part 2: Playbooks and tasks
Part 3: Templates and handlers 
Part 4: Variables


First thing first - what is Ansible?

Ansible is a simple yet powerful tool for configuration management and orchestration of your infrastructure. It speeds up installing software, configuring servers and most important - it reduces manual steps you need to make, to setup your server(s). It is also great alternative to Puppet and Chef. Both are similar tools to Ansible, but in my opinion Ansible is much easier to learn and master.

How do WE use Ansible (possible use cases)?

We have lot of servers that we need to take care of. At the beginning we were just logging into our servers via SSH and fire all commands in terminal. It was painful job, so we decided that we need to automate things. We checked Puppet, Chef and Ansible and we decided to stick with the last one. It was really good choice. Now we use Ansible for:

  • Installing our entire LAMP stack on new server
  • Configuration of most of the software on our servers
  • Testing if servers are configured properly
  • Deploying our applications
  • Keeping Infrastructure As Code (IAC) - all infrastructure is in single Git repository

Most of these tasks are done with single command. For instance we can update nginx on all servers with bumping the version in our variable file and execute one command in terminal. It will be updated on all servers at once.  No more logging it via SSH and executing commands one by one:)


What do I need to know about Ansible?

Ansible is using files for configuration and provisioning. By using Ansible you can go with IAC - Infrastructure As Code. It means that you can keep you entire infrastructure in GIT repository. You can track changes and use all goodies that comes with source version control.

Ansible is based on YAML files. If you don't know it, you will learn it in minutes. Really easy and simple.

For templates it uses Jinja2 engine. Powerful and easy to learn.

It does not require additional software to be installed on the server. It uses old-school ssh protocol for connecting and configuring the server. One requirements is to have Python installed on the server, but these days, all common Linux distributions have it out of the box.

Let's get started - how to install Ansible?

No matter what is you operating system, when you will be installing Ansible, make sure that you will install version 2.0  or higher. It has lot more features than 1.x versions. It is stable and commonly used these days.

If you are using OS X or Linux, it is not a big deal for you. Here are some tutorials from Ansible documentation - how to install Ansible on OS X or Linux? The easiest way is to use APT, Yum for Linux and Pip or Homebrew.  Please make sure that you are installing Ansible in version 2.0 or higher:)

If you are using Windows, it's a bit more hassle, unless you already have the Windows with Bash terminal! If you do, simply use APT for installing Ansible. However if you don't have it yet, you need to install more things. On our Windows machines we use Babun (much better version of Cygwin, if you didn't hear about it, check it out right now!). To install Ansible with Babun, please use this tutorial.


Prepare test environment

You will need a server or virtual machine where you can test Ansible. If you are just starting with I highly recommend to use empty and fresh server. You don't want to mess up production server while learning it!

If you have some clean server on DigitalOcean for example, you can use it. However easier (and free) way is to install Vagrant and use dummy box. If something will go wrong, you can always remove everything and start fresh. For the purpose of this tutorial, we will use Vagrant.

Start with a new directory for example: vagrant_ansible_tutorial . In this directory create file named Vagrantfile and paste following content into that file:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  config.vm.box = "geerlingguy/centos7"
  config.vm.network "forwarded_port", guest: 80, host: 8080
  config.vm.network "private_network", ip: ""
  config.vm.provider "virtualbox" do |vb|
     vb.memory = "2048"

You can adjust some parameters if you like. Save the changes in file and execute vagrant up in your terminal. You must be in vagrant_ansible_tutorial directory. Wait until your box will be up and running.

Inventory file

First thing that we need to do before we can actually start using Ansible is to create an Inventory file. Inventory files are the way to specify connection to the servers for Ansible. You can treat is as an alternative to SSH command. So instead of executing:

ssh -i my_private_key_file_path username@server_ip_or_domain.com

you need to add following line to inventory file:

server_ip_or_domain.com ansible_user=username ansible_private_key_file=my_private_key_file_path

Let's create a file in our directory project and name it hosts .  Paste following code into that file:

[ansible_tutorial] ansible_user=vagrant ansible_private_key_file="./.vagrant/machines/default/virtualbox/private_key"

Let's start from 2nd line. We specified IP address to our server. This is the same IP as in Vagrantfile . Instead of using the IP you can use domain address like blacksaildivision.com.

Next is the user that we will use to connect to the server. Vagrant default user is vagrant . Last thing is private key that we are using for accessing the server. After starting the box, Vagrant will create new key in the .vagrant directory.

Alternatively you can specify the password instead of SSH key. But if you are using passwords instead of keys for accessing your server, you should change it ASAP, because you are doing it wrong. Passwords are easy to hack. Trust me, at the beginning our server was hacked twice, even though we had pretty strong password. We didn't know about such thing as SSH back then:) If you will use password, Ansible will also give you a warning.

If you need to alter the port or add additional arguments for your connection, check Ansible documentation here.

Groups in Inventory files

In first line we have [ansible_tutorial]  and it's name of the group of the servers.  You can omit it, but the good practice is to specify the group for server(s). Take a look on following file:

[web] ansible_user=developer ansible_private_key_file="~/.ssh/web" ansible_user=developer ansible_private_key_file="~/.ssh/web" ansible_user=developer ansible_private_key_file="~/.ssh/web"
[database] ansible_user=db ansible_private_key_file="~/.ssh/db" ansible_user=db ansible_private_key_file="~/.ssh/db"

There are two groups of the servers - web and database. Now we can execute different commands on each group. For instance on database group we can install MongoDB related software and on servers that belongs to web group we can install nginx+php.

It's good practice to specify a group, even if you have single server. It's more descriptive way than using IP or host name.

Testing connection to server

Last thing in this part is to test connection between Ansible and the server. Execute following command:

ansible ansible_tutorial -m ping -i hosts

If you didn't connect to this server via ssh before, you will get message about authentication for new host. Just type yes.

You should get following input: | SUCCESS => {
    "changed": false, 
    "ping": "pong"

What we just did is we checked if Ansible is able to connect to the server. If you get SUCCESS message, you are ready to start provisioning with Ansible!

Explaining the command you just executed. There are 3 parameters. First one is the group from inventory file. You can also specify particular host or IP from inventory file or you can use all  to test all servers within given inventory file:

ansible all -m ping -i hosts

Second thing is module name. We need to use ping module to check the connection. Last thing is inventory file, which in our case is created hosts  file.

If you have the problems with getting SUCCESS message, you need to debug the connection. Use -vvvv to get debug output. Usually you are able to figure out what is wrong with the connection from debug output:

ansible all -m ping -i hosts -vvvv

In most cases it is wrong private key path or some typos.

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/*

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:


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:


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


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


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

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


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

-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 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

-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 -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


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


# Clear all iptables rules (everything is open)

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

# Keep all established connections

# 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
-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 INPUT -p icmp --icmp-type echo-request -j PING_OF_DEATH

# Prevent port scanning
-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

# 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


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.

How to install EPEL on CentOS

How to install EPEL repository on CentOS

We need to install EPEL repository for our CentOS. This will be first step to setup our working Web Server. EPEL is additional repository for yum, with tons of useful packages which we won't find in default base repository. We will use it in the future so let's start using it now;)

In this post we will also setup local vagrant box. We will be using it for future operations like installing Apache or Varnish.

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...

Vagrant setup

The best idea without messing everything out is to use Vagrant.

If You don't have Vagrant installed please download and install it first. After installation You should be ready to use Vagrant. Create directory in place where You want to keep Your vagrant file.

mkdir vms
cd vms

Next thing what we need to do is to download CentOS box. We need the box to replace default one that comes with Vagrant. We will use chef/centos-6.6 from Vagrant boxes directory. You can search more boxes here if You want, for instance You can use 6.5 instead of 6.6. Decision is Yours:)

vagrant box add chef/centos-6.6

Vagrant will download the image of OS You selected. It might take a while, depending on Your network performance.

Next we need to init Vagrant instance.

vagrant init chef/centos-6.6

It will create Vagrantfile in Your directory. You can check out this file in any editor. This file has basic Vagrant configuration, you can adjust the options like forwarded ports,  synced folders etc. For now, let's leave this configuration file and let's bring our machine to life!

vagrant up

It will init the machine. After that You can login via SSH and start playing around:)

vagrant ssh


mkdir vms
cd vms
vagrant init chef/centos-6.6
vagrant up
vagrant ssh


How to install EPEL

Installing EPEL on CentOS is really easy.  First of all You need to get the URL for EPEL repo. If You are just following the instructions in this tutorial You can execute the command. But for instance if You have 32bit version instead of 64bit You need to replace x86_64 to i386

yum install http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm

Depending on Your vagrant / server setup You might need to execute this command as root or at least as sudo user. If You are just following the steps in this tutorial You are probably Vagrant user. If You execute this command You will see permission denied error. Start this command with sudo (sudo yum install http://....) and everything should be OK:)

Next thing that we need to do is to import GPG key for this repo:

rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6

And basically that's it! You are now able to use EPEL packages with yum!

To validate the installation You can execute yum repolist command  and You should see epel Extra Packages for Enterprise Linux 6 - x86_64

yum repolist

It means that everything is OK. Now, if You want to install any packages with the EPEL repository You need to use

yum --enablerepo=epel install PACKAGE_NAME


yum install http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rp -y
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6


Using Ansible to install EPEL

If You are using Ansible for server provisioning as I do, You can find ansible-webserver project on GitHub


It has EPEL role, that will help You install EPEL via Ansible

GIT Deployment Keys

GIT Deployment Keys on Windows

Hello everybody!
Today I'd like to show You how to setup deployment keys on Windows OS. It's pretty helpful and time saving thing. If You are tired of passing username and password each time, it'll be simpler to use deployment key. After git push, your won't have to pass username and password.

OK, so how to start with deployment keys?

I'm using both GitHub and Bitbucket. They have possibilities to use deployment keys. I'm not entirely sure how about the rest of services. You will have to click through the settings to check if there's any Deployment key option. As I assume You already have the account. There's no need to read this article further if You don't have such account:p

Second thing that You have to have is GIT. Personally I use msysgit. But deployment keys trick will work on any other GIT that You are using.

Third, the most important thing is Your SSH key. Usually I'm using PuTTY Key Generator (puttygen.exe) to create public and private key pair. But You can use any software like OpenSSH. If You don't know how to generate private and public key, You can learn how to do this here. Or just typing in google something like "windows generate ssh key". There are plenty of good tutorials for that, depends on which software You are using for generating ssh key. While You are using PuTTY Keygen please choose from menu Conversion -> Export OpenSSH key. It will save Your ssh key in OpenSSH format.

When You are saving Your private and public key, it's nice to stick to Linux naming convection. So name Your keys like that:

  • private key -> id_rsa
  • public key -> id_rsa.pub

It's not necessary of course, but it's more readable to other users.

What to do with public ssh key?

When You have your keys generated which will become Your deployment keys, please login to GitHub or Bitbucket. In Your account settings You will find SSH Key options.

  • In Bitbucket You can find this section by visiting Manage Profile and SSH key.
  • In GitHub it's on Account Settings and SSH Key

In both cases please press Add SSH key  button in order to add new one. Label it as You wish and paste SSH public key into Key text area.

Check Repository config

When You create new repo, it's not a problem as long as You copy SSH not HTTPS URL for Your repo.

If You have existing repository, You will have to change it's config file if You want Your deployment keys to be working correctly. Simplest way is just to edit it in text editor.

Please open .git/config file. On Windows .git directory is hidden by default. Modify this file like this:

[remote "origin"]
    url = https://github.com/username/your_project.git //Remove this line
    url = git@github.com:username/your_project.git //Add this line

You can also do it from console by typing this command:

git remote set-url origin git@github.com:username/your_project.git

If You are not sure what Your repository ssh URL should look like, please login to Bitbucket/GitHub, go to Your repository and under clone choose SSH instead of HTTPS. The URL for clone will be the URL that You have to provide in set-url or paste it after url = in .git/config file.

Where to put my ssh keys?

That's very good question actually. When I wanted to setup deployment keys at the first time I didn't really know where to put them. In Linux OS we have .ssh directory in our home directory. So You just have to copy it there and it will work.

On Windows OS there is also ~ directory. Just type ls ~ in You command line and it will take You to Your home directory. Usually it'll be C:\User\USERNAME where USERNAME is Your Windows username. You can create .ssh directory there and it will behave like on Linux OS.

If this trick doesn't work for You, please try to create .ssh folder and copy your keys to msysgit installation folder.

Is it working?

Let's test our setup with deployment keys. Try for instance git pull. After first connection You will be asked if You want to add Your key permanently to know_hosts file. Type yes. You will have to type your passphrase to key if You set one. After that You can use deployment keys and never be notified that You have to type username or password.

Still not working? Please describe Your setup and errors in comments below. I'll try to solve Your problem:)