phpIPAM is an open-source web IP address management application (IPAM). Its goal is to provide light, modern and useful IP address management. It is php-based application with MySQL database backend, using jQuery libraries, ajax and HTML5/CSS3 features.


• IPv4/IPv6 IP address management

• Section / Subnet management

• Automatic free space display for subnets • Visual subnet display

• Automatic subnet scanning / IP status checks • PowerDNS integration

• NAT support • RACK management

• Domain authentication (AD, LDAP, Radius)

• Per-group section/subnet permissions

• Device / device types management

• RIPE subnets import

• XLS / CVS subnets import • IP request module


• Locations module

• VLAN management

• VRF Management – Virtual routing and forwarding (VRF) is a technology included in IP (Internet Protocol) network routers that allows multiple instances of a routing table to exist in a router and work simultaneously. This increases functionality by allowing network paths to be segmented without using multiple devices. Because traffic is automatically segregated, VRF also increases network security and can eliminate the need for encryption and authentication. Internet service providers (ISPs) often take advantage of VRF to create separate virtual private networks (VPNs) for customers; thus the technology is also referred to as VPN routing and forwarding. (Ne znam koliko nam je ovo bitno) • IPv4 / IPv6 calculator

• IP database search

• E-mail notifications


It’s presumed SELinux and firewall are disabled.Set locales:

more /etc/environment

Install all required packages

yum install httpd mariadb-server php php-cli php-gd php-common php-ldap php-pdo php-pear php-snmp php-xml php-mysql php-mbstring git
yum install epel-release
yum install php-mcrypt

Configuring Apache

Edit /etc/httpd/conf/httpd.conf

DocumentRoot "/var/www/html"
# Relax access to content within /var/www.
<Directory "/var/www">
    AllowOverride None
    # Allow open access:
    Require all granted
# Further relax access to the default document root:
<Directory "/var/www/html">
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    # The Options directive is both complicated and important.  Please see
    # for more information.
    Options FollowSymLinks
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   Options FileInfo AuthConfig Limit
    AllowOverride all
    Order allow,deny
    Allow from all

    # Controls who can get stuff from this server.
    #Require all granted

Set correct timezone to php.ini to avoid php warnings:

grep timezone /etc/php.ini
; Defines the default timezone used by the date functions
date.timezone = Europe/Belgrade

Start Apache and MariaDB

systemctl start httpd
systemctl enable httpd
systemctl start mariadb
systemctl enable mariadb

Harden MariaDB server:


Download PHP installation files and set correct permissions

cd /var/www/html/
git clone .
git checkout 1.4
chown apache:apache -R /var/www/html/
find . -type f -exec chmod 0644 {} \;
find . -type d -exec chmod 0755 {} \;
cp config.dist.php config.php

Installing phpIPAM

Open browser and type http://IP address – Click Automatic database installation

Then type MariaDB username/password (set when hardening MariDB)

On next screen set admin password, phpIPAM now should be installed, login to access GUI.

Email settings

Install SMTP PHP module

cd /var/www/html/
git submodule update --init --recursive

Set admin name and email address: Administration – phpIPAM settings

Administration – Mail Settings

Server type: SMTP

Server address:

Port: 587

Set username/password and admin email (set in previous step)

Creating Section

In a Section,you can organize yours subnets in differents folders.

Folder is a block or a group a subnets , like a folder on disk.
To create Section click on Administration – Sections

type name and set other options.

Creating VLANs

To create VLANS, L2 Domain needs to be created first (this is not necessary when creating VLANs via API calls)

Administration – VLAN

Add L2 Domain

Type name and select section

Type VLAN ID and description

Creating subnets

Administration – Subnets

Select section created in previous step

Create folder by clicking on folder icon

Type CIDR, select VLAN,nameserver…..

Set Check host status and Disover new hosts to Yes

Scanning subnet and discovering new hosts

Manually scan subnet:

/bin/php /var/www/html/functions/scripts/pingCheck.php
/bin/php /var/www/html/functions/scripts/discoveryCheck.php

Automatically scan subnets every 15 minutes- /etc/crontab

*/15 * * * * root /bin/php /var/www/html/functions/scripts/pingCheck.php
*/15 * * * * root /bin/php /var/www/html/functions/scripts/discoveryCheck.php


Enable API (Administration – phpIPAM settings)

In this example HTTP access is used so we must enable http support in /var/www/htmp/config.php file

$api_allow_unsafe = true;

Create API ID

Type:User token

Set App Permission

Get token

yum install jq -y
curl -X POST --user admin:pass http://localhost/api/myapi/user/ | jq "."
  "code": 200,
  "success": true,
  "data": {
    "token": "token",
    "expires": "2019-09-16 15:15:55"
  "time": 0.015

Now we can use token to search for data or to create new objects

Following methods are available:

  • GET – Reads object(s) details and returns it in requested format
  • POST – Creates new object
  • PUT – Changes object values
  • PATCH – Alias to PUT method
  • DELETE – Deletes an object

Following objects (controllers are available)

  • Sections
  • Subnets
  • Folders
  • VLANs
  • Addresses
  • L2 domains
  • VRFs
  • Devices
  • Tools
  • Prefix

Request structure:


Searching objects

Searching subnets

curl -X GET --header "token: token" | jq '.'

Searching VLANs

curl -X GET --header "token: token" | jq '.'

Search for specific section

curl -X GET  "" --header "token: token" | jq '.'

Deafult REST API output is in JSON format, if output is too lengthy, it can be tedious and troublesome to scroll whole output in PuTTY windows, so maybe better approach would be using Postman. Make sure, in Header section to create key named token and in Value paste token.

Creating VLAN

Creating VLAN 88

curl -X POST --header 'token: token' --header "Content-Type: application/json" http://localhost/api/myapi/vlans/ --data '{"number": "88","name": "vlan_88","description": "VLAN 88"}' | jq "." 

To execute same API in Postman: Import- Paste Raw Text – Paste same command as in previous example.

Create new subnet:

In this example subnet is created in Devtech section (SectionID:4) and assigned to VLAN 87 (vlanId:4)
To get Section and VLan Id, first run GET API for subnets and vlan controllers to get those IDs

curl -X POST --header 'token: MOOG3gikXMPF9htSjY56S-1i' --header "Content-Type:application/json" http://localhost/api/myapi/subnets/ --data '{"subnet": "","sectionId": 1,"description": "Test","masterSubnetId": 0,"mask": 24,"vlanId":"4"}' | jq "."

After packaging VBox VM

vagrant package --base ""
vagrant box add mypackagedbox

, and after provisioning exported/packaged machine,
i started getting errors:”Warning: Authentication failure. Retrying… ”

The solution (at least for me), was specifying config.ssh.insert_key = false on both Vagrantfile (when provisioning “original” and “packaged” VM).

  1. log in to original box and fill disk empty space with zeroes
yum -y clean all
 rm -rf /var/cache/yum
 dd if=/dev/zero of=/EMPTY bs=1M
 rm -f /EMPTY

2. shutdown the VM

shutdown -h 0

3. delete file insecure_private_key from Vagrant directory


4. export the box

vagrant package --base     vm_id_as_it_is_in_virtualbox --output box_file_name

This script will check if current IP is in specific range

Function IsIpAddressInRange {
        [string] $ipAddress,
        [string] $fromAddress,
        [string] $toAddress

    $ip = []::Parse($ipAddress).GetAddressBytes()
    $ip = [system.BitConverter]::ToUInt32($ip, 0)

    $from = []::Parse($fromAddress).GetAddressBytes()
    $from = [system.BitConverter]::ToUInt32($from, 0)

    $to = []::Parse($toAddress).GetAddressBytes()
    $to = [system.BitConverter]::ToUInt32($to, 0)

    $from -le $ip -and $ip -le $to

# get current IP and perform comparation
$ip1 = ((ipconfig | findstr [0-9].\.)[0]).Split()[-1]

if (IsIpAddressInRange $ip1 "" "")
Write-Host "in Corporate Network"
Write-Host "in private network"

I have couple of Hyper-V hosts, and using function below i’m getting CPU,Memory and disk utilization for each of them, so i can decide on which one to create new VM.

function Get-Resources{  
                 $computername =$env:computername  
                 # Processor utilization 
                 #Get-WmiObject -ComputerName $computer -Class win32_processor -ErrorAction Stop | Measure-Object -Property LoadPercentage -Average | Select-Object * 
                $cpu = gwmi win32_perfformatteddata_perfos_processor -ComputerName $computername| ? {$ -eq "_total"} | select -ExpandProperty PercentProcessorTime  -ea silentlycontinue  
                 # Memory utilization 
                 $ComputerMemory = Get-WmiObject -ComputerName $computername  -Class win32_operatingsystem -ErrorAction Stop 
                 $Memory = ((($ComputerMemory.TotalVisibleMemorySize - $ComputerMemory.FreePhysicalMemory)*100)/ $ComputerMemory.TotalVisibleMemorySize) 
                 $RoundMemory = [math]::Round($Memory, 2) 
                 # Free disk space 
                 $disks = get-wmiobject -class "Win32_LogicalDisk" -namespace "root\CIMV2" -computername $computername 
                 $results = foreach ($disk in $disks)  
                 if ($disk.Size -gt 0) 
                   $size = [math]::round($disk.Size/1GB, 0) 
                   $free = [math]::round($disk.FreeSpace/1GB, 0) 
                   Drive = $disk.Name 
                   Name = $disk.VolumeName 
                   "Total Disk Size" = $size 
                   "Free Disk Size" = "{0:N0} ({1:P0})" -f $free, ($free/$size) 
                   } } }     

                   # Write results 
                   Write-host "Resources on" $computername "- RAM Usage:"$RoundMemory"%, CPU:"$cpu"%, Free" $free "GB" 
I Decided to use script block to run this function on remote machines
1..5 | % {

Invoke-Command -ScriptBlock  ${Function:Get-Resources} -ComputerName server$_ 

Visual Studio Code (aka VS Code ) is “a lightweight but powerful source code editor which runs on your desktop and is available for Windows, macOS and Linux”.It is half-way between an text editor and an IDE. Main reasons for using Visual Studio Code

  • It comes with a built-in support for Javascript, TypeScript, nodeJs (auto-completion, syntax check, debug, …) , and according to Slant – 12 Best IDEs for TypeScript development as of 2019 it has the best typescript support
  • It has a great ecosystem of plugins for supporting other languages (C, C++, C#, Python, …), you can even install keymaps from text editors like sublime text, atom, vim
  • It is cross-platform :  Windows, Mac or Linux

In this post we’ll be installing Visual Studio code on Windows 10,open and execute Python script on remote linux box.

Creating SSH connection between Windows 10 and CentOS 7

Visual Studio Code uses SSH key-pair to connect to linux box.

So we’ll create key-pair on Windows 10 and copy Windows 10 public key to /~/.ssh/authorized_key file

Open Command prompt on Windows 10 and create keys.



On CentOS 7 create ~/.ssh/authorized_keys file, set appropriate permissions and copy content of public key Windows 10 file to ~/.ssh/authorized_keys</code

mkdir /root/.ssh
chmod -R 700 /root/.ssh/
vi /root/.ssh/authorized_keys
# copy content of your public key file to authorized_keys file
chmod 600 /root/.ssh/authorized_keys
systemctl restart sshd

Test ssh connection from Windows 10 to Linux

Open CMD and type

ssh -i c:\Users\user\.ssh\id_rsa root@

Install Visual Studio Code on Windows 10

Once installed, click on “Cog” button – extensions


Type Remote – SSH to install this extension – click on Install


Now, click again on “Cog” – Command Pallete


Type Remote – SSH: Open Configuration File


Select configuration file located in your User Profile


Change alias in some more descriptive, set IP address as hostname, user and path to private key, then save file


Now click green button (far bottom left) – select alias we set in configuration file


Connection to Linux should be established (Connected to), click Open folder, select desired folder – click OK


Now, open existing .py file (File – Open) or create new one (File – New File Save as .py)

Click debug – Add Configuration


Python extension will be offered for installation – Install Python extension


Select python interpreter (2 or 3 – it depends if one is installed on Linux box), choose whether


Click again on Debug icon – Add Configuration – Select Python File


Select interpreter





In this article we’ll use Apache alias directive to host multiple Laravel PHP sites using single IP

Accesing “development” web site


Accessing “staging” web site


Installing Apache and PHP 7.2

yum install epel-release

yum install

yum install yum-utils

yum-config-manager --enable remi-php72

yum install php72 php72-php php72-php-fpm php72-php-gd php72-php-json php72-php-mbstring php72-php-mysqlnd php72-php-xml php72-php-xmlrpc php72-php-opcache

yum enable httpd && yum start httpd 

Edit /etc/httpd/config.d/ssl.conf

Listen 443 https

SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog
SSLSessionCache         shmcb:/run/httpd/sslcache(512000)
SSLSessionCacheTimeout  300

SSLRandomSeed startup file:/dev/urandom  256
SSLRandomSeed connect builtin
SSLCryptoDevice builtin

<VirtualHost *:443>

# General setup for the virtual host, inherited from global configuration
DocumentRoot "/var/www/html"


ErrorLog /var/log/httpd/ssl_error_log
TransferLog logs/ssl_access_log
LogLevel warn

<Directory "/var/www/html/develop/public">
             SetEnv development
             DirectoryIndex index.php
             Options FollowSymLinks
             AllowOverride All
             Order allow,deny
             Allow from all

Alias "/development" "/var/www/html/develop/public"

<Directory "/var/www/html/staging/public">
             SetEnv staging
             DirectoryIndex index.php
             Options FollowSymLinks
             AllowOverride All
             Order allow,deny
             Allow from all

Alias "/staging" "/var/www/html/staging/public"

CustomLog /var/log/httpd/development_log combined env=development
CustomLog /var/log/httpd/staging_log combined env=staging

SSLEngine on

SSLProtocol all -SSLv2 -SSLv3


SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

SSLCertificateFile /etc/pki/tls/certs/localhost.crt

<Files ~ "\.(cgi|shtml|phtml|php3?)$">
    SSLOptions +StdEnvVars
<Directory "/var/www/cgi-bin">
    SSLOptions +StdEnvVars

BrowserMatch "MSIE [2-5]" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0

CustomLog logs/ssl_request_log \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"


Edit /var/www/html/develop/public/.htaccess for “development” site

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes

    Options +FollowSymLinks

    RewriteEngine On
    RewriteBase "/development/"

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
   # RewriteRule ^ %1 [L,R=301]

   RewriteRule ^(.*)/$ /$1 [L,R=301]

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f

    RewriteCond $1 !^index\.php/
    #RewriteRule ^(.*)$ index.php/$1 [L]

    RewriteRule ^ index.php [L]


Edit /var/www/html/staging/public/.htaccess file

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes

    Options +FollowSymLinks

    RewriteEngine On
    RewriteBase "/staging/"

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
   # RewriteRule ^ %1 [L,R=301]

   RewriteRule ^(.*)/$ /$1 [L,R=301]

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f

    RewriteCond $1 !^index\.php/
    #RewriteRule ^(.*)$ index.php/$1 [L]

    RewriteRule ^ index.php [L]


In last post we configured rsync on CentOS 7, in this one we’ll monitor rsync service on server.In Nagios terminology this script is called plugin, in this example we’ll be using simple Perl script to check if rsync daemon is running and it takes only 1 parameter: hostname/IP of mashine on which rsync is running,this plugin is using rsync command, so it must be installed also on the Nagios server.

Steps on Nagios core:

Download plugin and make it executable:

cd /usr/local/nagios/libexec/
wget '' -O check_rsync
chown nagios:nagios check_rsync
chmod +x check_rsync

Define command in /usr/local/nagios/etc/objects/commands.cfg

$USER1$ : This is macro for /usr/local/nagios/libexec , the location of the
plugin binaries, including check_rsync . It is defined in the sample configuration
in the file /usr/local/nagios/etc/resource.cfg .
$HOSTADDRESS$ : address of host for which this command
is used as a host or service definition.

define command {
command_name check_rsync
command_line $USER1$/check_rsync -H $HOSTADDRESS$

Edit monitoring host file /usr/local/nagios/etc/objects/hosts.cfg and add service block, here we specified host_name and command specified in commands.cfg file (define command)

define service {

      use                       generic-service
      host_name                 linux-box
      service_description       RSYNC
      check_command             check_rsync

Now restart nagios service and new service should appear


 Monitoring Rsync cron job

Cron job to monitor:

rsync -avm --include='dist-[0-9].[0-9].tar' --exclude='*' /data/ 2> &1 > /var/log/important_rsync_job.log; echo "Exit code: $?" >> /var/log/important_rsync_job.log

This Nagios plugin monitors rsync  log file created by cron and  accept time parameters (in minutes),  during which  log file shouldn’t not be modified, if log file is not modified even after specified minutes in time, raise an alert. Script also takes log file path parameter.

I found this perl script on the internet.

#!/usr/bin/env perl

use warnings;
use strict;
use Getopt::Std;
use IO::File;

my $plugin_name = "Nagios log check";
my $VERSION = "1.0";

use constant EXIT_OK => 0;
use constant EXIT_WARNING => 1;
use constant EXIT_CRITICAL => 2;
use constant EXIT_UNKNOWN => 3;

my %opts;
getopts('f:t:', \%opts);
if (not (defined $opts{f} ) or not (defined $opts{t} )) {
print "ERROR: invalid usage\n";

unless (-e $opts{f}) {
print "ERROR: $opts{f} not found";

my $status = EXIT_OK;
my $file_mod_time_in_days = -M $opts{f};
my $file_mod_time_in_minutes = sprintf( "%.2f", ($file_mod_time_in_days * 24 * 60) );

if ( $file_mod_time_in_minutes > $opts{t} ) {
print "ERROR: $opts{f} last modified $file_mod_time_in_minutes minutes, threshold set to $opts{t} minutes\n";

my $fh = IO::File->new($opts{f}, O_RDONLY)
or die 'Could not open file for reading.';

my $command_running = 1;

while( my $line =  ) {
if ( $line =~ /Exit code:/ ) {
if ( not $line =~ /Exit code: 0/ ) {
$command_running = 0;

close $fh;

if ($command_running == 1) {

print "Command currently running...";
} else {

print "Command completed successfully about $file_mod_time_in_minutes minutes ago.\n";


exit $status;


my $error_codes = {

my $unparsed_exit_code;
$unparsed_exit_code = $_[0];

my @split_exit_code = split(" ", $unparsed_exit_code);
my $exit_code = $split_exit_code[-1];
print "Exit code $exit_code: ";
print $error_codes->{$exit_code} || "UNKNOWN ERROR";

print <<EOHELP
Check log output has been modified with t minutes and contains "Exit code: 0".
If "Exit code" is not found, assume command is running.
Check assumes the log is truncated after each command is run.

--help shows this message
--version shows version information
-f path to log file
-t Time in minutes which a log file can be unmodified before raising CRITICAL alert

print <<EOVM $plugin_name v. $VERSION
Copyright 2012, Brian Buchalter - Licensed under GPLv2 EOVM ; }

Installing NRPE on monitored machine (rsync server)

Above perl script will be executed periodically from Nagios server, so we need to install NRPE package on monitored server.Nagios Remote Plugin Executor – NRPE, allows Nagios server to remotely execute plugins/commands on Linux/Unix machines and get the result back of the executed command.

yum install epel-release -y
yum install nrpe check_nrpe -y
systemctl enable nrpe && systemctl start nrpe

Save above perl script as rsync to /usr/lib64/nagios/plugins/ and make it executable

Test it:

/usr/lib64/nagios/plugins/rsync -f /var/log/important_rsync_job.log -t 10000

Output should be similar to following:

Command completed successfully about 1334.60 minutes ago.

Edit /etc/nagios/nrpe.cfg:

change server address to IP of your monitored host


in allowed hosts specify IP address of Nagios server and monitored machine


Define check by submitting above script to NRPE

add following line:

command[rsync]=/usr/lib64/nagios/plugins/rsync -f /var/log/important_rsync_job.log -t 60

restart NRPE service and check if service is running:

systemctl restart nrpe
ss -altn | grep 5666
LISTEN   0         5            *
LISTEN   0         5                      [::]:5666                [::]:*

Test it:

/usr/lib64/nagios/plugins/check_nrpe -H -c rsync
Command completed successfully about 10 minutes ago

Execute same command from Nagios server, in case of any issue check logs on monitoring machine (/var/log/messages) and test port 5666 availability from Nagios server

Configuring Nagios

Now we need to configure Nagios to monitor rsync cron job on remote machine:

edit  /usr/local/nagios/etc/objects/commands.cfg

define command {

command_name check_rsync_job

command_line /usr/lib64/nagios/plugins/check_nrpe -H -c $ARG1$

Edit monitoring host file /usr/local/nagios/etc/objects/hosts.cfg

define service{
     use                       local-service
     host_name                 linux-box
     service_description       Rsync Cron job monitoring
     check_command             check_nrpe!rsync

! mark separates the command from the arguments in the check_command entry. This defines that check_nrpe is the command and rsync is the value of $ARG1$.

Restart nagios service on Nagios server and ncpa service on monitored host:

service ncpa_listener restart

New service should appear