Managing Infrastructure with RapidIdentity Part 2: Managing Linux Hosts

    

Information technology teams tend to have a lot on their plate in terms of installing, maintaining, and protecting various systems throughout the enterprise. The larger the environment, the darker the cloud that tends to loom overhead when it comes to managing even trivial tasks, such as installing management agents or antivirus and keeping them up-to-date. Typically, this is due to the fact that there simply aren’t enough hands on the team, nor hours in the day, to touch dozens or hundreds of machines in disparate datacenters.

Outline

In this post, I’ll provide one example of how RapidIdentity (specifically RapidConnect) can help to automate much of the manual effort in accomplishing these tasks. More specifically, we’ll deal with the consistent installation and update of the free ClamAV antivirus engine for Linux. I’ll show you how to use RapidConnect actions to automatically authenticate to multiple Linux servers and execute the commands that are necessary to perform the following tasks:

  • Check each machine for a running ClamAV process (clamd).
  • If present, update both the application and virus signature database.
  • If not present, install ClamAV, and ensure the virus signature database is up-to-date.
  • Log each host as it is processed, such that any errors may be investigated.
  • At the end, log each host with installation status, as well as totals of all existing installations, new installs, and signatures updated.

A Challenge

Before we begin, I’ll put forth a challenge for the reader’s own undertaking.  In the following example, every command (including modifying files) is done line-by-line, such that it can provide better understanding to you, the reader.  However, execution using this method will be slower than most would like, as the action iterates through each server individually.  In doing so, considerable time is lost waiting on processes like antivirus engine installs and signature updates to complete on each host, before moving on to the next.  I challenge you to make this quicker, such that the process moves on and doesn’t take so long on the installations or updates.

Hint — instead of running all of the commands from the RapidConnect appliance, pipe certain commands to scripts on the Linux servers (or even copy script files down), depending upon the status of ClamAV on each host.  Then, let the servers run them locally, while RapidConnect moves on to the next host. This will speed up execution for the entire action set. You’ll notice that I create a shell script for cron in order to run scheduled daily scans of the /home directory.  Perhaps, pattern your installations and updates in the same manner.

A Solution

I’ll preface the example below by saying that this action set was written specifically for CentOS / RedHat hosts (or any that use yum for package management).  The action can easily be adapted or modified for others that use apt-get or other package management, but for the sake of example, we’ll be using yum.  With that out of the way, let’s begin!

First, our action set will open our globals file to ensure we have our consistent username, public / private key, and key password Global variables available to us, and we’ll initialize some counters.

openGlobals:  {
    # Open Globals
    globals()
}
counters:  {
    alreadyInstalled = 0
    newlyInstalled = 0
    updatedIfNeeded = 0
}


Next, we’ll open an input file containing information on our linux hosts, and create a master record (hostRecords) to store our host ClamAV installation data in later.  

openInputFile:  {
    # Open input file containing Linux hosts
    linuxHosts = openDelimitedTextInput("/input/linuxHosts.csv", "ip,os")
}
hostRecords:  {
    hostRecords = createRecord()
}


With our variables set and our input data ready, we’ll begin iterating through linuxHosts to check for ClamAV installations and handle each host accordingly.  We begin with a forEach loop.

clamAVLogic:  {
    # Iterate hosts
    forEach(host, linuxHosts) {
        log("Current host: " + host['ip'], "blue")


We establish a ‘currentHost’ record to use for installation status reporting at the end of the action, open a CLI connection to the host utilizing our Global.linuxUser and our Globals for our public and private key pair, and then, check to see if ClamAV is already running.

        recordForReporting:  {
            currentHost = createRecord()
            setRecordFieldValue(currentHost, "ip", host['ip'])
        }
        openCLI:  {
            # Open CLI connection
            linCLI = openRemoteCLIWithCert(host['ip'], Global.linuxUser, Global.privateKey, Global.publicKey, )
        }
        checkRunning:  {
            clamRunning = runRemoteCLICommand(linCLI, "pgrep clamd")
            clamRunning['output'] = stringReplaceAll(clamRunning['output'], "\n", "")
            if(clamRunning['output'] && (Integer.parseInt(clamRunning['output']) > 0)) {
                clamFound = true
            } else {
                clamFound = false
            }
        }


If we find a running clamd process, we perform an update to ensure the engine is at the most recent level, and set our currentHost record status, indicating that we’ve found an existing ClamAV installation:

            if(clamFound) {
                updateClavAVToLatest = runRemoteCLICommand(linCLI, "yum update clamd clamav -y")
                setRecordFieldValue(currentHost, "installStatus", "Exists and already running")
                alreadyInstalled = alreadyInstalled + 1


If we don’t find a running clamd process, we make sure we have the proper yum software repository to install from (adding it if needed), then install and start the clamd service.  Our currentHost record is updated, indicating that we have just installed ClamAV.

            } else {
                checkForEpelRepository:  {
                    repoCheck = runRemoteCLICommand(linCLI, "file /etc/yum.repos.d/epel.repo")
                    repoMissing = stringContains(repoCheck['output'], "No such file or directory", true)
                }
                addOrUpdateEpelRepoConfigAsNeeded:  {
                    if(repoMissing) {
                        repoInstalled = runRemoteCLICommand(linCLI, "yum install epel-release -y")
                    } else {
                        repoEnabled = stringContains(repoCheck['output'], "enabled=1", true)
                        repoDisabled = stringContains(repoCheck['output'], "enabled=0", true)
                        if(!repoEnabled) {
                            if(repoDisabled) {
                                runRemoteCLICommand(linCLI, "perl -pi -e 's/enabled=0/enabled=1/g' /etc/yum.repos.d/epel.repo")
                            } else {
                            }
                        } else {
                        }
                    }
                }
                installAndStartClamAV:  {
                    runRemoteCLICommand(linCLI, "yum install clamav clamd -y")
                    clamAVOn = runRemoteCLICommand(linCLI, "/etc/init.d/clamd on")
                    clamAVServiceEnabled = runRemoteCLICommand(linCLI, "chkconfig clamd on")
                    clamAVStart = runRemoteCLICommand(linCLI, "/etc/init.d/clamd start")
                    setRecordFieldValue(currentHost, "installStatus", "Installed during this run")
                    newlyInstalled = newlyInstalled + 1
                }
            }


Now that we know we know ClamAV is installed, we update the virus database signatures...

            updateVirusDatabase:  {
                stopFreshClam = runRemoteCLICommand(linCLI, "/usr/bin/freshclam stop")
                updateClamAVSignatures = runRemoteCLICommand(linCLI, "/usr/bin/freshclam")
                setRecordFieldValue(currentHost, "updated", "Updated")
                updatedIfNeeded = updatedIfNeeded + 1
            }
        }


...and we create a bash script and schedule a cron job on the host, in order to run daily virus scans on the /home directory (or others of our choosing).

        cronJobCheck:  {
            # cron exists check
            cronExistsCheck = runRemoteCLICommand(linCLI, "file /etc/cron.daily/manual_clamscan")
            cronMissing = stringContains(cronExistsCheck['output'], "No such file or directory")
            if(!cronMissing) {
            } else {
                # setupScanHomeDirs
                fileLineOne = runRemoteCLICommand(linCLI, "echo '#!/bin/bash' >> /etc/cron.daily/manual_clamscan")
                fileLineTwo = runRemoteCLICommand(linCLI, "echo 'SCAN_DIR=\"/home\"' >> /etc/cron.daily/manual_clamscan")
                fileLineThree = runRemoteCLICommand(linCLI, "echo 'LOG_FILE=\"/var/log/clamav/manual_clamscan.log\"' >> /etc/cron.daily/manual_clamscan")
                fileLineFour = runRemoteCLICommand(linCLI, "echo '/usr/bin/clamscan -i -r $SCAN_DIR >> $LOG_FILE\n' >> /etc/cron.daily/manual_clamscan")
                chmodCronJob = runRemoteCLICommand(linCLI, "chmod +x /etc/cron.daily/manual_clamscan")
            }
        }


At this point, we’re finished executing actions on the Linux host, so we close our CLI connection, append our host installation data to our hostRecords record for logging purposes, and the forEach loop repeats until all hosts in the input file have been processed, at which time, we close our input file.

        closeCLI:  {
            # Close CLI connection
            close(linCLI)
        }
    }
    appendHostToHostRecords:  {
        addRecordFieldValue(hostRecords, host['ip'], currentHost)
    }
    log("--------------------------------------------------------------", "black")
}
closeInputFile:  {
    # Close input file
    close(linuxHosts)
}


To conclude our action, the install status is logged for all hosts, and the counters from the job are logged to indicate how many were already installed, how many the current run had to install, and how many were processed in total.

logResults:  {
    iterateAllHostsForLog:  {
        forEach(hostRecord, hostRecords) {
            log("Host: " + hostRecord['ip'] + " --- Status of clamAV before or during this run: " + hostRecord['installStatus'])
        }
    }
    log("--------------------------------------------------------------", "black")
    log("Total hosts processed: " + hostRecords.length, "black")
    log("--------------------------------------------------------------", "black")
    log("clamAV was already installed on " + alreadyInstalled + " hosts.", "blue")
    log("clamAV was newly installed (this run) on " + newlyInstalled + " hosts.", "green")
    log("clamAV was updated to current signature data on " + updatedIfNeeded + " hosts.", "blue")
}


Conclusion

The example action set above demonstrated how your IT teams can utilize RapidConnect to perform routine tasks cleanly and efficiently, without the need for manual intervention, freeing up your staff to accomplish other tasks and duties.  It helps to ensure consistent application of software and / or settings across multiple servers in a distributed environment, providing logging for audit purposes and ‘peace of mind’ in knowing that tasks have been performed on all systems as required to maintain operational business functions and security metrics.  In our next blog post in the “Managing Infrastructure with RapidIdentity” series, we’ll examine a similar usage example within a Microsoft Windows environment.

Files

ClamAVLinuxHosts

openGlobals:  {
    # Open Globals
    globals()
}
counters:  {
    alreadyInstalled = 0
    newlyInstalled = 0
    updatedIfNeeded = 0
}
openInputFile:  {
    # Open input file containing Linux hosts
    linuxHosts = openDelimitedTextInput("/input/linuxHosts.csv", "ip,os")
}
hostRecords:  {
    hostRecords = createRecord()
}
clamAVLogic:  {
    # Iterate hosts
    forEach(host, linuxHosts) {
        log("Current host: " + host['ip'], "blue")
        recordForReporting:  {
            currentHost = createRecord()
            setRecordFieldValue(currentHost, "ip", host['ip'])
        }
        openCLI:  {
            # Open CLI connection
            linCLI = openRemoteCLIWithCert(host['ip'], Global.linuxUser, Global.privateKey, Global.publicKey, )
        }
        checkRunning:  {
            clamRunning = runRemoteCLICommand(linCLI, "pgrep clamd")
            clamRunning['output'] = stringReplaceAll(clamRunning['output'], "\n", "")
            if(clamRunning['output'] && (Integer.parseInt(clamRunning['output']) > 0)) {
                clamFound = true
            } else {
                clamFound = false
            }
        }
        installOrUpdate: (false) {
            if(clamFound) {
                updateClavAVToLatest = runRemoteCLICommand(linCLI, "yum update clamd clamav -y")
                setRecordFieldValue(currentHost, "installStatus", "Exists and already running")
                alreadyInstalled = alreadyInstalled + 1
            } else {
                checkForEpelRepository:  {
                    repoCheck = runRemoteCLICommand(linCLI, "file /etc/yum.repos.d/epel.repo")
                    repoMissing = stringContains(repoCheck['output'], "No such file or directory", true)
                }
                addOrUpdateEpelRepoConfigAsNeeded:  {
                    if(repoMissing) {
                        repoInstalled = runRemoteCLICommand(linCLI, "yum install epel-release -y")
                    } else {
                        repoEnabled = stringContains(repoCheck['output'], "enabled=1", true)
                        repoDisabled = stringContains(repoCheck['output'], "enabled=0", true)
                        if(!repoEnabled) {
                            if(repoDisabled) {
                                runRemoteCLICommand(linCLI, "perl -pi -e 's/enabled=0/enabled=1/g' /etc/yum.repos.d/epel.repo")
                            } else {
                            }
                        } else {
                        }
                    }
                }
                installAndStartClamAV:  {
                    runRemoteCLICommand(linCLI, "yum install clamav clamd -y")
                    clamAVOn = runRemoteCLICommand(linCLI, "/etc/init.d/clamd on")
                    clamAVServiceEnabled = runRemoteCLICommand(linCLI, "chkconfig clamd on")
                    clamAVStart = runRemoteCLICommand(linCLI, "/etc/init.d/clamd start")
                    setRecordFieldValue(currentHost, "installStatus", "Installed during this run")
                    newlyInstalled = newlyInstalled + 1
                }
            }
            updateVirusDatabase:  {
                stopFreshClam = runRemoteCLICommand(linCLI, "/usr/bin/freshclam stop")
                updateClamAVSignatures = runRemoteCLICommand(linCLI, "/usr/bin/freshclam")
                setRecordFieldValue(currentHost, "updated", "Updated")
                updatedIfNeeded = updatedIfNeeded + 1
            }
        }
        cronJobCheck:  {
            # cron exists check
            cronExistsCheck = runRemoteCLICommand(linCLI, "file /etc/cron.daily/manual_clamscan")
            cronMissing = stringContains(cronExistsCheck['output'], "No such file or directory")
            if(!cronMissing) {
            } else {
                # setupScanHomeDirs
                fileLineOne = runRemoteCLICommand(linCLI, "echo '#!/bin/bash' >> /etc/cron.daily/manual_clamscan")
                fileLineTwo = runRemoteCLICommand(linCLI, "echo 'SCAN_DIR=\"/home\"' >> /etc/cron.daily/manual_clamscan")
                fileLineThree = runRemoteCLICommand(linCLI, "echo 'LOG_FILE=\"/var/log/clamav/manual_clamscan.log\"' >> /etc/cron.daily/manual_clamscan")
                fileLineFour = runRemoteCLICommand(linCLI, "echo '/usr/bin/clamscan -i -r $SCAN_DIR >> $LOG_FILE\n' >> /etc/cron.daily/manual_clamscan")
                chmodCronJob = runRemoteCLICommand(linCLI, "chmod +x /etc/cron.daily/manual_clamscan")
            }
        }
        closeCLI:  {
            # Close CLI connection
            close(linCLI)
        }
    }
    appendHostToHostRecords:  {
        addRecordFieldValue(hostRecords, host['ip'], currentHost)
    }
    log("--------------------------------------------------------------", "black")
}
closeInputFile:  {
    # Close input file
    close(linuxHosts)
}
logResults:  {
    iterateAllHostsForLog:  {
        forEach(hostRecord, hostRecords) {
            log("Host: " + hostRecord['ip'] + " --- Status of clamAV before or during this run: " + hostRecord['installStatus'])
        }
    }
    log("--------------------------------------------------------------", "black")
    log("Total hosts processed: " + hostRecords.length, "black")
    log("--------------------------------------------------------------", "black")
    log("clamAV was already installed on " + alreadyInstalled + " hosts.", "blue")
    log("clamAV was newly installed (this run) on " + newlyInstalled + " hosts.", "green")
    log("clamAV was updated to current signature data on " + updatedIfNeeded + " hosts.", "blue")
}

 

 

dispelling-the-mythos-of-sso-portals-vs-full-featured-iam-systems

Comments

Subscribe Here!