Thursday, 19 January 2017

WebSite SSL cert. check script

There was a need to be able to check SSL certs for certain set of web services / site out of a central point without the having the actual cert.
To do this, I made use of openssl s_client functionality.

The below script takes a single parameter, the hostname:port and does the validation.
The script has 4 timing checks so it needs to be run every day to be able to catch the actual certificate expiration time.
Once it detects an expiring cert, it will send out an email given that sendmail is configured on the server that runs it.

[root@Beren check_certs]# cat check_certs.sh
#set -x

Check_CERTs ()
{
TARGET="$1"
TEST_DATE31=`date "+%b %e %T %Y %Z" -d "+31 day" |tr -s " "|cut -d" " -f1,2,4`
TEST_DATE21=`date "+%b %e %T %Y %Z" -d "+21 day" |tr -s " "|cut -d" " -f1,2,4`
TEST_DATE11=`date "+%b %e %T %Y %Z" -d "+11 day" |tr -s " "|cut -d" " -f1,2,4`
TEST_DATE03=`date "+%b %e %T %Y %Z" -d "+3 day" |tr -s " "|cut -d" " -f1,2,4`

SSL_TEST_CMD=" echo |openssl s_client -connect ${TARGET} 2>/dev/null|openssl x509 -inform pem -noout -text |grep \"Not After :\" |
cut -d\":\" -f2-|tr -s \" \"|cut -d\" \" -f2,3,5"

SSL_EXP_DATE=`bash -c "${SSL_TEST_CMD}"`

##SSL_EXP_DATE="Feb 9 2017"

echo $SSL_EXP_DATE

if [ "$SSL_EXP_DATE" == "$TEST_DATE31" ]
then
    echo "31 days left"
elif [ "$SSL_EXP_DATE" == "$TEST_DATE21" ]
then
        echo "21 days left"
elif [ "$SSL_EXP_DATE" == "$TEST_DATE11" ]
then
        echo "11 days left"
elif [ "$SSL_EXP_DATE" == "$TEST_DATE03" ]
then
        echo "3 days left"
fi
 

#End of the shell function.
}


#Main code:

CERT_TIME=`Check_CERTs "${1}"|grep left`

if [ -n "$CERT_TIME" ]
then

echo "Sending Email"

/usr/sbin/sendmail.sendmail -i -t << ENDL
From: "Script Cert Alert"
To: <sherif.abdelfattah@live.com>
Subject: CERT Expiration WaRNing

Please check the SSL Certs installed on ${1} !!
The certificate is about to expire !!
${CERT_TIME}


Please take action ASAP.

ENDL

fi
[root@Beren check_certs]#

The script uses Linux gnu date, that can take a time string using -d option, also we make use of the %e which uses space padded days of the month similar to OpenSSL command date format.
Also note the use of the "echo |openssl" construct, this prevents openssl command from waiting for further input thus, we can run it from a script.


Wednesday, 11 January 2017

A simple script to check Redis Cluster

Below is a simple script around the redis-cli tool that aims to give info about a replica cluster and to identify the redis master node.
This can be a simple fast check for redis.

Below is the script:

[redis@feanor]$ cat check_redis.sh
echo " "
REDIS_BIN=.
echo "Getting Master Info from Sentinel"

MASTER_IP=`$REDIS_BIN/redis-cli -p 9000 info sentinel | grep "master0:name=mymaster"|cut -d"," -f3|cut -d"=" -f2|cut -d":" -f1`
MASTER_PORT=`$REDIS_BIN/redis-cli -p 9000 info sentinel | grep "master0:name=mymaster"|cut -d"," -f3|cut -d"=" -f2|cut -d":" -f2`
MASTER_STATUS=`$REDIS_BIN/redis-cli -p 9000 info sentinel | grep "master0:name=mymaster"|cut -d"," -f2|cut -d"=" -f2`
MASTER_HOSTNAME=`nslookup $MASTER_IP|grep "name = " |cut -d"=" -f2`

echo "Master IP is: $MASTER_IP"
echo "Master Port is: $MASTER_PORT"
echo "Master Hostname is: $MASTER_HOSTNAME"
echo "Master Status is:  $MASTER_STATUS"
echo " "
echo "Replication info from Master:"
$REDIS_BIN/redis-cli -h $MASTER_IP -p $MASTER_PORT info replication
[redis@feanor]$


In this case Redis-server runs on port 7000 and Redis-Sentinel on port 9000.

I have allowed the script to extract the port form sentinel output as at times we have more than one Redis instance running on the same VM, thus using same IP but different port.



Thursday, 22 December 2016

Rundeck Parametric Job Definition

I have a use case where I need to run same set of shell instructions multiple times with different users and compare the results on remote hosts.

Thought about automating this using Rundeck, and since I am not allowed to use ssh keys password-less authentication in this case, Rundeck would be ideal.

below is a sample job listing (XML) for a parametric job that accepts username and a password and just prints the username in a simple embedded shell script.

<joblist>
  <job>
    <context>
      <options preserveOrder='true'>
        <option name='Password' required='true' secure='true' valueExposed='true' />
        <option name='User_Name' required='true' value='App_User' />
      </options>
    </context>
    <description></description>
    <executionEnabled>true</executionEnabled>
    <id>5e63d0fa-9bae-4d1d-bc09-a2d8429f8b19</id>
    <loglevel>INFO</loglevel>
    <name>Parametrized_job1</name>
    <scheduleEnabled>true</scheduleEnabled>
    <sequence keepgoing='false' strategy='node-first'>
      <command>
        <script><![CDATA[echo "This is a test Job"
echo 'using form $RD_OPTION_USER_NAME' $RD_OPTION_USER_NAME
echo 'using form ${option.User_name}' ${option.User_Name} #doesn't work :)
echo 'using form \@option.User_name\@' @option.User_Name@
]]></script>
        <scriptargs />
      </command>
    </sequence>
    <uuid>5e63d0fa-9bae-4d1d-bc09-a2d8429f8b19</uuid>
  </job>
</joblist>


The job parameters are better accessed as passed by Rundeck env. as:$RD_OPTION_paramname

The above job option env. variable will be passed to all rundeck executed scripts so it is much easier to use it in this form than the one between @ signs.
The form ${option.User_Name} is not working in this case as Rundeck will use it as a command-line argument if we are running a commad step rather than an embedded script.

below is an example job definition:

<joblist>
  <job>
    <context>
      <options preserveOrder='true'>
        <option name='Password' required='true' secure='true' valueExposed='true' />
        <option name='User_Name' required='true' value='App_User' />
      </options>
    </context>
    <description></description>
    <executionEnabled>true</executionEnabled>
    <id>5d1db9a0-d381-49ec-8981-c483b375751c</id>
    <loglevel>INFO</loglevel>
    <name>Parametrized_job2</name>
    <scheduleEnabled>true</scheduleEnabled>
    <sequence keepgoing='false' strategy='node-first'>
      <command>
        <exec>echo ${option.User_Name}</exec>
      </command>
    </sequence>
    <uuid>5d1db9a0-d381-49ec-8981-c483b375751c</uuid>
  </job>
</joblist>


Rundeck parametric jobs will prove very useful for doing generic tasks and generic automation.

Please check more info in Rundeck documentation at: http://rundeck.org/1.6.2/manual/job-options.html#prompting-the-user



Monday, 5 December 2016

JQuery Draggable and Droppable demo

I was exploring a way to use simple HTML UI components to offer UI drag and drop functionality.
This is a long term plan I was having to start real automation of my day to day work and avoid executing or editing shell scripts or puppet manifests.

JQuery offers a good simple API to allow drag and drop fuctionaltity with a lot of flexiability.

Since I am not UX-UI designer, it took me quite sometime to understand how it works and write down a working demo code for the functionality.

Below is a simple code for simple HTML table (TD elements) having draggable numbers from 1 to 8 and are only droppable inside the HTML table.

<html>

<head>
<style>
table, th, td {
    border: 5px solid black;
    text-align: center;
    color: blue;
    font-size: 300%;
    font-weight: bold;
    border-spacing: 10px;
}

</style>


<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

<script>

// Make the element with id "draggable" draggable
$(function () {
   
    count = 0;

    $("#td1").draggable({revert: "invalid", containment: "document", grid: [220,210] });
    $("#td2").draggable({revert: "invalid", containment: "document", grid: [220,210] });
    $("#td3").draggable({revert: "invalid", containment: "document", grid: [220,210]});
    $("#td4").draggable({revert: "invalid", containment: "document", grid: [220,210]});
    $("#td5").draggable({revert: "invalid", containment: "document", grid: [220,210]});
    $("#td6").draggable({revert: "invalid", containment: "document", grid: [220,210]});
    $("#td7").draggable({revert: "invalid", containment: "document", grid: [220,210]});
    $("#td8").draggable({revert: "invalid", containment: "document", grid: [220,210]});
   
    $( "#tb1" ).droppable({
      drop: function(event, ui) {
      Edit_dragable (  ui.draggable );
      //$("#logger").html( "Dropped! ");
      //$("#logger2").html( "Testing" );
    }
    });
   
   
        function Edit_dragable( $item ) {
        $item
          .css( "color", "Yellow" );
          var textval = 'The number is ' + $item.html() + ' & the td id was: ' + $item.attr('id');
          $("#logger2").html( textval );
      }
   
});
</script>

</head>

<table id="tb1" >
<caption>Test arrange game </caption>
 <tr id="tr1">
    <td id="td1" style="width:200;  height:200; border-color: green"> 1 </td>
    <td id="td2" style="width:200;  height:200;"> 2 </td>
    <td id="td3" style="width:200;  height:200;"> 3 </td>
</tr>

 <tr id="tr2">
    <td id="td4" style="width:200;  height:200"> 4 </td>
    <td id="td5" style="width:200;  height:200;"> 5 </td>
    <td id="td6" style="width:200;  height:200;"> 6 </td>
</tr>

 <tr id="tr3">
    <td id="td7" style="width:200;  height:200;"> 7 </td>
    <td id="td8" style="width:200;  height:200;"> 8 </td>
   
    <!--<td id="td9" draggable="true" ondragstart="drag(event)"> 9 </td>-->
</tr>
</table>

<p id="logger"> </p>
<p id="logger2"> </p>


The code involves some HTML, CSS styles and JQuery to be able to achieve the functionality.

Next steps is to combine this with my other code components to offer an interactive control dash board for automating my day to day tasks.
Will continue to post as I progress in this work.




Sunday, 25 September 2016

Haproxy CSV stats

Haproxy exposes a very useful statistics page that can be accessed over a web browser or from the command line using a tool like curl.
Haproxy allows the stats to be exposed in a CSV format that is exceedingly useful if you are going to have a script around it.

To access the CSV stats from http interface use the below:

curl  -u statsuser:statspassword 'http://haproxyhost:8001/stats/?stats;csv;' 2>/dev/null |egrep -iv "up|^#|open" |cut -d"," -f1,2,18

Where port 8001 is the statistics port as defined in the haproxy config.
The config is simple, already shown in older posts, should be something like this:

listen stats *:8001
    stats enable
    stats uri /stats
    stats hide-version
    stats auth statsuser:statspassword



The above mini script will print out only the apps and backends that show as down.
working ones will not show up.
The CVS header is also stripped as the header always starts with a "#" this makes it possible to process only the data using a grep -v as above.

The CSV result contains a big amount of info that can be used for load management and automation.
The details of the haproxy CSV stats layout can be found at:
https://cbonte.github.io/haproxy-dconv/1.5/configuration.html#9  

Thursday, 22 September 2016

Updating a field of a DB table using records from same table

The below query is to update a field that is set to null in a config table from records that contain values in the same table.

I came across this scenario while working on a puppet upgrade project where we are moving the file based Yaml configs to SQL DB.


below is the query:

update config_dev c
set groupname = l.groupname
from ( select distinct (groupname) , name from config_dev where groupname is not null ) l
where c.groupname is null
and c.name = l.name

The table contains some "groupname" fields that contains null, still those should contain same group values as others given that name field is the same.


the above query will do that for us.
Not that the above query will not run in MySQL !!

Thanks to my friend Mohamed Youssef who helped me with this query.



Monday, 19 September 2016

Setting up a new PostgreSQL on Redhat systems

Postgres setup turns out to be not as straight forward as I assumed.
When you setup the DB server using yum, the DB comes in a not initialized state, and thus it fails to start using standard service commands.

The below Fedora project wiki link entails a lot of details about this issue:
https://fedoraproject.org/wiki/PostgreSQL

An example on CentOS 7 looks like below.
Once you initialize the DB it starts up with no issues.

[root@vardamir ~]# systemctl start postgresql
Job for postgresql.service failed because the control process exited with error code. See "systemctl status postgresql.service" and "journalctl -xe" for details.
[root@vardamir ~]# journalctl -xe
-- Documentation: http://www.freedesktop.org/wiki/Software/systemd/multiseat
--
-- A new session with the ID 3 has been created for the user root.
--
-- The leading process of the session is 11388.
Sep 19 11:54:29 vardamir systemd[1]: Starting Session 3 of user root.
-- Subject: Unit session-3.scope has begun start-up
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit session-3.scope has begun starting up.
Sep 19 11:54:29 vardamir sshd[11388]: pam_unix(sshd:session): session opened for user root by (uid=0)
Sep 19 11:54:30 vardamir dbus[725]: [system] Activating service name='org.freedesktop.problems' (using servicehelper)
Sep 19 11:54:30 vardamir dbus-daemon[725]: dbus[725]: [system] Activating service name='org.freedesktop.problems' (using servicehelper)
Sep 19 11:54:30 vardamir dbus[725]: [system] Successfully activated service 'org.freedesktop.problems'
Sep 19 11:54:30 vardamir dbus-daemon[725]: dbus[725]: [system] Successfully activated service 'org.freedesktop.problems'
Sep 19 11:54:52 vardamir polkitd[1394]: Registered Authentication Agent for unix-process:11426:31997 (system bus name :1.28 [/usr/bin/pkttyagent --notify-fd 5 --fallback],
Sep 19 11:54:52 vardamir systemd[1]: Starting PostgreSQL database server...
-- Subject: Unit postgresql.service has begun start-up
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit postgresql.service has begun starting up.
Sep 19 11:54:52 vardamir postgresql-check-db-dir[11431]: "/var/lib/pgsql/data" is missing or empty.
Sep 19 11:54:52 vardamir postgresql-check-db-dir[11431]: Use "postgresql-setup initdb" to initialize the database cluster.
Sep 19 11:54:52 vardamir postgresql-check-db-dir[11431]: See /usr/share/doc/postgresql-9.2.15/README.rpm-dist for more information.
Sep 19 11:54:52 vardamir systemd[1]: postgresql.service: control process exited, code=exited status=1
Sep 19 11:54:52 vardamir systemd[1]: Failed to start PostgreSQL database server.
-- Subject: Unit postgresql.service has failed
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit postgresql.service has failed.
--
-- The result is failed.
Sep 19 11:54:52 vardamir systemd[1]: Unit postgresql.service entered failed state.
Sep 19 11:54:52 vardamir systemd[1]: postgresql.service failed.
Sep 19 11:54:52 vardamir polkitd[1394]: Unregistered Authentication Agent for unix-process:11426:31997 (system bus name :1.28, object path /org/freedesktop/PolicyKit1/Authe
[root@vardamir ~]# postgresql-setup initdb
Initializing database ... OK

[root@vardamir ~]# systemctl status postgresql
● postgresql.service - PostgreSQL database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2016-09-19 11:58:57 EDT; 10s ago
  Process: 11542 ExecStart=/usr/bin/pg_ctl start -D ${PGDATA} -s -o -p ${PGPORT} -w -t 300 (code=exited, status=0/SUCCESS)
  Process: 11537 ExecStartPre=/usr/bin/postgresql-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
 Main PID: 11546 (postgres)
   CGroup: /system.slice/postgresql.service
           ├─11546 /usr/bin/postgres -D /var/lib/pgsql/data -p 5432
           ├─11547 postgres: logger process  
           ├─11549 postgres: checkpointer process  
           ├─11550 postgres: writer process  
           ├─11551 postgres: wal writer process  
           ├─11552 postgres: autovacuum launcher process  
           └─11553 postgres: stats collector process  

Sep 19 11:58:56 vardamir systemd[1]: Starting PostgreSQL database server...
Sep 19 11:58:57 vardamir systemd[1]: Started PostgreSQL database server.
[root@vardamir ~]#


To allow the postgresql server (postmaster) to listen on all interfaces, we need to pieces of config changes, 1 in postgresql.conf and other in pg_hba.conf as below.
in postgresql.conf we need to set :

listen_addresses = '*' 

so that the postmaster server listens on all interfaces.
second we need to add a host entry in pg_hba.conf so that clients are allowed to connect.
this config will depend on network config of the installed DB, an example is shown below:

host    all             all             10.0.0.0/8              md5

md5 is used to allow encrypted password authentication.
Don't forget to allow the Linux firewall to open port 5432 so that external users can access the newly created DB server.

Its always better to create another user than the default postgres user and use that for the DB work remotely.