Ubuntu 18.04 – Apache2 – HTTP2

Today, I'm going to install the latest Apache2 and PHP7 on an Ubuntu 18.04 server and enable the HTTP/2 protocol. To upgrade an existing Apache system to use HTTP/2, follow these simple instructions:

$ sudo -i
$ apt-get install python-software-properties
$ add-apt-repository -y ppa:ondrej/apache2
$ apt-key update
$ apt-get update

The above commands add the latest apache2 repository to your system and updates the list of available packages your system is aware of.

$ apt-get upgrade

Now your system is up to date with the latest packages. I am assuming you already have Apache/php etc running. Now we can enable the module in Apache:

a2enmod http2

Now we have to edit the virtual host and add this protocol to it.

<VirtualHost *:443>
 # prefer http over http1
 Protocols h2 http/1.1

Now restart Apache and you should be good to go !

Ansible Linux

Ansible – One role to rule them all

Ansible Role is a concept that deals with ideas rather than events. Its basically another level of abstraction used to organize playbooks. They provide a skeleton for an independent and reusable collection of variables, tasks, templates, files, and modules which can be automatically loaded into the playbook. Playbooks are a collection of roles. Every role has specific functionality.

For example, to install Nginx, we need to add a package repository, install the package and set up configuration. Roles allow us to create very minimal playbooks that then look to a directory structure to determine the configuration steps they need to perform.

Role directory structure

In order for Ansible to correctly handle roles, we should build a directory structure so that Ansible can find and understand. We can do this by creating a Roles directory in our working directory.

The directory structure for Roles looks like this:

 - files
 - handlers
 - meta
 - templates
 - tasks
 - vars

A role's directory structure consists of files, handlers, meta, templates, tasks, and vars. These are the directories that will contain all of the code to implement our configuration. We may not use all of the directories, so in real practice, we may not need to create all of these directories.

Ansible will search for and read any yaml file called roles/nginx/tasks/main.yml automatically. Here is the main.yml file;

- name: Installs Nginx
  apt: pkg=nginx state=installed update_cache=true
    - Start Nginx

- name: Upload default index.php for host
  copy: src=index.php dest=/usr/share/nginx/html/ mode=0644
  register: php
  ignore_errors: True

- name: Remove index.html for host
  command: rm /usr/share/nginx/html/index.html
  when: php|success

- name: Upload default index.html for host
  copy: src=index.html dest=/usr/share/nginx/html/ mode=0644
  when: php|failed

As we can see, the file just lists the steps that are to be performed, which makes it reads well.

We also made a change how we references external files in our configuration. Our src lines reference a static_files directory. This is unnecessary if we place all of our static files in the files subdirectory. Ansible will find them automatically.

Now that we have the task portion of the playbook in the tasks/main.yml file, we need to move the handlers section into a file located at handlers/main.yml.

- name: Start Nginx
  service: name=nginx state=started

Move index.html and index.php pages out of the static_files directory and put them into the roles/nginx/files directory.

So now we can create a very very simple playbook with the following content:

- hosts: test_group
    - role: nginx

Run it!

$ ansible-playbook -s test.yml

PLAY [test_group] ******************************************************************** 

GATHERING FACTS *************************************************************** 
ok: []

TASK: [nginx | Installs Nginx] ************************************************ 
ok: []

TASK: [nginx | Upload default index.php for host] ***************************** 
ok: []

TASK: [nginx | Remove index.html for host] ************************************ 
changed: []

TASK: [nginx | Upload default index.html for host] **************************** 
skipping: []

PLAY RECAP ********************************************************************              : ok=4    changed=1    unreachable=0    failed=0  

Shopware + NGIX

Shopware is a widely used professional open source e-commerce software. Based on bleeding edge technologies like Symfony 3, Doctrine2 and Zend Framework Shopware comes as the perfect platform for your next e-commerce project.

Set up the timezone and make sure all updates are done and required packages are installed:

sudo dpkg-reconfigure tzdata
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget vim git unzip socat apt-transport-https

Install PHP and required packages

sudo apt install -y php7.0 php7.0-cli php7.0-fpm php7.0-common php7.0-mysql php7.0-curl php7.0-json php7.0-zip php7.0-gd php7.0-xml php7.0-mbstring php7.0-opcache

Install database server (mysql or mariadb)

sudo apt install -y mariadb-server
sudo mysql_secure_installation
Would you like to setup VALIDATE PASSWORD plugin? N
New password: your_secure_password
Re-enter new password: your_secure_password
Remove anonymous users? [Y/n] Y
Disallow root login remotely? [Y/n] Y
Remove test database and access to it? [Y/n] Y
Reload privilege tables now? [Y/n] Y

Connect and create a user and database:

sudo mysql -u root -p
# Enter password
mysql> CREATE DATABASE dbname;
mysql> GRANT ALL ON dbname.* TO 'username' IDENTIFIED BY 'password';

Install and configure NGIX

sudo apt install -y nginx
sudo nano /etc/nginx/sites-available/shopware.conf
server {
    listen 80;
    listen 443 ssl;

    ssl_certificate /etc/letsencrypt/;
    ssl_certificate_key /etc/letsencrypt/;
    ssl_certificate /etc/letsencrypt/example.com_ecc/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/example.com_ecc/private.key;
    root /var/www/shopware;

    index shopware.php index.php;

    location / {
        try_files $uri $uri/ /shopware.php$is_args$args;

    location /recovery/install {
      index index.php;
      try_files $uri /recovery/install/index.php$is_args$args;

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
sudo ln -s /etc/nginx/sites-available/shopware.conf /etc/nginx/sites-enabled
sudo systemctl reload nginx.service

Now it's time to install Shopware;

sudo mkdir -p /var/www/shopware
sudo chown -R {your_user}:{your_user} /var/www/shopware
cd /var/www/shopware
wget -O
sudo chown -R www-data:www-data /var/www/shopware

You should alter the default PHP values of memory_limit = 256M and upload_max_filesize = 6M.

Now fire up a browser to your server and you will see the setup wizard of Shopware, ready to complete.


Configuration of HP IRF (Intelligent Resilient Framework)

HP’s Intelligent Resilient Framework (IRF) is an advanced technology that allows one to virtualize 2 or more switches into a single switching and routing system also known as a “virtual switch”. IRF is available on the new HP A series switches such as the A5120 model and the A5500-5800 models.

From Wikipedia:
Intelligent Resilient Framework (IRF) is a software virtualization technology developed by H3C (3Com). Its core idea is to connect multiple network devices through physical IRF ports and perform necessary configurations, and then these devices are virtualized into a distributed device. This virtualization technology realizes the cooperation, unified management, and non-stop maintenance of multiple devices.[1] This technology follows some of the same general concepts as Cisco’s VSS and vPC technologies.

Basic Configuration;

Step 1:
Login to the switch through the console port

Step 2:
Ensure that both switches are running the same software version

[H3C] system view
[H3C]display version

Step 3:
Reset the configuration of the switches.

reset saved-configuration

Step 4:
Assign a unit number to each S5800. Switch 1 or 2. (Later you will see the unit number on the right side the switch on the front panel led)

On unit 1:

[H3C]irf member 1 renumber 1
Warning: Renumbering the switch number may result in configuration change or loss. Continue?[Y/N]:y

On unit 2:

[H3C]irf member 1 renumber 2
Warning: Renumbering the switch number may result in configuration change or loss. Continue?[Y/N]:y

Step 5:
Save the configuration and reboot the switches

save irf.cfg
startup saved-configuration irf.cfg

Step 6:
Setting priority on Master S5800.
On unit 1:

[H3C]irf member 1 priority 32

Step 7:
Shutdown the 10 Gbps port that will form the IRF Group (on both switches)
On Unit 1:

[H3C]int TenGigabitEthernet 1/0/25
[H3C]int TenGigabitEthernet 1/0/26

On Unit 2:

[H3C]int TenGigabitEthernet 2/0/27
[H3C]int TenGigabitEthernet 2/0/28

Step 8:
Assign the 10 Gbps port to an IRF port group
On Unit 1:

[H3C]irf-port 1/1
[H3C-irf-port]port group interface TenGigabitEthernet 1/0/25
[H3C-irf-port]port group interface TenGigabitEthernet 1/0/26

On Unit 2:

[H3C]irf-port 2/2
[H3C-irf-port]port group interface TenGigabitEthernet 2/0/27
[H3C-irf-port]port group interface TenGigabitEthernet 2/0/28

Step 9:
Enable the 10 Gbps ports that will form the IRF (on both switches)
On unit 1:

[H3C]int TenGigabitEthernet 1/0/25
[H3C-Ten-GigabitEthernet1/0/25]undo shutdown
[H3C]int TenGigabitEthernet 1/0/26
[H3C-Ten-GigabitEthernet1/0/26]undo shutdown

On unit 2:

[H3C]int TenGigabitEthernet 2/0/27
[H3C-Ten-GigabitEthernet2/0/25]undo shutdown
[H3C]int TenGigabitEthernet 2/0/28
[H3C-Ten-GigabitEthernet2/0/26]undo shutdown
Step 10: Activate the IRF Port Configuration (on both switches)
[H3C]irf-port-configuration active

Step 11:
Save the configuration


Step 12:
Connect the 2 10GbE Direct Attach Cables (DACs) as Shown in the IRF Diagram
NOTE: The secondary switch (unit 2) will now reboot automatically.

Step 13:
The IRF stack should now be formed. Verify IRF operation

[H3C]display irf
[H3C]display irf configuration
[H3C]display irf topology
[H3C]display devices


Linux Networking

Ubuntu Bonding (trunk) with LACP

Linux allows us to bond multiple network interfaces into single interface using a special kernel module named bonding. The Linux bonding driver provides a method for combining multiple network interfaces into a single logical “bonded” interface.

sudo apt-get install ifenslave-2.6

Now, we have to make sure that the correct kernel module bonding is present, and loaded at boot time.
Edit /etc/modules file:

# /etc/modules: kernel modules to load at boot time.
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.

As you can see we added “bonding”.
Now stop the network service:

service networking stop

Load the module (or reboot server):

sudo modprobe bonding

Now edit the interfaces configuration to support bonding and LACP.

auto eth1
iface eth1 inet manual
    bond-master bond0
auto eth2
iface eth2 inet manual
    bond-master bond0
auto bond0
iface bond0 inet static
    # For jumbo frames, change mtu to 9000
    mtu 1500
    bond-miimon 100
    bond-downdelay 200 
    bond-updelay 200 
    bond-mode 4
    bond-slaves none

Now start the network service again

service networking start

Verify the bond is up:

cat /proc/net/bonding/bond0

Output should be something like:

~$ cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011)
Bonding Mode: IEEE 802.3ad Dynamic link aggregation
Transmit Hash Policy: layer2 (0)
MII Status: up
MII Polling Interval (ms): 0
Up Delay (ms): 0
Down Delay (ms): 0
802.3ad info
LACP rate: slow
Min links: 0
Aggregator selection policy (ad_select): stable
Active Aggregator Info:
    Aggregator ID: 1
    Number of ports: 2
    Actor Key: 33
    Partner Key: 2
    Partner Mac Address: cc:e1:7f:2b:82:80
Slave Interface: eth1
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: 00:0c:29:4f:26:c5
Aggregator ID: 1
Slave queue ID: 0
Slave Interface: eth2
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: 00:0c:29:4f:26:cf
Aggregator ID: 1
Slave queue ID: 0

CGN: Carrier Grade NAT

Every network engineer with some experience knows RFC1918 address space from the top of their head. So no need to explain that almost every office, home user and some datacenter networks are using IP’s from this RFC. So far, so good. But, what if you have a large network with more then 10 physical locations and need to hook things together? This is where CGN comes in handy.

If you have multiple offices or locations and one of the NAT-performing routers has the same subnet on the inside as on the outside (the outside being the main office network here), no routing will be possible for this network. Specially when dealing with a lot of branch offices (and more IT personel) it becomes more difficult to know exactly what RFC1918 ranges are in use, and where. For example, i have worked for a large enterprise where somebody in Spain wanted to maintain control over the local network (idiot). He just figured it would be handy to configure as local network and everything worked until he had to open a VPN tunnel to the main office in Amsterdam. As the main office network equipment was using the things started to fall apart.

This is where RFC 6598 comes in handy. This RFC reserves an IPv4 prefix that can be used for internal addressing, separately from the RFC1918 addresses. Result: no overlap, yet no use of publicly routable addresses. The chosen prefix is

It’s good to know that, for networking purposes, there is a complete /10 range that can be used (obviously isolated from anything else). CGN has drawbacks such as complexity and administation. But in a large enterprise CGN would definatly be the way to go.

Here you can find some great test results!


Pacemaker and Corosync HA

In this setup we will setup a HA failover solution using Corosync and Pacemake, in a Active/Passive setup.

Installation and Setup


  • Hosts or DNS resolvers
  • NTP Must be installed and configured on all nodes
cat /etc/hosts
10.0.1 10   ha1 server01   ha2 server02

We will install pacemaker, it should install corosync as an dependency, if not install it.

apt-get install pacemaker

Edit corosync.conf. The bind address is the network address, NOT the IP. The mcastaddr is default, which is fine.

cat /etc/corosync/corosync.conf
interface {
        # The following values need to be set based on your environment
        ringnumber: 0
        mcastport: 5405

We also want corosync to start pacemaker automatically. If we do not do this, we will have to start pacemaker manually.
ver: 0 Indicates corosync to start pacemaker automatically. Setting it to 1, will require manually start of pacemaker!

cat /etc/corosync/corosync.conf
service {
    # Load the Pacemaker Cluster Resource Manager
    ver:       0
    name:      pacemaker

Copy/paste the content of corosync.conf, or scp the file to the second node.

scp /etc/corosync/corosync.conf

Make corosync starts at boot time.

cat /etc/default/corosync
# start corosync at boot [yes|no]

Start corosync

/etc/init.d/corosync start

Check the status of the cluster

Last updated: Fri Jun  9 11:02:55 2017          Last change: Wed Jun  7 14:26:06 2017 by root via cibadmin on server01
Stack: corosync
Current DC: server01 (version 1.1.14-70404b0) - partition with quorum
2 Nodes configured, 2 expected votes
0 Resources configured.
Online: [ server01 ]

Copy the config file to the second node

scp /etc/corosync/corosync.conf server02:/etc/corosync/

Now on the second node, try to start corosync

/etc/init.d/corosync start

Check the status again. We should now hopefully see the second node joining. If this fails check the firewall settings and hosts file (they must be able to resolve).

We are getting some warnings. Use the following commands:

crm configure property stonith-enabled=false
sudo crm configure property no-quorum-policy=ignore
crm_verify -L

Now add a virtual IP to the cluster.

crm configure primitive VIP ocf:IPaddr2 params ip= nic=eth0 op monitor interval=10s

Now we should have added an VIP/Floating IP, we can test this by a simple ping. Should respond from both nodes.

Adding Resources: Services

Now we are ready to add a service to our cluster. In this example we use a postfix service (smtp) that we want to failover. Postfix must be installed on both nodes

crm configure primitive HA-postfix lsb:postfix op monitor interval=15s

Check the status.

crm status

As we have not linked the IP to the service yet, postfix could be running on server02 while the IP is on server01. We need to set them both in one HA group.

crm configure group HA-Group VIP HA-postfix

If we check the status again, we can see that the two resources are now running on the same server.

Online: [ server01 server02 ]
 Resource Group: HA-Group
     VIP    (ocf::heartbeat:IPaddr2):   Started server01
     HA-postfix (lsb:postfix):  Started server01

Looks good !

If an resource fails, for some reason, like postfix crashes, and cannot start again, we want to migrate to another server.
Per default the migration-threshold is not defined/set to infinity, which will never migrate it.

When we have 3 fails, migrate the node, and expire the failed resource after 60 seconds. This will allow it to automatically to move it back to this node.

primitive HA-postfix lsb:postfix \
        op monitor interval="15s" \
        meta target-role="Started" migration-threshold="3" failure-timeout=60s

Now we are DONE!

Some extra commands that might be usefull when managing the cluster:

Deleting a resource

crm resource stop HA-XXXX
crm configure delete HA-XXXX

Where XXXX is the name of the HA cluster.

Migrate / Move Resource

crm_resource --resource HA-Group --move --node server02

View configuration

crm configure show

View status and fail counts

crm_mon -1 --fail

Configure FC Multipath on Debian (HP EVA)

This detailed how to guides to high availability and performance on Debian/Ubuntu for with a dual FC HBA (Brocade) and shared storage on a HP EVA6300. Tested on Debian Linux 5.x and 6.x bits running on HP Proliant Dl360 and DL380 models, with 8GB FC Host Bus Adapters from Brocade.

Configure the software we need

# apt-get install multipath-tools-boot multipath-tools firmware-qlogic sysfsutils lsscsi
# reboot

Verifying that the correct Linux kernel module was loaded

[email protected]:~# cat /var/log/dmesg | grep Brocade
[ 11.584057] Brocade BFA FC/FCOE SCSI driver - version:
[ 11.654052] scsi1 : Brocade FC/FCOE Adapter, hwpath: 0000:0a:00.0 driver:
[ 12.011790] scsi4 : Brocade FC/FCOE Adapter, hwpath: 0000:0a:00.1 driver:
[email protected]:~# cat /var/log/dmesg | grep scsi
[ 11.550599] scsi0 : hpsa
[ 11.558223] scsi 0:0:0:0: RAID HP P420i 3.54 PQ: 0 ANSI: 5
[email protected]:~# modinfo bfa
filename: /lib/modules/3.2.0-4-amd64/kernel/drivers/scsi/bfa/bfa.ko
author: Brocade Communications Systems, Inc.
description: Brocade Fibre Channel HBA Driver fcpim

Create the /etc/multipath.conf for the IBM DS8300 storage

First we need to find out the correct wwid:
As multipath is not yet correctly configured, the command below will return “undef” for some paths, as the example below. What we need now is to identify the wwid between parenthesis.

[email protected]:~# multipath -ll
fc_storage (3600143801259ba3a0000b00001650000) dm-1 HP,HSV340
size=2.0T features='1 queue_if_no_path' hwhandler='0' wp=rw
|-+- policy='round-robin 0' prio=1 status=active
| `- 1:0:0:1 sdb 8:16 active ready running
|-+- policy='round-robin 0' prio=1 status=enabled
| `- 1:0:1:1 sdc 8:32 active ready running
|-+- policy='round-robin 0' prio=1 status=enabled
| `- 4:0:0:1 sdd 8:48 active ready running
`-+- policy='round-robin 0' prio=1 status=enabled
  `- 4:0:1:1 sde 8:64 active ready running

Mind the wwid (3600…..)

# Multipath.conf file for HP EVA system
# Version 1.02
# Storage node: HP EVA
# Connection: Dual 8GB FC
defaults {
    polling_interval    30
    failback            immediate
    no_path_retry       5
    rr_min_io           100
    path_checker        tur
    user_friendly_names yes
devices {
# These are the default settings for P6300 (HP EVA)
    device {
        vendor                   "HP"
        product                  "HSV340"
        path_grouping_policy     group_by_prio
multipaths {
        multipath {
                wwid                    3600143801259ba3a0000b00001650000
                alias                   fc_storage
                path_grouping_policy    failover
                path_selector           "round-robin 0"

The internet is broken?

Yesterday, 12th of Aug 2014, the internet grew passed the 512.000 BGP Routes. This was not something new, Cisco warned about this in May 2014:

It wasn’t that long ago (2008) that the table reached 256k routes, triggering action by network administrators to ensure the continued growth of the Internet. Now that the table has passed 500,000 routes, it’s time to start preparing for another significant milestone – the 512k mark.

A nice graph can be found on also showing that the number of ASN’s grew passed the 48K.

If you accept full internet routes on your network this might be the time to verify your maximum table size on those components. Some equipment might need to be rebooted in order for this change to become active.

More information can be found here (read it!)