A quick intro to background and foreground command control in bash.

Appending an & (ampersand) to any command run within bash will background the process.

For example:

for count in $(seq 1 10); do
    sleep 10m &
[11] 12775
[12] 12776
[13] 12777
[14] 12778
[15] 12779
[16] 12780
[17] 12781
[18] 12782
[19] 12783
[20] 12784

Will background 10 processes which will simply sleep for 10 minutes. Bash will return a table of job ids and their PIDs.

To list a table which shows all background processes use the built-in jobs command.

$ jobs -l

You can also use ps to list the background processes by requesting the processes which have the parent pid of your current bash shell.

$ ps -O user --ppid $$

By using ps we can refer to important information on the processes.

To connect to one of the background processes, use fg and the job id number.

$ fg 11
sleep 10m

To put job 11 back into the background you first have to suspend the process by using ^Z (Control-Z) and then running bg specifying the job id.

$ fg 11
sleep 10m
[11]+  Stopped                 sleep 10m
$ bg 11
[11]+ sleep 10m &

To put fg, bg and ^Z to practical use, lets say you were copying a large directory which was going to take a long time though you wanted to regain control of your current shell.

Suspend the current command using ^Z.

$ cp -a /media/backups/rsnapshot/hourly.0 /opt/restore
[1]+  Stopped                 cp -a /media/backups/rsnapshot/hourly.0 /opt/restore

Background the task with bg referring to the job id in the table.

$ bg 1
[1]+ cp -a /media/backups/rsnapshot/hourly.0 /opt/restore &

Confirm the process is running with ps.

$ ps -O user --ppid $$
19496 root     D pts/11   00:00:06 cp -a /media/backups/rsnapshot/hourly.0 /opt/restore

Correct the file tool from reporting the mime-type of a Python script as text/x-java

When using the file tool to report the mime type of a file, your distributions file-libs package which ships the patterns that detect the mime type of the script maybe slightly out dated.

For example, take the python script below.

#!/usr/bin/env python

import sys

def main():
    print("hello world")

if __name__ == '__main__':

Running file over this python script will report a mime-type of text/x-java for older distributions such as CentOS6 (see bug report #9999).

$ file --mime-type helloworld.py 
helloworld.py: text/x-java

file will accept local magic data found in /etc/magic so by adding the following pattern below to /etc/magic, we should be able to have file report the correct mime-type for our hello world script.

0       regex    \^(\ |\t)*def\ +[a-zA-Z]+
>&0     regex   \ *\(([a-zA-Z]|,|\ )*\):$ Python script text executable
!:mime text/x-python
$ file --mime-type helloworld.py 
helloworld.py: text/x-python

Setup sudo with google-authenticator for 2 Factor authentication on CentOS 7.

By configuring the google-authenticator PAM module with sudo, you can force system users to have to authenticate with one-time passcode and their system password in order to use sudo.

From https://github.com/google/google-authenticator:

The Google Authenticator project includes implementations of one-time passcode generators for several mobile platforms, as well as a pluggable authentication module (PAM).

To set this up on CentOS7, we'll install the google-authenticator PAM module and update your server's PAM configuration.

First, install the tools required to build the google-authenticator PAM module.

# yum install -y git autoconf automake make libtool pam-devel

Clone the google-authenticator git repo, build and install the plugin.

# git clone https://github.com/google/google-authenticator.git
# cd google-authenticator/libpam
# ./bootstrap.sh
# ./configure
# make
# make install

This will install the google-authenticator binary and the pam_google_authenticator.so PAM module under /usr/local.

Before continuing, login as root and do not exit from this login whilst making changes to your system. A mistake could lock you out from your root account.

Add pam_google_authenticator.so to /etc/pam.d/sudo.

auth       required     /usr/local/lib/security/pam_google_authenticator.so forward_pass nullok
auth       include      system-auth
account    include      system-auth
password   include      system-auth
session    optional     pam_keyinit.so revoke
session    required     pam_limits.so

Its important that the path used to define pam_google_authenticator.so is correct or else PAM may not be able to find pam_google_authenticator.so and sudo will log an error.

Dec 21 09:25:34 server sudo: PAM unable to dlopen(/usr/lib64/security/pam_google_authenticator.so): /usr/lib64/security/pam_google_authenticator.so: cannot open shared object file: No such file or directory
Dec 21 09:25:34 server sudo: PAM adding faulty module: /usr/lib64/security/pam_google_authenticator.so

It is also important that the pam_google_authenticator.so is found before the 'auth include system-auth' line within /etc/pam.d/sudo.

Any user who will need to use sudo now needs to setup their secret key and google-authenticator settings which live in ~/.google_authenticator by simply running the google-authenticator binary on the server. They will be shown a QRCode that can be scanned into their two-factor authentication mobile app such as Authy or Google Authenticator.

Next time the user uses sudo, they will be asked for their system password and one-time passcode.

Once all your users who use sudo have setup their google-authenticator secret key, you should remove nullok from /etc/pam.d/sudo.

Use overlayfs as the storage driver in docker on CentOS 7.1

Ensure you have docker installed.

# yum install -y docker

Set the docker storage driver to overlay by editing /etc/sysconfig/docker-storage and setting the DOCKER_STORAGE_OPTIONS variable.

DOCKER_STORAGE_OPTIONS= --storage-driver=overlay

Restart docker.

# systemctl restart docker

Confirm that the storage driver is set to overlay and the backing filesystem is extfs.

# docker info

Storage Driver: overlay
 Backing Filesystem: extfs

Install and configure php5 fpm to run under a system user account using nginx as the web server on Ubuntu Trusty 14.04.

This tutorial will setup php5-fpm running under a system account and configure nginx to use the pool.

Whats php-fpm? From php-fpm.org:

PHP-FPM (FastCGI Process Manager) is an alternative PHP FastCGI
implementation with some additional features useful for sites of any
size, especially busier sites.

First setup a user which will run php5-fpm.

# adduser --system --shell=/bin/false \
    --home=/var/run/exampleuser \
    --ingroup nogroup \

Next, install the php5-fpm package.

# apt-get install --yes php5-fpm

Copy the default php-fpm www.conf pool file.

# cp /etc/php5/fpm/pool.d/www.conf \

Within the /etc/php5/fpm/pool.d/exampleuser.conf file, change the variable pool name to match the username.

; Start a new pool named 'www'.
; the variable $pool can we used in any directive and will be replaced by the
; pool name ('www' here)

Change the user and group variables to match the new username and group it belongs to.

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
user = exampleuser
group = nogroup

Change the socket path to be within the new users home directory.

listen = /var/run/exampleuser/php5-fpm.sock

Restart the php5-fpm service.

# service php5-fpm restart

nginx will need to make use of the php5-fpm socket file. Within the server nginx block add a location directive which points to the socket.

location ~\.php$ {
    fastcgi_split_path_info ^(.+\.php)(.*)$;
    fastcgi_pass    unix:/var/run/exampleuser/php5-fpm.sock;
    fastcgi_index   index.php;
    include         /etc/nginx/fastcgi_params;

Restart the nginx service.

# service nginx restart

Configure dnsmasq to query different nameservers for different domains

When connecting to a VPN you may have a DNS server which serves split horizon for a particular domain. For example when connected to your companies VPN, your local DNS config in /etc/resolv.conf is updated with:


The DNS server is your companies internal DNS server which resolves admin.example.org to You need to access admin.example.org on but don’t necessarily want to have all DNS queries go to You also don’t want manage /etc/hosts entries which can become stale over time.

dnsmasq a lightweight DNS and DHCP service can help.

Simply install dnsmasq, starting off with a simple config.


Create /etc/dnsmasq.d/example.org.conf.

  • line 1 returns for the host vpn.example.org.
  • line 2 specifies as the upstream DNS server for all other example.org queries such as admin.example.org.

Reload the dnsmasq service.

service dnsmasq restart

And finally update /etc/resolv.conf.


Now your local resolver clients will use dnsmasq as a DNS server with dnsmasq only forwarding queries for example.org to the upstream DNS server

Specify a different nameserver in /etc/resolv.conf when using DHCP in CentOS

When using dhcp within a ifcfg network configuration file you may want to specify a different nameserver instead of using the nameserver assigned by the DHCP server.

Use the PEERDNS, DNS1 and DNS2 options.


To check that it all works.

# ifdown eth0;ifup eth0
# grep ^nameserver /etc/resolv.conf

If you want to use the nameservers that are provided by DHCP server but also specify an extra nameserver within your /etc/resolv.conf, you can make use of dhclient.conf options in /etc/dhcp/dhclient-em1.cfg

prepend domain-name-servers;

Specify a different nameserver in /etc/resolv.conf when using DHCP in CentOS/RHEL/Fedora

When using dhcp within a ifcfg network configuration file you may want to specify a different nameserver instead of using the nameserver assigned by the DHCP server.

Use the PEERDNS, DNS1 and DNS2 options.


To check that it all works.

$ ifdown eth0;ifup eth0
$ grep ^nameserver /etc/resolv.conf

If you want to use the nameservers that are provided by DHCP server but also specify an extra nameserver within your /etc/resolv.conf, you can make use of dhclient.conf options.

prepend domain-name-servers;

Setup PAM authentication with OpenVPN's auth-pam module

The OpenVPN auth-pam module provides an OpenVPN server the ability to hook into Linux PAM modules adding a powerful authentication layer to OpenVPN.

Adding support to your existing OpenVPN server config is simple.

On the server, add the following to the OpenVPN config.

plugin /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn

For Ubuntu and Debian distributions, the path to the plugin is /usr/lib/openvpn/openvpn-plugin-auth-pam.so.

Create a new PAM service file located at /etc/pam.d/openvpn.

auth    required        pam_unix.so    shadow    nodelay
account required        pam_unix.so

On the client, add the following to the OpenVPN config.


Restart the OpenVPN server and that should be it. Any new OpenVPN connections will first be authenticated with pam_unix.so so the user will need a system account.

If the OpenVPN server exits with the log below after an authentication attempt, you most likely are running OpenVPN within a chroot and have not created a tmp directory.

Wed Sep 17 19:58:15 2014 us=704365 Could not create temporary file '/tmp/openvpn_acf_5a98375273019ab2d5d300dabcf91c91.tmp': No such file or directory

Simply create a tmp directory within the chroot with the permissions that match your OpenVPN server config.

$ grep -E "(^chroot|^user|^group)" /etc/openvpn/server.conf
chroot /var/lib/openvpn
user openvpn
group openvpn
$ mkdir --mode=0700 -p /var/lib/openvpn/tmp
$ chown openvpn:openvpn /var/lib/openvpn/tmp

Extending the OpenVPN PAM service

You can extend the use of PAM by adding to the /etc/pam.d/openvpn file.

auth    required        pam_unix.so    shadow    nodelay
auth    requisite       pam_succeed_if.so uid >= 500 quiet
auth    requisite       pam_succeed_if.so user ingroup wheel quiet
auth    required        pam_tally2.so deny=4 even_deny_root unlock_time=1200
account required        pam_unix.so
  • line 2 requires that the user's uid is greater than or equal to 500.
  • line 3 requires that the user belongs to the wheel group.
  • line 4 uses pam_tally2 to lock out the account after 4 consecutive unsuccessful authentication attempts. After 1200 seconds, the counter will reset back to 0.

Update the timezone in CentOS 6

The timezone a server is configured with can be shown with the date command.

$ date +%Z

To change the timezone in CentOS 6, first ensure you have the latest tzdata package installed.

$ yum install tzdata

Then update the /etc/localtime with the new timezone by copying the timezone file the tzdata package.

$ cp /usr/share/zoneinfo/UTC /etc/localtime
$ date +%Z