Generate a self-signed SSL Certificate with OpenSSL

Occasionally it may be necessary to generate a self-signed SSL certificate. This could be for internal websites, or for other internal uses that may require secure encrypted network transmissions.

Generating a self-signed certificate may be an easy task for the intermediate or senior level admin, however we decided to post this guide for everyone to use, since using the guide as a reference may hopefully be useful to those of you out there 😉

1. Generate an SSL key without a passphrase, enter:

openssl genrsa -out /etc/httpd/ssl/mycorp.com.key 1024

2: Create a self-signed certificate, enter:

openssl req -new -key /etc/httpd/ssl/mycorp.com.key -x509 -out /etc/httpd/ssl/mycorpcom.crt -days 999

Sample output:

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:CA
Locality Name (eg, city) []:CA
Organization Name (eg, company) [Internet Widgits Pty Ltd]:mycorp, LLC
Organizational Unit Name (eg, section) []:Sales
Common Name (eg, YOUR name) []:  
Email Address []:you@mycorp.com

My Sample Apache httpd.conf virtual host file:

DocumentRoot "/var/www/html/ssl_doc_root/"
ServerAdmin you@mycorp.com
ServerName www.mycorp.com
SSLEngine On
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:
+EXP:+eNULL
SSLCertificateFile /etc/httpd/ssl/mycorpcom.crt
SSLCertificateKeyFile /etc/httpd/ssl/mycorp.com.key
SetEnvIf User-Agent ".*MSIE.*" 
nokeepalive ssl-unclean-shutdown 
downgrade-1.0 force-response-1.0

3. Restart httpd/Apche:

service httpd restart

Thats it! Test the SSL Cert to ensure it loads fine and reflects the proper values when you examine the properties.

Manage Nagios with Scripts

Working at many different organisations over the past 10 years, I have been involved in the implementation and maintenance of many different monitoring implementations. These include commercial and open source implementations, such as :

– Nagios
– IP Monitor
– Uptime
– OpenNMS
– Zabbix

Although Nagios may not be the most scalable or dynamic solution, for some organisations that perhaps have 1-100 servers, Nagios may be the best solution.

Additionally, the ability to write custom plugins, as well as the inherent SSL / TLS encryption of the NRPE checks, it may be the most viable. There are pro’s and con’s for each solution out there, and it is completely dependant on the skill level, nature of environment and available time for management / maintenance.

During the course of utilising Nagios, we noticed that one of the most time consuming tasks was maintaining the flat file configuration for adding, removing and modifying hosts within Nagios.

As a result, it was decided to write a quick Perl based script to manage the day-to-day tasks of adding and removing hosts within Nagios. When all is said and done, it really does save ALOT of time. This script can be integrated with existing control based management situations or other automation scripts / solutions where command line options and external scripting / plugins are possible. This way, you can encompass a more rounded, standardised and reliable way of managing your systems in Nagios.

In order for the script to work, you need to have 3 types of servers :

– Windows
– Unix/Linux
– VPS (Virtual Private Server)

Obviously you can modify the script to encompass an unlimited number of categories. Basically the script has defined three pre-existing hosts in the nagios hosts.cfg / hostgroups.cfg and services.cfg files to model them when adding the new server, based on your input.

Please take a look at the script, hopefully it will help make your life a little easier! 😉

#!/usr/bin/perl

# Don't break me, I'm used by automated scripts.

###############################################################################
# Star Dot Hosting : www.stardothosting.com
# Nagios Config Manager
# Description: This program will add/remove entries from nagios.
# The files will be backed up in a archive before any changes are made.
###############################################################################
# Perl Libraries
use File::Copy;
use Switch;
###############################################################################
# Variables

###############################################################################
# Nagios file handlers
my $host_file = "/usr/local/nagios/etc/objects/hosts.cfg";
my $group_file = "/usr/local/nagios/etc/objects/hostgroups.cfg";
my $services_file = "/usr/local/nagios/etc/objects/services.cfg";
my $unixmatch = "sdh-unix" ;
my $windowsmatch  = "sdh-windows";
my $vpsmatch = "vps-server";
my $date = `date "+%d%m%y-%H%M%S"`;

###############################################################################
# Verify Arguments
if ((!$ARGV[0]) || (!$ARGV[1])) {
        &usage;
}

if (length($ARGV[1]) gt 1 ) {   print "Command options too long!n"; &usage; }

# Verify Nagios is working before we start
my $nagios = `nagios -v /usr/local/nagios/etc/nagios.cfg`;
        if ($nagios =~ /One or more problems was encountered while processing the config files/) {
        print "CRITICAL ERROR!nnNagios is already broken and we cannot continue!nPlease fix it!n";
                @error_array = split(/./, $nagios);
                for $error (@error_array) {
                $error=~s/^n//g;
                        print "$errorn" if  $error=~ /Error:/;
             }
        die "nnProgram Aborting before even starting due to nagios config error!n"

}


# Clean up any old tmp files.
unlink("/tmp/hosts.cfg.tmp");
unlink("/tmp/hostgroups.cfg.tmp");
unlink("/tmp/services.cfg.tmp");

###############################################################################
# The Main Program control statement.
###############################################################################
switch ($ARGV[1]) {

        case /d/i {     &delete;        }
        case /x/i {     &addEntry("x"); }
        case /w/i {     &addEntry("w"); }
        case /v/i {     &addEntry("v"); }
        else {
                print "Option: $ARGV[1] not found n";
                &usage;
        }
}
###############################################################################

###############################################################################
# Subroutines
###############################################################################

###############################################################################
## sub backup - Backs up the nagios config files that are to be modified
###############################################################################
sub backup {

# Backup The Nagios files into an archive.
        $date =~s/n//g;

        mkdir("/var/backup/nagios/$date", 0755 ) || die "Cannot create directory /var/backup/nagios/$daten";

        copy($host_file, "/var/backup/nagios/$date/hosts.cfg.bck"); #|| die "Cannot copy $host_file to /var/backup/nagios/$date/hosts.cfg.bckn";
        copy($group_file, "/var/backup/nagios/$date/hostgroups.cfg.bck"); #|| die "Cannot copy $host_file to /var/backup/nagios/$date/hostgroups.cfg.bckn";
        copy($services_file, "/var/backup/nagios/$date/services.cfg.bck"); #|| die "Cannot copy $service_file to /var/backup/nagios/$date/services.cfg.bckn";
}

###############################################################################
## sub openFile($filename) - returns the file to a buffer for parsing
###############################################################################
sub openFile {
        my $blob;
        my $file = shift;
        open (F, "< $file") or die "Can't open $file : $!";

        while(  ) {
                $blob .= $_;
        }

        close(F);
return $blob;

}

###############################################################################
###############################################################################


###############################################################################
## sub delete - Deletes the servername from the config files.
###############################################################################
sub delete {
        &backup;                # Backup the files before we do anything to them.
        &delete_host;
        &delete_hostgroup;
        &delete_services;
        &checkNagios;
}

###############################################################################
## sub delete_host - deletes the host entry from hosts.cfg
###############################################################################
sub delete_host {
        my $host_str = &openFile($host_file);
        my $pattern=$ARGV[0];   # The parser doesn't like the array so we just pass it to a variable.

        # parse the hosts.cfg file first
        # This regular expression is a defined host entry, if it can't find it
        # and assert that the hostname is part of that context, it will die.
        if ($host_str =~/define[^_]*.name.*(?s-i:$pattern)[^}]*./i) {
                print "command: $ARGV[1] : Deleting $ARGV[0] $1n" if $host_str =~s/define[^_]*.name.*(?s-i:$pattern)[^}]*.//g;
                print "Match: $ARGV[0]n" if $host_str =~/define[^_]*.name.*(?s-i:$pattern)[^}]*./i;
                print "Deleted $ARGV[0] from hosts.cfgn";

        # Write the successfull deleteion to a tmp file.
        open(HF, ">/tmp/hosts.cfg.tmp") || die "Cannot open /tmp/hosts.cfg.tmp";
        print HF $host_str;
    close(HF);

        } else { die "Could not find and entry for $ARGV[0] in hosts.cfgn"; };
}

###############################################################################
## sub delete_hostgroup - deletes the hostgroup entry
###############################################################################
sub delete_hostgroup {

        my $hostgrp_str = &openFile($group_file);
        my $pattern=$ARGV[0];   # The parser doesn't like the array so we just pass it to a variable.

        # search/replace the hostgroup.cfg file
        if ($hostgrp_str =~ /$pattern/i) {
                # If the server has a comma after it, we need to remove that too.. or breakage.
                if ($hostgrp_str =~ /$pattern,/i ) {
                        print "Deleted $ARGV[0], from hostgroups.cfgn" if $hostgrp_str =~ s/$pattern,//g;
                }

                        print "Deleted $ARGV[0] from hostgroups.cfgn" if $hostgrp_str =~ s/$pattern//g;

        } else {
                die "Could not find and entry for $ARGV[0] in hostgroups.cfgn";
        }
        open(HGF, ">/tmp/hostgroups.cfg.tmp") || die "Cannot open /tmp/hostgroups.cfg.tmp";
        print HGF $hostgrp_str;
        close(HGF);
}

###############################################################################
## sub delete_services - delete the serivices.cfg entry
###############################################################################
sub delete_services {

        my $services_str= &openFile($services_file);
        my $pattern=$ARGV[0];   # The parser doesn't like the array so we just pass it to a variable.


        # search/replace the hostgroup.cfg file
        if ($services_str =~ /$pattern/i) {
                # If the server has a comma after it, we need to remove that too.. or breakage.
                if ($services_str =~ /$pattern,/i ) {
                        print "Deleted $ARGV[0], from services.cfgn" if $services_str =~ s/$pattern,//g;
                }
                        print "Deleted $ARGV[0] from services.cfgn" if $services_str =~ s/$pattern//g;
         } else {
                die "Could not find and entry for $ARGV[0] in services.cfgn";
        }
        open(SF, ">/tmp/services.cfg.tmp") || die "Cannot open /tmp/services.cfg.tmp";
        print SF $services_str;
        close(SF);
}

###############################################################################
## sub checkNagios - checks nagios for errors and rolesback if so.
###############################################################################
sub checkNagios {


        copy("/tmp/hosts.cfg.tmp", $host_file) || print "Cannot copy /tmp/hosts.cfg.tmp to  $host_filen";
        copy("/tmp/hostgroups.cfg.tmp", $group_file) || print "Cannot copy /tmp/hostgroups.cfg.tmp to $host_filen";
        copy("/tmp/services.cfg.tmp", $services_file) || print "Cannot copy /tmp/services.cfg.tmp $service_filen";
        my $success = `nagios -v /etc/nagios/nagios.cfg`;

        if ($success =~ /One or more problems was encountered while processing the config files/) {
                print "CRITICAL FAILURE - See Errors!n";
                @error_array = split(/./, $success);
                for $error (@error_array) {
                $error=~s/^n//g;
                        print "$errorn" if  $error=~ /Error:/;
                        }
                print "nRestoring from backupnCheck /tmp/hosts.cfg /tmp/hostgroup.cfg /tmp/service.cfgn";
        copy("/var/backup/nagios/$date/hosts.cfg.bck", $host_file) || die "Cannot copy /var/backup/nagios/$date/hosts.cfg.bck to $host_filen";
        copy("/var/backup/nagios/$date/hostgroups.cfg.bck", $group_file) || die "Cannot copy /var/backup/nagios/$date/hostgroups.cfg.bck to $group_filen";
        copy("/var/backup/nagios/$date/services.cfg.bck", $services_file) || die "Cannot copy /var/backup/nagios/$date/services.cfg.bck $services_filen";

        } else {
                print "Nagios config reports success, restarting nagiosn";
                my $restart = `/etc/init.d/nagios reload`;
                print $restart;
        }

}

###############################################################################
## sub addEntry - adds the unix or windows host entry.
###############################################################################
sub addEntry {

        my $type = shift;
        my $pattern = $ARGV[0];
        my $host_str = &openFile($host_file);
        my $hostgrp_str = &openFile($group_file);
        my $services_str= &openFile($services_file);

        if ($host_str=~/$pattern/) { die "$ARGV[0] already in hosts.cfg, aborting!n"; }
        if ($hostgrp_str=~/$pattern/) { die "$ARGV[0] already in hostgroups.cfg, aborting!n"; }
if(($type eq 'w') || ($type eq 'x')) {
        if ($services_str=~/$pattern/) { die "$ARGV[0] already in services.cfg, aborting!n"; }
}
        # Some sanity checks to help prevent data entry errors
        if (!$ARGV[2]) { print "nNo Server Alias, aborting!nn"; &usage; }
        #if ($ARGV[2]=~/[0-9]{5,8}$/i) {} else { print "No Member ID!n"; exit 0}
        if (!$ARGV[3]) { print "nNo IP Address specified, aborting!nn"; &usage; }
        if ($ARGV[3]=~/[a-z]/i) { print "nIP Address $ARGV[3] is invalid, please double checknn"; exit 0; }
        if ($ARGV[3]=~/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}/) {
                @ip = split(/./, $ARGV[3]);
        } else {
                print "nIP Address $ARGV[3] is invalid, please double checknn"; exit 0;
        }

        if (($ip[0] > 254) || ($ip[1] > 254) ||
            ($ip[3] > 254) || ($ip[4] > 254)) {

        print "nIP Address $ARGV[3] is invalid, please double checknn"; exit 0;
        }

# Passes sanity checks, back up the mo fo.
&backup;
# Check if windows or unix

switch ($type) {

        case "x" {      print "Unix!n";
                        $hostgrp_str =~ s/$unixmatch/$unixmatch,$pattern/g;
                        $services_str =~ s/$unixmatch/$unixmatch,$pattern/g;  }
        case "w" {      print "Windowsn";
                        $hostgrp_str =~ s/$windowsmatch/$windowsmatch,$pattern/g;
                        $services_str =~ s/$windowsmatch/$windowsmatch,$pattern/g; }

        else {          print "VPSn";
                        $hostgrp_str =~ s/$vpsmatch/$vpsmatch,$pattern/g;
                                }
        } # end switch


        # Add it to the host_str buffer.
        $host_str .= "define host{
        use                     sdh-dedicated
        host_name               $ARGV[0]
        alias                   $ARGV[2]
        address                 $ARGV[3]
        }nn";
        open(HF, ">/tmp/hosts.cfg.tmp") || die "Cannot open /tmp/hosts.cfg.tmp";
        print HF $host_str;
        close(HF);
        open(HGF, ">/tmp/hostgroups.cfg.tmp") || die "Cannot open /tmp/hostgroups.cfg.tmp";
        print HGF $hostgrp_str;
        close(HGF);

if(($type eq 'w') || ($type eq 'x')) {
        open(SF, ">/tmp/services.cfg.tmp") || die "Cannot open /tmp/services.cfg.tmp";
        print SF $services_str;
        close(SF);
        }
        &checkNagios;

}
###############################################################################
## sub usage - prints the usage when things don't add up from args
###############################################################################
sub usage{
        print "Usage: /usr/local/bin/nagios-add.pl    nn";
        print "Optional Flags:n";
        print "td delete a servern";
        print "tw add a windows servern";
        print "tx add a unix servern";
        print "tv add a VPS servernn";
        print "eg delete:t./usr/local/bin/nagios-add.pl sdh-server12 dn";
        print "eg add:tt./usr/local/bin/nagios-add.pl sdh-server12 x "sdh-server12 sdh-server12.stardothosting.com MemID:155" 192.168.111.10n";
        exit 0;
}

MySQL Replication : Replicating an existing database

You may remember a previous post about MySQL replication.

I decided to make a revised post detailing the different steps required in order to implement a master / slave replication relationship within two or more MySQL servers.

The steps required are slightly different and I think its important to outline the necessary steps in order to accomplish this task — it may actually save you some troubleshooting! 🙂

    Replication of Existing DBs

If you have existing data on your master that you want to synchronize on your slaves before starting the replication process, then you must stop processing statements on the master, obtain the current position, and then dump the data, before allowing the master to continue executing statements.

If you do not stop the execution of statements, the data dump and the master status information that you use will not match and you will end up with inconsistent or corrupted databases on the slaves.

    PREPARATION OF MASTER SERVER

1. Select a master server. It can be either one.

2. Make sure all databases that you want to replicate to the slave already exist! The easist way is to just copy the database dirs inside your MySQL data directory intact over to your slave, and then recursively chown them to “mysql:mysql”. Remember, the binary structures are file-system dependant, so you can’t do this between MySQL servers on different OS’s. In this instance you will want to use mysqldump most likely.

3. Create /etc/my.cnf if you do not already have one:

[mysqld]
socket=/tmp/mysql.sock [enter YOUR path to mysql.sock here]
server-id=1
log-bin=mysql-bin
binlog-do-db=bossdb     # input the database which should be replicated
binlog-ignore-db=mysql1 # input the database that should be ignored for replication
binlog-ignore-db=mysql2  # input the database that should be ignored for replication

4. Permit your slave server to replicate by issuing the following SQL command (substituting your slave’s IP and preferred password):

mysql> GRANT SUPER,REPLICATION CLIENT,REPLICATION SLAVE,RELOAD ON *.* TO 'replicate'@'192.168.1.1' IDENTIFIED BY 'somepass';

5. Flush all talbes and block write statements :

mysql> FLUSH TABLES WITH READ LOCK;

6. Use the SHOW MASTER STATUS statement to determine the current binary log file name and offset on the master:

mysql > SHOW MASTER STATUS;
+---------------+----------+--------------+------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+---------------+----------+--------------+------------------+
| mysql-bin.003 | 73       | test         | manual,mysql     |
+---------------+----------+--------------+------------------+

Copy the file + position for use in Step 4 of the slave configuration.

7. Create data snapshot to import into slave with mysqldump :

shell> mysqldump -u root -p db_name --lock-all-tables >dbdump.sql

8. Unlock the tables of the database :

mysql> UNLOCK TABLES;

9. Transfer & import the db into the slave

10. Shut down and restart MySQL daemon and verify that all is functional.

PREPARATION OF SLAVE

1. Create /etc/my.cnf if you do not already have one:

[mysqld]
socket=/tmp/mysql.sock [enter YOUR path to mysql.sock here]
server-id=2 [MUST be different to master]
master-host=192.168.1.1
master-user=replicate
master-password=somepass

2. Shut down and restart MySQL on slave.

3. Log into mysql and stop slave :

mysql> stop slave;

4. Set the master configuration on the slave :

mysql> CHANGE MASTER TO
    ->     MASTER_HOST='master_host_name',
    ->     MASTER_USER='replication_user_name',
    ->     MASTER_PASSWORD='replication_password',
    ->     MASTER_LOG_FILE='recorded_log_file_name',
    ->     MASTER_LOG_POS=recorded_log_position;

3. Issue the following SQL command to check status:

mysql> show slave statusG;

Ensure that the following two fields are showing this :

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

If not, try to issue the following command :

mysql> start slave;

This will manually start the slave process. Note that only updated tables and entries after the slave process has started will be sent from the master to the slave — it is not a differential replication.

TESTING

Just update some data on the master, and query that record on the slave. The update should be instantaneous.

Test creating a table on the master MySQL server database :

mysql> use replicateddb;
Database changed

mysql> CREATE TABLE example4( id INT NOT NULL AUTO_INCREMENT,  PRIMARY KEY(id),  name VARCHAR(30),   age INT);
Query OK, 0 rows affected (0.04 sec)
Menu