<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Linux &#8211; Johnny Morano&#039;s Tech Articles</title>
	<atom:link href="https://jmorano.moretrix.com/category/linux/feed/" rel="self" type="application/rss+xml" />
	<link>https://jmorano.moretrix.com</link>
	<description>Ramblings of an old-fashioned space cowboy</description>
	<lastBuildDate>Wed, 08 Jun 2022 06:35:06 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>

<image>
	<url>https://jmorano.moretrix.com/wp-content/uploads/2022/04/cropped-jmorano_emblem-32x32.png</url>
	<title>Linux &#8211; Johnny Morano&#039;s Tech Articles</title>
	<link>https://jmorano.moretrix.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Using multipath together with mdadm on Debian</title>
		<link>https://jmorano.moretrix.com/2022/06/using-multipath-together-with-mdadm-on-debian/</link>
					<comments>https://jmorano.moretrix.com/2022/06/using-multipath-together-with-mdadm-on-debian/#respond</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Wed, 08 Jun 2022 06:35:04 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Debian]]></category>
		<category><![CDATA[Fiber Channel]]></category>
		<category><![CDATA[mdadm]]></category>
		<category><![CDATA[multipath]]></category>
		<category><![CDATA[SysAdmin]]></category>
		<guid isPermaLink="false">https://jmorano.moretrix.com/?p=1533</guid>

					<description><![CDATA[Using multipath together with mdadm on Debian Linux requires some changes to the initrd image, otherwise mdadm might&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Using multipath together with mdadm on Debian Linux requires some changes to the initrd image, otherwise mdadm might start before multipath, leading to only one FC path being used or even failure of starting multipath (no FC paths found at all).</p>



<p>To prevent this issue, three steps are required:</p>



<ul class="wp-block-list"><li>change the order kernel modules get loaded by initrd</li><li>disable the mdadm auto-assembly of disks when initrd is loaded</li><li>generate a new initrd image </li></ul>



<p>The initrd configuration files can be found at <code>/etc/initramfs-tools</code>.</p>



<p>First define the order of module loading during boot (ensure that <code>multipath</code> is loaded before <code>mdadm</code>). Edit the file <code>/etc/initramfs-tools/modules</code> to match:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># List of modules that you want to include in your initramfs.
# They will be loaded at boot time in the order below.
#
# Syntax: module_name [args ...]
#
# You must run update-initramfs(8) to effect this change.
#
# Examples:
#
# raid1
# sd_mod
libfc
megaraid_sas
scsi_dh_alua
scsi_transport_fc
dm_multipath
dm_service_time
multipath
md_mod</pre>



<p>This starts by loading the &#8220;fiber channel libs&#8221; (<code>libfc</code>) first, followed by the MegaRAID modules, SCSI over fiber channel, <code>multipath</code> and <code>mdadm</code>.</p>



<p>Now copy the mdadm initramfs hook to <code>/etc</code> to override the default one:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">cp /usr/share/initramfs-tools/hooks/mdadm /etc/initramfs-tools/hooks/</pre>



<p>Edit the hook file in <code>/etc/initramfs-tools/hooks/mdadm </code>and add an &#8216;<code>exit 0</code>&#8216; (see line 10):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">#!/bin/sh
#
# Copyright © 2006-2008 Martin F. Krafft &lt;madduck@debian.org>,
#
2012 Michael Tokarev &lt;mjt@tls.msk.ru>
# based on the scripts in the initramfs-tools package.
# released under the terms of the Artistic Licence.
#
set -eu
exit 0
PREREQ="udev"
prereqs()
{
echo "$PREREQ"
}</pre>



<p>Disable automatic startup of the <code>mdadm</code> service:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">rm /lib/systemd/system/mdadm.service
systemctl daemon-reload
systemctl disable mdadm.service</pre>



<p>Finally, update the initrd images for all kernel versions installed:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">update-initramfs -u -k all</pre>



<p>Reboot the server to see if modules are now loaded in the correct order, and multipath is loaded and started before mdadm.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2022/06/using-multipath-together-with-mdadm-on-debian/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Read the HAProxy UNIX socket file using Perl</title>
		<link>https://jmorano.moretrix.com/2022/04/read-the-haproxy-unix-socket-file-using-perl/</link>
					<comments>https://jmorano.moretrix.com/2022/04/read-the-haproxy-unix-socket-file-using-perl/#respond</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Mon, 25 Apr 2022 10:52:45 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[HAProxy]]></category>
		<category><![CDATA[Monitoring]]></category>
		<guid isPermaLink="false">https://jmorano.moretrix.com/?p=1515</guid>

					<description><![CDATA[HAProxy provides a socket file which can be used to do maintenance (enable/ disable backends, retrieve information and&#8230;]]></description>
										<content:encoded><![CDATA[
<p><a rel="noreferrer noopener" href="http://www.haproxy.org/" data-type="URL" data-id="http://www.haproxy.org/" target="_blank">HAProxy</a> provides a <a href="http://docs.haproxy.org/2.5/management.html#9.3" data-type="URL" data-id="http://docs.haproxy.org/2.5/management.html#9.3" target="_blank" rel="noreferrer noopener">socket file</a> which can be used to do maintenance (enable/ disable backends, retrieve information and statistics, &#8230;).</p>



<p>The statistics part contains quite some interesting information for monitoring and alerting.</p>



<p>The below Perl code snippit will loop over a <code>glob</code> of socket files (for instance when you have multiple HAProxy configurations running as separate processes) and print out the values returned by the &#8220;<code>show info</code>&#8221; command.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">use IO::Socket::UNIX;

foreach my $socket_file (glob("/run/haproxy/*.sock")){
    print "- Reading socket: $socket_file\n";
    my $client = IO::Socket::UNIX->new(
        Type => SOCK_STREAM(),
        Peer => $socket_file,
    );

    print "- show info\n";
    print $client "show info\n";
    my $header = &lt;$client>;
    chomp($header);

    $header =~ s/^#\s+//;
    my @keys = split ',', $header;
    print "- header:$header\n";

    while (my $line = &lt;$client>){
        next unless $line =~ /^.+/;

        chomp($line);
        my @values = split ',', $line;
        print " - Got $line\n";
        print "   $keys[$_]: ".($values[$_]//'')."\n" foreach 0..$#keys;
    }

    close $client;
}</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2022/04/read-the-haproxy-unix-socket-file-using-perl/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Managing LDAP passwords with Perl</title>
		<link>https://jmorano.moretrix.com/2022/04/managing-ldap-passwords-with-perl/</link>
					<comments>https://jmorano.moretrix.com/2022/04/managing-ldap-passwords-with-perl/#respond</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Mon, 25 Apr 2022 09:30:40 +0000</pubDate>
				<category><![CDATA[Automation]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[OpenmLDAP]]></category>
		<category><![CDATA[SysAdmin]]></category>
		<guid isPermaLink="false">https://jmorano.moretrix.com/?p=1511</guid>

					<description><![CDATA[OpenLDAP Software is an open source implementation of the Lightweight Directory Access Protocol. Many graphical interfaces are available&#8230;]]></description>
										<content:encoded><![CDATA[
<p><a href="https://openldap.org/" data-type="URL" data-id="https://openldap.org/" target="_blank" rel="noreferrer noopener">OpenLDAP</a> Software is an open source implementation of the Lightweight Directory Access Protocol.</p>



<p>Many graphical interfaces are available for managing user accounts in OpenLDAP like PHPLDAPAdmin (<a rel="noreferrer noopener" href="http://phpldapadmin.sourceforge.net/wiki/index.php/Main_Page" target="_blank">http://phpldapadmin.sourceforge.net/wiki/index.php/Main_Page</a>) or LAM (<a rel="noreferrer noopener" href="https://www.ldap-account-manager.org/lamcms/" target="_blank">https://www.ldap-account-manager.org/lamcms/</a>).</p>



<p>When generating a bulk amount of accounts with automation or just managing user details with a simple script, allows much more flexibility and can be even quicker.</p>



<p>LDAP passwords can be stored or changed by using an LDIF file. This LDIF file needs 3 required lines:</p>



<ol class="wp-block-list"><li>The &#8220;<code>dn</code>&#8221; you are about to change</li><li>the &#8220;<code>changetype</code>&#8221; set to &#8220;<code>modify</code>&#8220;</li><li>A &#8220;<code>replace</code>&#8221; line containing the field you want to change (in our case, since we are changing the password, this will be &#8220;<code>userPassword</code>&#8220;)</li></ol>



<p>Your LDAP password can be stored either in clear-text (which is not advisable) or by sending a <code>SHA-hash</code>. The <code>SHA-hash</code> must include the salt at the end and must be <code>base64</code> encoded.</p>



<p>The code snippit below will call a subroutine called <code>generate_password()</code> which comes from a previous article (<a href="https://jmorano.moretrix.com/2013/08/secure-password-generator-perl/" data-type="post" data-id="953">Secure Password Generator in Perl</a>).</p>



<p>At the end of the script, it will print out the LDIF file content, which needs to be saved to <code>change.ldif</code>. As last, it will print the <code>ldapmodify</code> command to make the actual change. You will need to know the <code>admin</code> password for this. Alternatively, you could also make this change using your own <code>dn</code> for authentication.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">use Digest::SHA;
use MIME::Base64;

my $random_password = generate_password(24);
my $random_salt     = generate_password(3);

my $ctx = Digest::SHA->new;
$ctx->add($random_password);
$ctx->add($random_salt);
my $hashedPasswd = encode_base64($ctx->digest . $random_salt, '');

print "password: $random_password\n";
print "salt: $random_salt\n";
print &lt;&lt;EOF;
# LDIF
dn: uid=user1,ou=users,dc=shihai-corp,dc=at
changetype: modify
replace: userPassword
userPassword: {SSHA}$hashedPasswd
EOF

print "\n";
print q{LDAP cmd: ldapmodify -H "ldap://ldap_server01" -Z -x -W -D "cn=ldapadmin,ou=admins,dc=shihai-corp,dc=at" -f change.ldif} . "\n\n"</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2022/04/managing-ldap-passwords-with-perl/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Import configuration from Hiera or a Git repository with YAML files into Terraform</title>
		<link>https://jmorano.moretrix.com/2022/04/import-configuration-from-hiera-or-a-git-repository-with-yaml-files-into-terraform/</link>
					<comments>https://jmorano.moretrix.com/2022/04/import-configuration-from-hiera-or-a-git-repository-with-yaml-files-into-terraform/#comments</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Tue, 05 Apr 2022 11:26:43 +0000</pubDate>
				<category><![CDATA[Automation]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Terraform]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[Hashicorp]]></category>
		<category><![CDATA[YAML]]></category>
		<guid isPermaLink="false">https://jmorano.moretrix.com/?p=1393</guid>

					<description><![CDATA[De-duplication of configuration information is key when managing large environments which use different types of automation (Terraform, Jenkins,&#8230;]]></description>
										<content:encoded><![CDATA[
<p>De-duplication of configuration information is key when managing large environments which use different types of automation (<a rel="noreferrer noopener" href="https://www.terraform.io/" data-type="URL" data-id="https://www.terraform.io/" target="_blank">Terraform</a>, <a rel="noreferrer noopener" href="https://www.jenkins.io/" data-type="URL" data-id="https://www.jenkins.io/" target="_blank">Jenkins</a>, <a rel="noreferrer noopener" href="https://www.ansible.com/" data-type="URL" data-id="https://www.ansible.com/" target="_blank">Ansible</a>, scripts executed as <a rel="noreferrer noopener" href="https://systemd.io/" data-type="URL" data-id="https://systemd.io/" target="_blank">Systemd</a> timers, <a rel="noreferrer noopener" href="https://puppet.com/" data-type="URL" data-id="https://puppet.com/" target="_blank">Puppet</a>&#8230;). Although many different configuration management tools exist (RDBMS, <a rel="noreferrer noopener" href="https://www.consul.io/" data-type="URL" data-id="https://www.consul.io/" target="_blank">Consul</a>, &#8230;), one of the easiest to use is <a href="https://puppet.com/docs/puppet/7/hiera_intro.html" data-type="URL" data-id="https://puppet.com/docs/puppet/7/hiera_intro.html" target="_blank" rel="noreferrer noopener">Hiera</a> or just a plain normal <a rel="noreferrer noopener" href="https://git-scm.com/" data-type="URL" data-id="https://git-scm.com/" target="_blank">Git</a> repository with <code><a rel="noreferrer noopener" href="https://yaml.org/" data-type="URL" data-id="https://yaml.org/" target="_blank">YAML</a></code> files, in some hierarchical way (which Hiera in theory is).</p>



<p> The YAML configuration hierarchy could be defined as the following file structure:</p>



<ul class="wp-block-list"><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">common.yaml</mark></code>: Default settings no matter which role or host. These can be overridden in all the below.</li><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">my_environment01.yaml</mark></code>: Environment specific configuration (example: development, staging, production, amsterdam, az01, az04, &#8230;). These can be overridden in all the below.</li><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">common/roles/some_server_role.yaml</mark></code>: A server role, or type definition, which contains role specific configuration parameters. The roles could implement an extra hierarchy as for instance:<br /><ul><li><code>debian::databases::postgres</code></li><li><code>debian::databases::postgres::timescale</code></li><li><code>debian::databases::postgres::timescale::prometheus</code></li><li><code>debian::loadbalancer::internal</code></li><li><code>debian::application::request_processor</code><br /><br />The hierarchy steps are divided by<code> :: </code>in the above example, and need to be inherited accordingly, each with their own YAML file.<br />These can be overridden in all the below.</li></ul></li><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">my_environment01/roles/some_server_role.yaml</mark></code>: override role configuration parameters per environment.<br />These can even be overridden on host level below.</li><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">my_environment01/hosts/my_hostname01.yaml</mark></code>: set host specific configuration parameters. This file is actually always required and should contain at least the IP address of the node and the server role string.</li></ul>



<p>Let&#8217;s take the following example: The host <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">vmazdbprm01</mark></code> has the role <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color"><code>debian::databases::postgres::timescale::prometheus</code> </mark>and is deployed in the environment in <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">my_cool_location03</mark></code>. The configuration management should search for parameters in the following file locations (and first verify if the file path exists):</p>



<ol class="wp-block-list"><li><code>common.yaml</code></li><li><code>my_cool_location01.yaml</code></li><li><code>common/roles/debian.yaml</code></li><li><code>common/roles/debian::databases.yaml</code></li><li><code>common/roles/debian::databases::postgres.yaml</code></li><li><code>common/roles/debian::databases::postgres::timescale.yaml</code></li><li><code>common/roles/debian::databases::postgres::timescale::prometheus.yaml</code></li><li><code>my_cool_location01/roles/debian.yaml</code></li><li><code>my_cool_location01/roles/debian::databases.yaml</code></li><li><code>my_cool_location01/roles/debian::databases::postgres.yaml</code></li><li><code>my_cool_location01/roles/debian::databases::postgres::timescale.yaml</code></li><li><code>my_cool_location01/roles/debian::databases::postgres::timescale::prometheus.yaml</code></li><li><code>my_cool_location01/hosts/vmazdbprm01.yaml</code></li></ol>



<p>This means that any code which wants to implement the above configuration management, needs to verify if the above <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">13 files</mark> exists from top to bottom, and if yes loads the YAML file accordingly.</p>



<p>Terraform is an open-source infrastructure as code software tool that provides a consistent CLI workflow to manage hundreds of cloud services.</p>



<p>Implementing the above <code>YAML</code> hierarchy in Terraform, could be done as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">locals {
  host_cfg             = yamldecode(fileexists("cfgmgmt/${var.environment}/hosts/${var.node}.yaml") ? file("cfgmgmt/${var.environment}/hosts/${var.node}.yaml") : "{server_role: debian}")

  roles_list           = split("::", local.host_cfg.server_role)
  all_roles_list       = [ for index in range(length(local.roles_list)): join("::",slice(local.roles_list, 0, index + 1))  ]

  common_cfg           = yamldecode(fileexists("cfgmgmt/common.yaml") ? file("cfgmgmt/common.yaml") : "{}")
  common_role_cfg_list = [ for file in local.all_roles_list:
      yamldecode(fileexists("cfgmgmt/common/roles/${file}.yaml") ? file("cfgmgmt/common/roles/${file}.yaml") : "{}" )]
    
  env_cfg              = yamldecode(fileexists("cfgmgmt/${var.environment}.yaml") ? file("cfgmgmt/${var.environment}.yaml") : "{}")
  env_role_cfg_list    = [ for file in local.all_roles_list:
      yamldecode(fileexists("cfgmgmt/${var.environment}/roles/${file}.yaml") ? file("cfgmgmt/${var.environment}/roles/${file}.yaml") : "{}") ]
        
  common_role_cfg_map  = merge(local.common_role_cfg_list...)
  env_role_cfg_map     = merge(local.env_role_cfg_list...)

  cfg                  = merge(local.common_cfg, local.env_cfg, local.common_role_cfg_map, local.env_role_cfg_map, local.host_cfg)
}</pre>



<p>Let&#8217;s have a look what actually happens in the above code.</p>



<p>All YAML files are stored in a Git/ Hiera repository, accessible in the sub-directory <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">cfgmgmt</mark></code>.</p>



<p>The code declares &#8220;<code>local</code>&#8221; variables by issuing the resource &#8220;<code><a rel="noreferrer noopener" href="https://www.terraform.io/language/values/locals" data-type="URL" data-id="https://www.terraform.io/language/values/locals" target="_blank">locals</a></code>&#8220;, starting from <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">line 1</mark>.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 2</mark> will check if a file called <code>cfgmgmt/${var.environment}/hosts/${var.node}.yaml</code> exists and if <code>true</code>, loads the <code>YAML</code> content as an map into the local variable <code>host_cfg</code>. If the file doesn&#8217;t exists, a default <code>YAML</code> code will be loaded. In theory, each node/ host must have a file defined as it should have at least data configuration such as:</p>



<ul class="wp-block-list"><li>unique node host name</li><li>IP address</li><li>server role</li><li>(optionally) VLAN/ subnet configuration</li><li>&#8230;</li></ul>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 4</mark> splits the server role string, stored in <code>local.host_cfg.server_role</code>, into a list, to build the server role hierarchy further below.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 5 </mark>creates a list of top level server roles which need to be imported too. Example: if the server role was set to <code>debian::databases::postgres::timescale::prometheus</code> , the list <code>all_roles_list</code> will contain the following elements:</p>



<ul class="wp-block-list"><li><code>debian</code></li><li><code>debian::databases</code></li><li><code>debian::databases::postgres</code></li><li><code>debian::databases::postgres::timescale</code></li><li><code>debian::databases::postgres::timescale::prometheus</code></li></ul>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 7</mark> loads the YAML content of <code>common.yaml</code>, if it exists.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 8</mark> loops over the <code>all_roles_list</code> elements, created on <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">line 5</mark>, and will load the YAML content of the server roles (if the file exists) into a list element. The result is a list called <code>common_role_cfg_list</code>.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 11</mark> loads the general environment configuration YAML content (if it exists) into the local variable <code>env_cfg</code>.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 12 </mark>will do the same thing as<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color"> line 8</mark>, but for environment specific roles. (for instance: when certain server roles have environment specific configuration parameters).</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Lines 15</mark> and <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">16</mark> will merge the elements (which in theory are <code><a rel="noreferrer noopener" href="https://www.terraform.io/language/values/variables#map" data-type="URL" data-id="https://www.terraform.io/language/values/variables#map" target="_blank">maps</a></code> of YAML data) of the server role lists into one big map, in the order of the list. This allows that keys can be overridden. The expansion <code>...</code> notation is explained at <a rel="noreferrer noopener" href="https://www.terraform.io/language/expressions/function-calls#expanding-function-arguments" target="_blank">https://www.terraform.io/language/expressions/function-calls#expanding-function-arguments</a>.</p>



<p>Finally on <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">line 18</mark>, a local variable called <code>cfg</code> will be created, which merges the values of:</p>



<ul class="wp-block-list"><li><code>local.common_cfg</code></li><li><code>local.env_cfg</code></li><li><code>local.common_role_cfg_map</code></li><li><code>local.env_role_cfg_map</code></li><li><code>local.host_cfg</code></li></ul>



<p>By providing the environment name and the host/ node name to the above code (as <code>var.environment</code> and <code>var.node</code> ), all required configuration parameters can be loaded per node in Terraform, but since we&#8217;ve used a Git repository, this information can be loaded in any kind of automation tool (required is of course that each automation implements the same kind of hierarchy code).</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2022/04/import-configuration-from-hiera-or-a-git-repository-with-yaml-files-into-terraform/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>IPTables Logs in Loki and Grafana (with Promtail)</title>
		<link>https://jmorano.moretrix.com/2022/04/iptables-logs-in-loki-and-grafana-with-promtail/</link>
					<comments>https://jmorano.moretrix.com/2022/04/iptables-logs-in-loki-and-grafana-with-promtail/#respond</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Fri, 01 Apr 2022 08:00:00 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Grafana]]></category>
		<category><![CDATA[IPTables]]></category>
		<category><![CDATA[Loki]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Promtail]]></category>
		<guid isPermaLink="false">https://jmorano.moretrix.com/?p=1310</guid>

					<description><![CDATA[In the previous article (Logging in IPTables with NFLog and ulogd2) rules were created to log certain IPTables&#8230;]]></description>
										<content:encoded><![CDATA[
<p>In the previous article (<a href="https://jmorano.moretrix.com/2022/03/logging-in-iptables-with-nflog-and-ulogd2/" data-type="URL" data-id="https://jmorano.moretrix.com/2022/03/logging-in-iptables-with-nflog-and-ulogd2/">Logging in IPTables with NFLog and ulogd2</a>) rules were created to log certain IPTables rules with the use of <code>NFLOG</code> and <code>ulogd2</code> to a file in JSON format.</p>



<p>With Promtail (<a rel="noreferrer noopener" href="https://grafana.com/docs/loki/latest/clients/promtail/" data-type="URL" data-id="https://grafana.com/docs/loki/latest/clients/promtail/" target="_blank">https://grafana.com/docs/loki/latest/clients/promtail/</a>), the above created log files can be sent to <a rel="noreferrer noopener" href="https://grafana.com/docs/loki/latest/" data-type="URL" data-id="https://grafana.com/docs/loki/latest/" target="_blank">Loki</a> so that they can finally be displayed in <a rel="noreferrer noopener" href="https://grafana.com/grafana/" data-type="URL" data-id="https://grafana.com/grafana/" target="_blank">Grafana</a>.</p>



<p>The installation of both Loki and Grafana are not covered in this article. The installation of Promtail is documented at <a rel="noreferrer noopener" href="https://grafana.com/docs/loki/latest/clients/promtail/installation/" target="_blank">https://grafana.com/docs/loki/latest/clients/promtail/installation/</a>.</p>



<p>Once Promtail is installed, create the following configuration file at <code>/etc/promtail-local-config.yaml</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="json" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">server:                                                                                                                                                                                                            
  http_listen_port: 9080                                                                                                                                                                                           
  grpc_listen_port: 0                                                                                                                                                                                              
                                                                                                                                                                                                                   
positions:                                                                                                                                                                                                         
  filename: /var/tmp/promtail_positions.yaml                                                                                                                                                                       
                                                                                                                                                                                                                   
clients:                                                                                                                                                                                                           
  - url: http://loki_server:3100/loki/api/v1/push       
                                                                                                                                                               
scrape_configs:
    - job_name: iptableslogsjson
      static_configs:
      - targets:
          - localhost
        labels:
          instance: myhostname01
          job: iptableslogsjson
          __path__: /var/log/ulog/*json
      pipeline_stages:
      - json:
          expressions:
            timestamp: timestamp
            prefix: '"oob.prefix"'
            src: src_ip
            dst: dest_ip
      - labels:
          timestamp:
          prefix:
          src:
          dst:</pre>



<p>With the above configuration, Promtail will create 4 extra labels per log line:</p>



<ul class="wp-block-list"><li><code>timestamp</code>: Contains the logged timestamp</li><li><code>prefix</code>: the NFLOG prefix string</li><li><code>src</code>: the source IP address</li><li><code>dst</code>: the destination IP address</li></ul>



<p>Once the logs are arriving in Loki, and Loki has been configured as a datasource in Grafana, graphs can be created using <a href="https://grafana.com/docs/loki/latest/logql/" data-type="URL" data-id="https://grafana.com/docs/loki/latest/logql/" target="_blank" rel="noreferrer noopener">LogQL</a>.</p>



<p>Example:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">sum(rate({job="iptableslogsjson"} [$__interval])) by (prefix)</pre>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="916" height="296" src="https://jmorano.moretrix.com/wp-content/uploads/2022/03/Screenshot-from-2022-03-30-15-29-02.png" alt="" class="wp-image-1311" srcset="https://jmorano.moretrix.com/wp-content/uploads/2022/03/Screenshot-from-2022-03-30-15-29-02.png 916w, https://jmorano.moretrix.com/wp-content/uploads/2022/03/Screenshot-from-2022-03-30-15-29-02-300x97.png 300w, https://jmorano.moretrix.com/wp-content/uploads/2022/03/Screenshot-from-2022-03-30-15-29-02-768x248.png 768w" sizes="(max-width: 916px) 100vw, 916px" /></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2022/04/iptables-logs-in-loki-and-grafana-with-promtail/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Terraform: Create a map of subnet IDs in Azure</title>
		<link>https://jmorano.moretrix.com/2022/03/terraform-create-a-map-of-subnet-ids-in-azure/</link>
					<comments>https://jmorano.moretrix.com/2022/03/terraform-create-a-map-of-subnet-ids-in-azure/#respond</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Fri, 04 Mar 2022 11:27:16 +0000</pubDate>
				<category><![CDATA[Automation]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Terraform]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[azurerm]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Hashicorp]]></category>
		<guid isPermaLink="false">https://jmorano.moretrix.com/?p=1295</guid>

					<description><![CDATA[The subnets accessor in the azurerm_virtual_network Terraform data source returns a list of subnet names only. In most&#8230;]]></description>
										<content:encoded><![CDATA[
<p>The <code>subnets</code> accessor in the <code>azurerm_virtual_network</code> Terraform data source returns a list of subnet names only. In most cases however, you will need to use a or multiple subnet IDs, for instance when deploying virtual machines. Instead of creating a new <code>datasource</code> (for possibly a small list of subnets) for each virtual machine you want to deploy, creating a <code>locals</code> map, which can be looked up afterwards, is going to be faster on the <code>apply</code> run.</p>



<p>Create a list of the existing virtual network subnets:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="monokai" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">data azurerm_subnet "subnets" {
  count = length(data.azurerm_virtual_network.my_vnet.subnets)

  name                 = data.azurerm_virtual_network.my_vnet.subnets[count.index]
  virtual_network_name = var.vnet_name
  resource_group_name  = var.resource_group
}

locals {
  subnets = tomap({
      for snet in data.azurerm_subnet.subnets: snet.name => snet.id
  })
}</pre>



<p>In the above example, we first loop over all subnet names, returned by <code>data.azurerm_virtual_network.my_vnet.subnets</code>, to create a list of Azure virtual network subnet objects.</p>



<p>Afterwards we create a <code>locals</code> map called <code>subnets</code>, which contains mapping like &#8220;subnet name points to subnet ID&#8221;.</p>



<p>Finally, when creating Azure network interfaces with an IP configuration, you can easily lookup the correct subnet ID based on the subnet name (which you might have configured per virtual machine)</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="monokai" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">resource "azurerm_network_interface" "my_nic" {
...
  ip_configuration {
    ...
    subnet_id = lookup(local.subnets, "my_subnet_name")
  }
...</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2022/03/terraform-create-a-map-of-subnet-ids-in-azure/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Block countries on OpenBSD using pf</title>
		<link>https://jmorano.moretrix.com/2022/03/block-countries-on-openbsd-using-pf/</link>
					<comments>https://jmorano.moretrix.com/2022/03/block-countries-on-openbsd-using-pf/#respond</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Tue, 01 Mar 2022 12:28:09 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[OpenBSD]]></category>
		<category><![CDATA[PF]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SysAdmin]]></category>
		<category><![CDATA[UNIX]]></category>
		<guid isPermaLink="false">https://jmorano.moretrix.com/?p=1291</guid>

					<description><![CDATA[Same as in the previous article, full countries can be easily blocked on OpenBSD firewall using the pf&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Same as in the <a href="https://jmorano.moretrix.com/2022/03/block-countries-using-iptables/" data-type="post" data-id="1285">previous article</a>, full countries can be easily blocked on OpenBSD firewall using the <code>pf</code> command and <a rel="noreferrer noopener" href="https://ipdeny.com/" target="_blank">https://ipdeny.com/</a>.</p>



<p>The zone files provided by <a rel="noreferrer noopener" href="https://ipdeny.com/" target="_blank">https://ipdeny.com/</a> need to be stored locally. A simple way to achieve this is by having a <code>cronjob</code> downloading those periodically (for instance once per day):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">#!/bin/sh

# download the latest country zone files
curl -s https://www.ipdeny.com/ipblocks/data/countries/ru.zone > /etc/ru.zone
curl -s https://www.ipdeny.com/ipblocks/data/countries/cn.zone > /etc/cn.zone
﻿</pre>



<p>We store them directly to <code>/etc</code> in the above example.</p>



<p>In the <code>/etc/pf.conf</code>, first add a table based on the files you have generated with the above statements:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># add a bad hosts table based on local disk text files
# one CIDR per line
table &lt;badhosts> persist file "/etc/ru.zone" file "/etc/cn.zone"
</pre>



<p>In the above example, we have created a table called <code>badhosts</code> based on two local files.</p>



<p>Finally we need some rules which actually blocks from and to these network ranges, an example <code>PF</code> block rule could be:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># block bad IP addresses
block from &lt;badhosts> to any
block from any to &lt;badhosts>
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2022/03/block-countries-on-openbsd-using-pf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Block countries using IPtables and IPDeny.com</title>
		<link>https://jmorano.moretrix.com/2022/03/block-countries-using-iptables/</link>
					<comments>https://jmorano.moretrix.com/2022/03/block-countries-using-iptables/#comments</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Tue, 01 Mar 2022 07:12:28 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[IPTables]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SysAdmin]]></category>
		<guid isPermaLink="false">https://jmorano.moretrix.com/?p=1285</guid>

					<description><![CDATA[Certain server setups do not require access for all countries or just want to block certain countries since&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Certain server setups do not require access for all countries or just want to block certain countries since they are know for their malicious activity.</p>



<p>One simple (not full bullet-proof) way of doing this, is by setting up block rules on firewall level, which can be achieved on Linux servers with <code>iptables</code> and zone files of <a rel="noreferrer noopener" href="https://www.ipdeny.com/" target="_blank">https://www.ipdeny.com/</a>. These zone files contain the network ranges assigned to a specific country.</p>



<p>The network ranges are fed to a tool called <code>ipset</code>, which sets up of hash map that can be easily used within <code>iptables</code> rules.</p>



<p>On Debian/Ubuntu systems, <code>ipset</code> can be installed with the <code>apt</code> command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="monokai" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">apt install ipset</pre>



<p>Next, create an iptables chain called &#8220;<code>blocked_countries</code>&#8220;, to which we will add rules afterwards. Add this chain to the beginning of the <code>INPUT</code> and <code>FORWARD</code> chain, to have early blocking in your ruleset.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="monokai" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">iptables -N blocked_countries
iptables -I INPUT -j blocked_countries -m comment --comment "Blocked countries"
iptables -I FORWARD -j blocked_countries -m comment --comment "Blocked countries"</pre>



<p>Finally, create a shell script which will download the required zone files from <a rel="noreferrer noopener" href="https://ipdeny.com/" target="_blank">https://ipdeny.com/</a> and which feeds them to <code>ipset</code>. The  example script will try to block the countries China, Russia and Belarus:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="monokai" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">#!/bin/bash

COUNTRIES=('cn' 'ru' 'by')

for COUNTRY in "${COUNTRIES[@]}"; do
    ipset create "countries_${COUNTRY}" hash:net
done

iptables -v -F blocked_countries

for i in "${COUNTRIES[@]}"; do
    echo "Ban IP of country ${i}"
    ipset flush "countries_${i}"


    for IP in $(wget -O - https://www.ipdeny.com/ipblocks/data/countries/${i}.zone)
    do
        ipset add "countries_${i}" $IP
    done
    iptables -I blocked_countries   -m set --match-set "countries_${i}" src  -j LOGDROP -m comment   --comment "Block .${i}"
done
﻿</pre>



<p>You can check the <code>blocked_countries</code> chain if packets are being blocked by your new rules:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="monokai" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">iptables -v -n -L blocked_countries 
# Warning: iptables-legacy tables present, use iptables-legacy to see them
Chain blocked_countries (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    2   104 LOGDROP    all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set countries_by src /* Block .by */
 2182  155K LOGDROP    all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set countries_ru src /* Block .ru */
  344 21370 LOGDROP    all  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set countries_cn src /* Block .cn */
﻿</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2022/03/block-countries-using-iptables/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Monitor running processes with Perl</title>
		<link>https://jmorano.moretrix.com/2014/05/monitor-running-processes-with-perl/</link>
					<comments>https://jmorano.moretrix.com/2014/05/monitor-running-processes-with-perl/#comments</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Thu, 15 May 2014 12:33:22 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[CPAN]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[SysAdmin]]></category>
		<guid isPermaLink="false">http://jmorano.moretrix.com/?p=1081</guid>

					<description><![CDATA[Update: This article is updated thanks to Colin Keith his excellent comment. I was extremely inspired by it&#8230;]]></description>
										<content:encoded><![CDATA[
<p><strong>Update:</strong> This article is updated thanks to Colin Keith his excellent comment. I was extremely inspired by it</p>



<p>Maintaining a large number of servers cannot be done without proper programming skills. Each good system administrator must therefor make sure he knows how to automate his daily works.</p>



<p>Although many many programming languages exist, most persons will only write code in one. I happen to like Perl.</p>



<p>In this next blog post, I am going to show how to create a script which can be deployed on all the Linux servers you need to maintain and need to check for certain running services.</p>



<p>Of course, a tool as Nagios together with NRPE and a configured event-handler could also be used, but lately I was often in the situation that the &#8216;nrpe daemon&#8217; crashed, Nagios was spewing a lot of errors and the event-handler&#8230; well, since nrpe was down, the event-handler of course couldn&#8217;t connect or do anything. So why rely on a remote triggered action, when a simple script could be used.</p>



<p>The following script will check a default list of services and can additionally load or overwrite these services. A regular expression can be used to check for running processes, and of course, a startup command needs to be defined. And that is all the script will and should do.</p>



<p>The script uses three CPAN modules:</p>



<ul class="wp-block-list"><li><a title="Proc::ProcessTable" href="http://search.cpan.org/~jwb/Proc-ProcessTable-0.50/ProcessTable.pm">Proc::ProcessTable</a></li><li><a title="YAML" href="http://search.cpan.org/~ingy/YAML-0.90/lib/YAML.pm">YAML</a></li><li><a title="File::Slurp" href="http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm">File::Slurp</a></li></ul>



<p>The first one will be used to get a full listing of all running processes and the second one will provide us a means for using configuration files.</p>



<p>So let&#8217;s start our script:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">#!/usr/bin/env perl 
use strict; use warnings;
use utf8;

use Proc::ProcessTable;
use YAML qw/LoadFile/;
use File::Slurp;

# Default set of processes to watch
my %default_services = (
    'NRPE' =&amp;gt; {
        'cmd'     =&amp;gt; '/etc/init.d/nagios-nrpe-server restart',
        're'      =&amp;gt; '/usr/sbin/nrpe -c /etc/nagios/nrpe.cfg -d',
	'pidfile' =&amp;gt; '/var/tmp/nagios-nrpe-server.pid',
    },
    'Freshclam' =&amp;gt; {
        'cmd'     =&amp;gt; '/etc/init.d/clamav-freshclam restart',
        're'      =&amp;gt; '/usr/bin/freshclam -d --quiet',
	'pidfile' =&amp;gt; '/var/tmp/clamav-freshclam.pid',
    },
    'Syslog-NG' =&amp;gt; {
        'cmd'     =&amp;gt; '/etc/init.d/syslog-ng restart',
        're'      =&amp;gt; '/usr/sbin/syslog-ng -p /var/run/syslog-ng.pid',
	'pidfile' =&amp;gt; '/var/run/syslog-ng.pid',     
    },
    'VMToolsD' =&amp;gt; {
        'cmd'     =&amp;gt; '/etc/init.d/vmware-tools restart',
        're'      =&amp;gt; '/usr/sbin/vmtoolsd',
	'pidfile' =&amp;gt; '/var/tmp/vmtoolsd.pid',
    },
    'Munin-Node' =&amp;gt; {
        'cmd'     =&amp;gt; '/etc/init.d/munin-node restart',
        're'      =&amp;gt; '/usr/sbin/munin-node',
	'pidfile' =&amp;gt; '/var/tmp/munin-node.pid',
    },
);

my (%services) = (%default_services);
</pre>



<p>Until now, no rocket science. We load the required modules, we defined our default services that need to be checked.</p>



<p>Next part, check if there is a configuration file on disk. The script looks for a hard-coded path &#8221;/etc/default/watchdog.yaml&#8221;:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Check if there is a local config file and if yes, load them in the services hash
if( -f '/etc/default/watchdog.yaml' ){
    my $local_config = LoadFile '/etc/default/watchdog.yaml';

    %services = (%default_services, %{ $local_config->{services} });
}
</pre>



<p>The last Perl statement actually allows to overwrite one or more (or even all) the default defined services.</p>



<p>Now let&#8217;s see if these processes are actually running. The following code was hugely inspired by Colin Keith&#8217;s comment below. I have combined his examples together with my code.</p>



<p>Let&#8217;s first have a look at the code:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Get current process table
my $processes = Proc::ProcessTable-&amp;gt;new;
my %procs; 
my %matched_procs;
foreach my $p (@{ $processes }){
    $procs{ $p-&amp;gt;{pid} } = $p-&amp;gt;{cmndline};
    foreach my $s (keys %services){
        if($p-&amp;gt;{cmndline} =~ m#$services{$s}-&amp;gt;{re}#){
            $matched_procs{$s}++;
            last;
        }
    }
}

# Search the process table for not running services
foreach my $service ( keys %services ) {
    if(exists($services{$service}-&amp;gt;{pidfile}) &amp;amp;&amp;amp; -f $services{$service}-&amp;gt;{pidfile} ) {
        my $pid = read_file( glob($services{$service}-&amp;gt;{pidfile}) );
 
        # If we get a pid ensure that it is running, and that we can signal it
        $pid &amp;amp;&amp;amp; exists($procs{$pid}) &amp;amp;&amp;amp; kill(0, $pid) &amp;amp;&amp;amp; next;  
        
        # Remove the stale PID file because no running process for this PID file
        unlink( $services{$service}-&amp;gt;{pidfile} );
    }
    else {
        # check if the configured process regex matches
        if( exists($matched_procs{$service}) ){
            # process is running but has no PID file
            next;
        }
    }
	
    # Execute the service command
    system( $services{$service}-&amp;gt;{'cmd'} );

    # Check the exit code of the service command
    if ($? == -1) {
        print "Failed to restart '$service' with '$services{$service}-&amp;gt;{cmd}': $!\n";
    }
    elsif ($? &amp;amp; 127) {
        printf "Restart of '$service' died with signal %d, %s coredump\n", ($? &amp;amp; 127),  ($? &amp;amp; 128) ? 'with':'without';
    }
    else {
        printf "Process '$service' successfully restarted, exit status:  %d\n", $? &amp;gt;&amp;gt; 8;
    }
}
</pre>



<p>Lines 2 retrieves the current process list. We will save that information in two hashes with a little less information, because we actually only need the PID and the actual &#8216;command line&#8217; of each process.</p>



<p>At line 16 we will start looping through the processes we have defined in the <code>%services</code> hash.<br />Inspired by Colins post, we will check if the process&#8217; PID file is still there and if one is configured. If it still exists, we will then verify if the PID stored in the PID file, exists in the process list, which we have stored in <code>%procs</code>. This happens in lines 18-21.<br />At line 21, if the process is still running and the PID matches, we will check the next service to check (<code>&amp;&amp; next</code> part)<br />If the process is not running anymore but the PID file was still in the defined path, then it will be removed at line 24.</p>



<p>Otherwise, if no PID file was found or no PID file was configured, we will check the process list with the regular expression defined for that process. We have already created a hash, <code>%matched_procs</code> between lines 7 and 10, which we will use for this checking. If the process exists in the hash, we will skip and check the next process to be checked.</p>



<p>Now, if there was no PID file or the PID file was removed at line 24, the process will be started again. This happens at line 35.<br />I&#8217;ve executed it with the &#8216;system&#8217; function since I want to have the output of this command directly in STDOUT. And of course, the last thing to do is to check if the process started up correctly or not by checking its exit code.</p>



<p>Now save that script to for instance &#8216;watchdog.pl&#8217; and configure it in a cron job.<br />Example:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">*/5 * * * * root /usr/local/bin/watchdog.pl
</pre>



<p>And here&#8217;s an example of the configuration file:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">services:
    Exim-Mailserver:
        cmd: /etc/init.d/exim4 restart
        re: /usr/sbin/exim4 -bd -q30m
    Ossec-Agent:
        cmd: /etc/init.d/ossec restart
        re: !!perl/regexp '(?:ossec-agentd|ossec-logcollector|ossec-syscheckd)'

</pre>



<p>Link to script source code: <a href="https://github.com/insani4c/perl_tools/tree/master/watchdog" target="_blank" rel="noopener">https://github.com/insani4c/perl_tools/tree/master/watchdog</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2014/05/monitor-running-processes-with-perl/feed/</wfw:commentRss>
			<slash:comments>15</slash:comments>
		
		
			</item>
		<item>
		<title>A simple TCP server written in Perl</title>
		<link>https://jmorano.moretrix.com/2013/09/simple-tcp-server-written-perl/</link>
					<comments>https://jmorano.moretrix.com/2013/09/simple-tcp-server-written-perl/#comments</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Tue, 17 Sep 2013 12:47:04 +0000</pubDate>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[client/server]]></category>
		<category><![CDATA[CPAN]]></category>
		<category><![CDATA[Debian]]></category>
		<category><![CDATA[TCP]]></category>
		<guid isPermaLink="false">http://jmorano.moretrix.com/?p=997</guid>

					<description><![CDATA[The below example is a very simple TCP server script written in Perl, which uses the AnyEvent module.It&#8230;]]></description>
										<content:encoded><![CDATA[
<p>The below example is a very simple TCP server script written in Perl, which uses the <a href="http://software.schmorp.de/pkg/AnyEvent.html" target="_blank" rel="noopener">AnyEvent</a> module.<br />It will create a separate process for each connections and has the ability to return data to the parent process.<br />The below example allows 15 child processes to be created, which results in 15 simultaneous client connections.</p>



<p>The script itself is pretty straight-forward: it creates a server object using <em>IO::Socket::INET</em> and attaches that socket to an <em>AnyEvent</em> IO eventloop. Furthermore, upon every connection, it will call the subroutine <em>fork_call</em> (which is in the <em>AnyEvent::Util</em> module) to open up a client socket.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> 
#!/usr/bin/env perl 
use strict;
use warnings;
use utf8;

use IO::Socket::INET;
use AnyEvent;
use AnyEvent::Util;
$AnyEvent::Util::MAX_FORKS = 15;

my $handled = 0;
$|++;

my $server = IO::Socket::INET->new(
    'Proto'     => 'tcp',
    'LocalAddr' => 'localhost',
    'LocalPort' => 1234,
    'Listen'    => SOMAXCONN,
    'Reuse'     => 1,
) or die "can't setup server: $!\n";
print "Listening on localhost:1234\n";

my $cv = AnyEvent->condvar;
my $w; $w = AnyEvent->io(
        fh   => \*{ $server }, 
        poll => 'r', 
        cb   => sub { 
                   $handled++;
                   $cv->begin; 
                   fork_call &amp;handle_connections, 
                             $server->accept, 
                             sub { 
                               my ($client) = @_ ;
                               print " - Client $client closed\n"
                             } 
                    }
);
$cv->recv;

#
# Subroutines
# 
sub handle_connections {
    my ($client) =  @_;

    my $host = $client->peerhost;
    print "[Accepted connection from $host]\n";

    print $client "Hi, you're client #$handled\n";
    chomp ( my $input = &lt;$client> );
    my $output = reverse $input;
    print $client $output, "\n";
    print $client "Bye, bye.\n";

    $cv->end;
    return $host;
}
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2013/09/simple-tcp-server-written-perl/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
