commit 5b9939ce8e5d9bad2a46ca3977d00b73c61fd9d2 Author: root Date: Sun Oct 19 17:27:33 2014 +0200 first commit diff --git a/aur b/aur new file mode 100755 index 0000000..d829328 --- /dev/null +++ b/aur @@ -0,0 +1,3 @@ +#!/bin/bash + +sudo -u repo HOME=/data/repo local-repo repo "$@" diff --git a/aur-add b/aur-add new file mode 100755 index 0000000..1c0b0e0 --- /dev/null +++ b/aur-add @@ -0,0 +1,14 @@ +#!/bin/bash + +if [ -e $1 ]; then + pkg=/tmp/$1 + cp $1 $pkg + sudo chown repo:repo $pkg + shift + sudo -u repo HOME=/data/repo local-repo repo -a $pkg "$@" +else + sudo -u repo HOME=/data/repo local-repo repo -A "$@" +fi +[[ "$?" != "0" ]] && exit $? +echo Now you can install the package +echo $ pacman -Sy $@ diff --git a/aur-update b/aur-update new file mode 100755 index 0000000..b22a575 --- /dev/null +++ b/aur-update @@ -0,0 +1,3 @@ +#!/bin/bash + +sudo -u repo HOME=/data/repo local-repo repo -U diff --git a/backup-container b/backup-container new file mode 100755 index 0000000..421e4bb --- /dev/null +++ b/backup-container @@ -0,0 +1,120 @@ +#!/usr/bin/env ruby +require 'json' +require 'pathname' +require 'fileutils' +require 'optparse' + +RSYNC_CMD="rsync" +RSYNC_ARGS=["--archive", "--delete", "--numeric-ids"] +ZFS_SNAPSHOT_CMD="zfs-auto-snapshot" +# --quiet --syslog --label=daily --keep=31 backup/rsync" +LXC_PATH=Pathname.new("/data/containers") +BACKUP_LOCATIONS = %w{home srv etc usr/local} +CONFIG_PATH="/etc/lxc/container.json" + +BACKUP_PATH="/backup" +#ZFS_DATASET="backup/rsync" + +def load_config + return JSON.load(File.open(CONFIG_PATH)) +rescue SystemCallError => e + abort "failed to open configuration '#{CONFIG_PATH}', #{e}" +rescue JSON::ParserError => e + abort "failed to parse configuration '#{CONFIG_PATH}', #{e}" +end + +module Utils + def self.sh(cmd, *args) + pretty_args = args.map {|arg| "'#{arg}'"} + puts ([cmd] + pretty_args).join(" ") + system(cmd, *args) + end + + def self.backup(remote_path, local_path) + sh RSYNC_CMD, *RSYNC_ARGS, remote_path, local_path + end +end + +class Container + def initialize(name, backup_paths, backup_scripts) + @name = name + @backup_paths = backup_paths + @backup_scripts = backup_scripts + @path = LXC_PATH.join(name, "rootfs") + end + def backup + backup_paths = BACKUP_LOCATIONS + if @backup_paths.is_a?(Array) + backup_paths += @backup_paths + end + backup_paths.each do |relative_path| + backup_path = @path.join(relative_path) + local_path = Pathname.new(@name).join(relative_path) + FileUtils.mkdir_p(local_path) + + if File.exists?(backup_path) && !empty_directory?(backup_path) + Utils.backup(backup_path.to_s, File.dirname(local_path)) + end + end + end + def run_backup_scripts + if @backup_scripts.is_a?(Array) + @backup_scripts.each do |script| + backup_script(script) + end + end + end + + private + def backup_script(script) + unless script.is_a?(Hash) + abort("backup-scripts: Expected an Object, got #{script.class}") + end + command = script["command"] + if command.nil? + abort("command not set for backup-scripts for container '#{@name}'") + end + backupname = script["backupname"] + if backupname.nil? + abort("backupname not set for backup-scripts for container '#{@name}'") + end + backupname = backupname.gsub("/", "") + FileUtils.mkdir_p(backupname) + puts "cd #{backupname}" + Dir.chdir(backupname) do + Utils.sh(command) + end + end + + def empty_directory?(path) + return false unless Dir.exists?(path) + return Dir.entries(path).size <= 2 # - [".", ".."] + end +end + +options = {} +OptionParser.new do |opts| + opts.on("-l", "--label [LABEL]", String, "daily, hourly") do |label| + options[:label] = label + end + opts.on("-k", "--keep [NUMBER]", Integer, "Number of backups to keep") do |keep| + options[:keep] = keep + end +end.parse! + +puts "cd #{BACKUP_PATH}" +Dir.chdir(BACKUP_PATH) do + BACKUP_LOCATIONS.each do |location| + FileUtils.mkdir_p(location) + Utils.backup("/" + location, "localhost") + end + config = load_config + config["network"].each do |container, data| + next if data["lxc"] == false + container = Container.new(container, data["backup-paths"], data["backup-scripts"]) + container.backup + container.run_backup_scripts + end + #args = ["--label", options[:label], "--keep", options[:keep].to_s, ZFS_DATASET] + #Utils.sh(ZFS_SNAPSHOT_CMD, *args) +end diff --git a/backup-mysql b/backup-mysql new file mode 100755 index 0000000..238f1ed --- /dev/null +++ b/backup-mysql @@ -0,0 +1,16 @@ +#!/bin/bash +# TARGET: Backup-Ziel +# IGNORE: Liste zu ignorierender Datenbanken (durch | getrennt) +IGNORE="mysql|information_schema|performance_schema|test" +PASSWORD="DtkXaU6ZeWeizvcZjRQJqY3no9dGf3ASa7N73Y8Z8PULxJVrvvrq7AAak4s2HvD2" + +DBS="$(/usr/bin/mysql --host="mysql" --user="root" --password="$PASSWORD" -Bse 'show databases' | /usr/bin/grep -Ev $IGNORE)" + +rm -f *.sql.bz2 + +for DB in $DBS; do + /usr/bin/mysqldump --host="mysql" --user="root" --password="$PASSWORD" --skip-extended-insert --skip-comments "$DB" | bzip2 -c > "$DB.sql.bz2" +done + +echo "$0 - Backup erfolgreich durchgefuehrt" +exit 0 diff --git a/backup-postgres b/backup-postgres new file mode 100755 index 0000000..da4915d --- /dev/null +++ b/backup-postgres @@ -0,0 +1,13 @@ +#!/bin/bash + +export PGPASS=/root/.pgpass + +# restore: +# psql -f $database.dump postgres +LIST=$(psql -h postgres -U postgres -At -c "select datname from pg_database order by datname;") +for d in $LIST +do + if [ "$d" != "template0" ]; then + pg_dump -h postgres -U postgres "$d" | gzip -c > "$d.dump.gz" + fi +done diff --git a/create-mysql-user-and-database b/create-mysql-user-and-database new file mode 100755 index 0000000..acb20e9 --- /dev/null +++ b/create-mysql-user-and-database @@ -0,0 +1,28 @@ +#!/bin/sh + +if [ $# -lt 1 ] +then + echo "Usage: $0 " + exit 1 +fi + +if ! [ $EUID -eq 0 ] +then + echo "Must be root!" >&2 + exit 1 +fi + +USER="$1" +PASSWORD="$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64 | xargs)" + +cat <&2 + exit 1 +fi + +if [ $# -lt 2 ] +then + echo "Usage: $0 []" >&2 + exit 1 +fi + +NAME="$1" +DOMAIN="$2" +ROOTFS="/data/containers/$NAME/rootfs" +WEBFS="/data/containers/web/rootfs" +WEBPATH="/srv/http/$DOMAIN" +NGINX="$WEBFS/etc/nginx" +PHP_MODULES=('') +shift 2 + +# handle extra options +while (( "$#" )) +do + case $1 in + mysql) + PHP_MODULES=(mysqli mysql pdo_mysql) + ;; + postgres) + PHP_MODULES=(pgsql pdo_pgsql) + ;; + esac + shift +done + +# clone container +echo "${bold}Cloning container ...$normal" +TEMPFILE=$(mktemp) +ruby -rjson -e 'puts ({php_extensions: ARGV}).to_json' "$PHP_MODULES[@]" > "$TEMPFILE" +lxc-clone -o base -n "$NAME" -- --group php --vars "$TEMPFILE" +rm "$TEMPFILE" + +# configure bind mount +UNIT_NAME=$(systemd-escape --path --suffix=mount "${WEBFS}${WEBPATH}") +cat << EOF > "/etc/systemd/system/$UNIT_NAME" +[Mount] +What = ${ROOTFS}${WEBPATH} +Where = ${WEBFS}${WEBPATH} +Type = none +Options = bind,ro + +[Install] +WantedBy=lxc-mount.target +EOF + +systemctl enable "$UNIT_NAME" +systemctl start "$UNIT_NAME" + +# configure nginx +echo "${bold}Configuring nginx ...$normal" +cat << EOF > "$NGINX/sites-available/$DOMAIN" +server { + listen 80; + listen 443 ssl; + index index.php index.html index.htm; + + server_name $DOMAIN; + + root /srv/http/$DOMAIN; + location ~ \.(php|php5)$ { + fastcgi_pass $NAME:9000; + fastcgi_index index.php; + include fastcgi.conf; + } +} +EOF +chroot "$WEBFS" nginx_ensite "$DOMAIN" + +# restart nginx +echo "${bold}Reload nginx ...$normal" +lxc-attach -n web -- systemctl reload nginx + +# configure filewall +echo "${bold}Configuring firewall ...$normal" +cat << EOF > "/etc/ferm.d/services/45-$NAME" +&def_service($NAME, $NAME, tcp, 9000); +&allow_service_for($NAME, web); +EOF +fw-apply + +# start container +echo "${bold}Starting container ...$normal" +lxc-start -d -n "$NAME" diff --git a/create-postgres-user-and-database b/create-postgres-user-and-database new file mode 100755 index 0000000..b9ab1d7 --- /dev/null +++ b/create-postgres-user-and-database @@ -0,0 +1,33 @@ +#!/bin/sh + +set -eu + +if [ $# -lt 1 ] +then + echo "Usage: $0 []" + exit 1 +fi + +if ! [ $EUID -eq 0 ] +then + echo "Must be root!" >&2 + exit 1 +fi + +USER="$1" +DBPASSWORD="$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 64)" +DATABASE="${2:-$USER}" +export PGPASSFILE=/root/.pgpass + +psql --host postgres --user postgres <&2 + exit 1 +fi + +set -e + +echo -n "Password for data storage: " +read -s password +echo + +echo $password | cryptsetup luksOpen /dev/sda3 zfs_hd_1 +echo $password | cryptsetup luksOpen /dev/sdb3 zfs_hd_2 + +zfs mount -a diff --git a/destroy-php-container b/destroy-php-container new file mode 100755 index 0000000..d58fccd --- /dev/null +++ b/destroy-php-container @@ -0,0 +1,42 @@ +#!/bin/sh + +bold=`tput bold` +normal=`tput sgr0` + +if ! [ $EUID -eq 0 ] +then + echo "Must be root!" >&2 + exit 1 +fi + +if [ $# -lt 2 ] +then + echo "Usage: $0 []" >&2 + exit 1 +fi + +NAME="$1" +DOMAIN="$2" +ROOTFS="/data/containers/$NAME/rootfs" +WEBFS="/data/containers/web/rootfs" +WEBPATH="/srv/http/$DOMAIN" +NGINX="$WEBFS/etc/nginx" + +# destroy container +lxc-stop -n "$NAME" +/usr/bin/lxc-destroy -n "$NAME" + +# unconfigure nginx +chroot "$WEBFS" nginx_dissite "$DOMAIN" +rmdir "$WEBFS$WEBPATH" +rm -f "$WEBFS/etc/nginx/sites-available/$DOMAIN" + +# restart nginx +lxc-attach -n web -- systemctl restart nginx + +# unconfigure firewall +rm -f "/etc/ferm.d/services/45-$NAME" +fw-apply + +# clean up container.json +/etc/lxc/scripts/cleanup-container-file.rb diff --git a/duplicity-backup b/duplicity-backup new file mode 100755 index 0000000..9f90d07 --- /dev/null +++ b/duplicity-backup @@ -0,0 +1,23 @@ +#!/bin/sh +## Remote backup script. Requires duplicity and gpg-agent with the keys and passphrases loaded as root. +## Uses separate encryption and signing keys +## Usage: 'backup_remote.sh' + +enc_key=AF5834A6 +sign_key=AF5834A6 +src="/home" +#dest="scp://u93722@u93722.your-backup.de/duplicity" +dest="file:///mnt/backup/duplicity" + +duplicity --gpg-options "--secret-keyring /data/duplicity/duplicity.sec --keyring /data/duplicity/duplicity.pub" \ + --verbosity notice \ + --encrypt-key "$enc_key" \ + --sign-key "$sign_key" \ + --full-if-older-than 60D \ + --num-retries 3 \ + --asynchronous-upload \ + --volsize 250 \ + --exclude-filelist-stdin \ + --archive-dir /data/duplicity/cache \ + --log-file /var/log/duplicity.log \ + "$src" "$dest" diff --git a/fw-apply b/fw-apply new file mode 100755 index 0000000..bc81fee --- /dev/null +++ b/fw-apply @@ -0,0 +1,15 @@ +#!/bin/sh + +if ! [ $EUID -eq 0 ] +then + echo "Must be root!" >&2 + exit 1 +fi + +cd /etc + +if ferm --interactive --timeout 10 ferm.conf +then + ferm -n -l --domain ip ferm.conf > /etc/iptables/iptables.rules + ferm -n -l --domain ip6 ferm.conf > /etc/iptables/ip6tables.rules +fi diff --git a/list-all-pkgs b/list-all-pkgs new file mode 100755 index 0000000..b342451 --- /dev/null +++ b/list-all-pkgs @@ -0,0 +1,11 @@ +#!/bin/sh + +( +for c in /data/containers/* +do + if [ -d "$c/rootfs" ] + then + pacman -r "$c/rootfs" -Q + fi +done +) | cut -d ' ' -f 1 | sort | uniq diff --git a/lxc-destroy b/lxc-destroy new file mode 100755 index 0000000..101a3a2 --- /dev/null +++ b/lxc-destroy @@ -0,0 +1,3 @@ +#!/bin/bash + +echo use /usr/bin/lxc-destroy instead >&2 diff --git a/lxc-foreach b/lxc-foreach new file mode 100755 index 0000000..3316fdb --- /dev/null +++ b/lxc-foreach @@ -0,0 +1,11 @@ +#!/bin/bash + +for n in `lxc-ls`; do + name=n + r=/data/containers/$n/rootfs + root=$r + c=/data/containers/$n/config + config=$c + eval echo "$ $1" + eval "$1" +done diff --git a/lxc-log b/lxc-log new file mode 100755 index 0000000..d4d6388 --- /dev/null +++ b/lxc-log @@ -0,0 +1,28 @@ +#!/bin/bash + +set -eu + +die() { echo $1 1>&2; exit 1; } +usage() { echo "USAGE: $SCRIPT NAME SERVICE [JOURNALCTL_OPTIONS...]"; exit 0; } + +SCRIPT=${0##*/} +[ $# -lt 2 ] && usage + +case "$1" in + --help|-h) + usage + ;; + *) + CONTAINER=$1; shift + SERVICE=$1; shift + ;; +esac + +CONTAINER_PATH="/data/containers/$CONTAINER" + +[ $EUID -eq 0 ] || die "Needs root permission" +[ -d "$CONTAINER_PATH" ] || die "No such container found: $CONTAINER" + +SYSTEMD_CGROUP="/system.slice/system-lxc.slice/lxc@${CONTAINER}.service/system.slice/${SERVICE}.service" + +journalctl _SYSTEMD_CGROUP=${SYSTEMD_CGROUP} "$@" diff --git a/lxc-path b/lxc-path new file mode 100755 index 0000000..26e2fe8 --- /dev/null +++ b/lxc-path @@ -0,0 +1,3 @@ +#!/bin/bash + +echo /data/containers/$1/rootfs/$2 diff --git a/rsnapshot_cp_zfs b/rsnapshot_cp_zfs new file mode 100755 index 0000000..d0b9dde --- /dev/null +++ b/rsnapshot_cp_zfs @@ -0,0 +1,23 @@ +#!/bin/bash + +# name of the volume +zname=$(dirname $(dirname $(echo $2 | cut -b 2-))) + +# name of the future snapshot +bsname="$zname@rsnap-$(date +%F)" + +sname=$bsname +for i in 1 2 3 4 5 +do + # exit if success + zfs snapshot "$sname" + if [ "x$?" == "x0" ]; then + echo "backup started at $(date)" > $2/info + exit 0 + fi + # iterate over it if already exists + sname=$bsname-v$i +done + +echo ERROR zfs_cp: something is really broken +exit 1 diff --git a/smartdnotify b/smartdnotify new file mode 100755 index 0000000..7fb696a --- /dev/null +++ b/smartdnotify @@ -0,0 +1,7 @@ +#! /bin/sh + +# Send mail +echo "$SMARTD_MESSAGE" | mail -s "$SMARTD_FAILTYPE" "$SMARTD_ADDRESS" + +# Notify user +wall "$SMARTD_MESSAGE" diff --git a/ssl-cert-check b/ssl-cert-check new file mode 100755 index 0000000..a12a230 --- /dev/null +++ b/ssl-cert-check @@ -0,0 +1,764 @@ +#!/bin/bash +# +# Program: SSL Certificate Check +# +# Source code home: http://prefetch.net/code/ssl-cert-check +# +# Documentation: http://prefetch.net/articles/checkcertificate.html +# +# Author: Matty < matty91 at gmail dot com > +# +# Current Version: 3.27 +# +# Revision History: +# +# Version 3.27 +# - Allow white spaces to exist in the certificate file list +# - Add an additional check to pick up bad / non-existent certificates +# - Add a check to look for the existence of a mail program. Error out if it's not present. +# - Enable the TLS -servername extension by default - Juergen Knaack & Johan Denoyer +# +# Version 3.26 +# - Allow the certificate type (PEM, DER, NET) to be passed on the command line +# +# Version 3.25 +# - Check for "no route to host" errors -- Dan Doyle +# - Set RETCODE to 3 (unknown) if a connection error occurs -- Dan Doyle +# - Documentation fixes +# +# Version 3.24 +# - Utilize the -clcerts option to limit the results to client certificates - Eitan Katznelson +# +# Version 3.23 +# - Fixed typo in date2julian routine -- Ken Cook +# +# Version 3.22 +# - Change the validation option to "-V" +# - Add a "-v" option to specify a specific protocol version (ssl2, ssl3 or tls) +# +# Version 3.21 +# - Adjust e-mail checking to avoid exiting if notifications aren't enabled -- Nick Anderson +# - Added the number of days until expiration to the Nagios output -- Nick Anderson +# +# Version 3.20 +# - Fixed a bug in certificate length checking -- Tim Nowaczyk +# +# Version 3.19 +# - Added check to verify the certificate retrieved is valid +# +# Version 3.18 +# - Add support for connecting to FTP servers -- Paul A Sand +# +# Version 3.17 +# - Add support for connecting to imap servers -- Joerg Pareigis +# +# Version 3.16 +# - Add support for connecting to the mail sbmission port -- Luis E. Munoz +# +# Version 3.15 +# - Adjusted the file checking logic to use the correct certificate -- Maciej Szudejko +# - Add sbin to the default search paths for OpenBSD compatibility -- Alex Popov +# - Use cut instead of substring processing to ensure compatibility -- Alex Popov +# +# Version 3.14 +# - Fixed the Common Name parser to handle DN's where the CN is not the last item +# eg. EmailAddr -- Jason Brothers +# - Added the ability to grab the serial number -- Jason Brothers +# - Added the "-b" option to print results without a header -- Jason Brothers +# - Added the "-v" option for certificate validation -- Jason Brothers +# +# Version 3.13 +# - Updated the subject line to include the hostname as well as +# the common name embedded in the X509 certificate (if it's +# available) -- idea proposed by Mike Burns +# +# Version 3.12 +# - Updated the license to allow redistribution and modification +# +# Version 3.11 +# - Added ability to comment out lines in files passed +# to the "-f" option -- Brett Stauner +# - Fixed comment next to file processing logic +# +# Version 3.10 +# - Fixed POP3 port -- Simon Matter +# +# Version 3.9 +# - Switched binary location logic to use which utility +# +# Version 3.8 +# - Fixed display on 80 column displays +# - Cleaned up the formatting +# +# Version 3.7 +# - Fixed bug in NAGIOS tests -- Ben Allen +# +# Version 3.6 +# - Added support for certificates stored in PKCS#12 databases -- Ken Gallo +# - Cleaned up comments +# - Adjusted variables to be more consistent +# +# Version 3.5 +# - Added support for NAGIOS -- Quanah Gibson-Mount +# - Added additional checks for mail -- Quanah Gibson-Mount +# - Convert tabs to spaces -- Quanah Gibson-Mount +# - Cleaned up usage() routine +# - Added additional checks for openssl +# +# Version 3.4 +# - Added a missing "{" to line 364 -- Ken Gallo +# - Move mktemp to the start of the main body to avoid errors +# - Adjusted default binary paths to make sure the script just works +# w/ Solaris, BSD and Linux hosts +# +# Version 3.3 +# - Added common name from X.509 certificate file to E-mail body / header -- Doug Curtis +# - Fixed several documentation errors +# - Use mktemp to create temporary files +# - Convert printf, sed and awk to variables +# - Check for printf, sed, awk and mktemp binaries +# - Add additional logic to make sure mktemp returned a valid temporary file +# +# Version 3.2 +# - Added option to list certificates in the file passed to "-f". +# +# Version 3.1 +# - Added handling for starttls for smtp -- Marco Amrein +# - Added handling for starttls for pop3 (without s) -- Marco Amrein +# - Removed extra spacing at end of script +# +# Version 3.0 +# - Added "-i" option to print certificate issuer +# - Removed $0 from Subject line of outbound e-mails +# - Fixed some typographical errors +# - Removed redundant "-b" option +# +# Version 2.0 +# - Fixed an issue with e-mails formatting incorrectly +# - Added additional space to host column -- Darren-Perot Spruell +# - Replaced GNU date dependency with CHRIS F. A. JOHNSON's +# date2julian shell function. This routine can be found on +# page 170 of Chris's book "Shell Scripting Recipes: A +# Problem-Solution Approach," ISBN #1590594711. Julian function +# was created based on a post to comp.unix.shell by Tapani Tarvainen. +# - Cleaned up function descriptions +# - Removed several lines of redundant code +# - Adjusted the help message +# +# Version 1.1 +# - Added "-c" flag to report expiration status of a PEM encoded +# certificate -- Hampus Lundqvist +# - Updated the prints messages to display the reason a connection +# failed (connection refused, connection timeout, bad cert, etc) +# - Updated the GNU date checking routines +# - Added checks for each binary required +# - Added checks for connection timeouts +# - Added checks for GNU date +# - Added a "-h" option +# - Cleaned up the documentation +# +# Version 1.0 +# Initial Release +# +# Last Updated: 02-27-2013 +# +# Purpose: +# ssl-cert-check checks to see if a digital certificate in X.509 format +# has expired. ssl-cert-check can be run in interactive and batch mode, +# and provides facilities to alarm if a certificate is about to expire. +# +# License: +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# Requirements: +# Requires openssl +# +# Installation: +# Copy the shell script to a suitable location +# +# Tested platforms: +# -- Solaris 9 using /bin/bash +# -- Solaris 10 using /bin/bash +# -- OS X 10.4.2 using /bin/bash +# -- OpenBSD using /bin/sh +# -- FreeBSD using /bin/sh +# -- Centos Linux 3, 4, 5 & 6 using /bin/bash +# -- Redhat Enterprise Linux 3, 4, 5 & 6 using /bin/bash +# +# Usage: +# Refer to the usage() sub-routine, or invoke ssl-cert-check +# with the "-h" option. +# +# Examples: +# Please refer to the following site for documentation and examples: +# http://prefetch.net/articles/checkcertificate.html +PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/ssl/bin:/usr/sfw/bin +export PATH + +# Who to page when an expired certificate is detected (cmdline: -e) +ADMIN="joerg@higgsboson.tk" + +# Number of days in the warning threshhold (cmdline: -x) +WARNDAYS=30 + +# If QUIET is set to TRUE, don't print anything on the console (cmdline: -q) +QUIET="FALSE" + +# Don't send E-mail by default (cmdline: -a) +ALARM="FALSE" + +# Don't run as a Nagios plugin by default (cmdline: -n) +NAGIOS="FALSE" + +# NULL out the PKCSDBPASSWD variable for later use (cmdline: -k) +PKCSDBPASSWD="" + +# Type of certificate (PEM, DER, NET) (cmdline: -t) +CERTTYPE="pem" + +# Protocol version to use (cmdline: -v) +VERSION="" + +# Send along the servername when TLS is used +TLSSERVERNAME="TRUE" + +# Location of system binaries +AWK=$(which awk) +DATE=$(which date) +GREP=$(which grep) +OPENSSL=$(which openssl) +PRINTF=$(which printf) +SED=$(which sed) +MKTEMP=$(which mktemp) + +# Try to find a mail client +if [ -f /usr/bin/mailx ] +then + MAIL="/usr/bin/mailx" +elif [ -f /bin/mail ] +then + MAIL="/bin/mail" +elif [ -f /usr/bin/mail ] +then + MAIL="/usr/bin/mail" +elif [ -f /sbin/mail ] +then + MAIL="/sbin/mail" +elif [ -f /usr/sbin/mail ] +then + MAIL="/usr/sbin/mail" +else + MAIL="cantfindit" +fi + +# Return code used by nagios. Initialize to 0. +RETCODE=0 + +# Set the default umask to be somewhat restrictive +umask 077 + +############################################################################# +# Purpose: Convert a date from MONTH-DAY-YEAR to Julian format +# Acknowledgements: Code was adapted from examples in the book +# "Shell Scripting Recipes: A Problem-Solution Approach" +# ( ISBN 1590594711 ) +# Arguments: +# $1 -> Month (e.g., 06) +# $2 -> Day (e.g., 08) +# $3 -> Year (e.g., 2006) +############################################################################# +date2julian() { + + if [ "${1}" != "" ] && [ "${2}" != "" ] && [ "${3}" != "" ] + then + ## Since leap years add aday at the end of February, + ## calculations are done from 1 March 0000 (a fictional year) + d2j_tmpmonth=$((12 * ${3} + ${1} - 3)) + + ## If it is not yet March, the year is changed to the previous year + d2j_tmpyear=$(( ${d2j_tmpmonth} / 12)) + + ## The number of days from 1 March 0000 is calculated + ## and the number of days from 1 Jan. 4713BC is added + echo $(( (734 * ${d2j_tmpmonth} + 15) / 24 + - 2 * ${d2j_tmpyear} + ${d2j_tmpyear}/4 + - ${d2j_tmpyear}/100 + ${d2j_tmpyear}/400 + $2 + 1721119 )) + else + echo 0 + fi +} + +############################################################################# +# Purpose: Convert a string month into an integer representation +# Arguments: +# $1 -> Month name (e.g., Sep) +############################################################################# +getmonth() +{ + case ${1} in + Jan) echo 1 ;; + Feb) echo 2 ;; + Mar) echo 3 ;; + Apr) echo 4 ;; + May) echo 5 ;; + Jun) echo 6 ;; + Jul) echo 7 ;; + Aug) echo 8 ;; + Sep) echo 9 ;; + Oct) echo 10 ;; + Nov) echo 11 ;; + Dec) echo 12 ;; + *) echo 0 ;; + esac +} + +############################################################################# +# Purpose: Calculate the number of seconds between two dates +# Arguments: +# $1 -> Date #1 +# $2 -> Date #2 +############################################################################# +date_diff() +{ + if [ "${1}" != "" ] && [ "${2}" != "" ] + then + echo $((${2} - ${1})) + else + echo 0 + fi +} + +##################################################################### +# Purpose: Print a line with the expiraton interval +# Arguments: +# $1 -> Hostname +# $2 -> TCP Port +# $3 -> Status of certification (e.g., expired or valid) +# $4 -> Date when certificate will expire +# $5 -> Days left until the certificate will expire +# $6 -> Issuer of the certificate +##################################################################### +prints() +{ + if [ "${QUIET}" != "TRUE" ] && [ "${ISSUER}" = "TRUE" ] && [ "${VALIDATION}" != "TRUE" ] + then + MIN_DATE=$(echo $4 | ${AWK} '{ print $1, $2, $4 }') + if [ "${NAGIOS}" == "TRUE" ] + then + ${PRINTF} "%-35s %-17s %-8s %-11s %-4s %-30s\n" "$1:$2" "$6" "$3" "$MIN_DATE" \|days="$5" + else + ${PRINTF} "%-35s %-17s %-8s %-11s %-4s %-30s\n" "$1:$2" "$6" "$3" "$MIN_DATE" "$5" + fi + elif [ "${QUIET}" != "TRUE" ] && [ "${ISSUER}" = "TRUE" ] && [ "${VALIDATION}" == "TRUE" ] + then + ${PRINTF} "%-35s %-35s %-32s %-17s\n" "$1:$2" "$7" "$8" "$6" + + elif [ "${QUIET}" != "TRUE" ] && [ "${VALIDATION}" != "TRUE" ] + then + MIN_DATE=$(echo $4 | ${AWK} '{ print $1, $2, $4 }') + if [ "${NAGIOS}" == "TRUE" ] + then + ${PRINTF} "%-47s %-12s %-12s %-4s %-30s\n" "$1:$2" "$3" "$MIN_DATE" \|days="$5" + else + ${PRINTF} "%-47s %-12s %-12s %-4s %-30s\n" "$1:$2" "$3" "$MIN_DATE" "$5" + fi + elif [ "${QUIET}" != "TRUE" ] && [ "${VALIDATION}" == "TRUE" ] + then + ${PRINTF} "%-35s %-35s %-32s\n" "$1:$2" "$7" "$8" + fi +} + + +#################################################### +# Purpose: Print a heading with the relevant columns +# Arguments: +# None +#################################################### +print_heading() +{ + if [ "${NOHEADER}" != "TRUE" ] + then + if [ "${QUIET}" != "TRUE" ] && [ "${ISSUER}" = "TRUE" ] && [ "${NAGIOS}" != "TRUE" ] && [ "${VALIDATION}" != "TRUE" ] + then + ${PRINTF} "\n%-35s %-17s %-8s %-11s %-4s\n" "Host" "Issuer" "Status" "Expires" "Days" + echo "----------------------------------- ----------------- -------- ----------- ----" + + elif [ "${QUIET}" != "TRUE" ] && [ "${ISSUER}" = "TRUE" ] && [ "${NAGIOS}" != "TRUE" ] && [ "${VALIDATION}" == "TRUE" ] + then + ${PRINTF} "\n%-35s %-35s %-32s %-17s\n" "Host" "Common Name" "Serial #" "Issuer" + echo "----------------------------------- ----------------------------------- -------------------------------- -----------------" + + elif [ "${QUIET}" != "TRUE" ] && [ "${NAGIOS}" != "TRUE" ] && [ "${VALIDATION}" != "TRUE" ] + then + ${PRINTF} "\n%-47s %-12s %-12s %-4s\n" "Host" "Status" "Expires" "Days" + echo "----------------------------------------------- ------------ ------------ ----" + + elif [ "${QUIET}" != "TRUE" ] && [ "${NAGIOS}" != "TRUE" ] && [ "${VALIDATION}" == "TRUE" ] + then + ${PRINTF} "\n%-35s %-35s %-32s\n" "Host" "Common Name" "Serial #" + echo "----------------------------------- ----------------------------------- --------------------------------" + fi + fi +} + + +########################################## +# Purpose: Describe how the script works +# Arguments: +# None +########################################## +usage() +{ + echo "Usage: $0 [ -e email address ] [ -x days ] [-q] [-a] [-b] [-h] [-i] [-n] [-v]" + echo " { [ -s common_name ] && [ -p port] } || { [ -f cert_file ] } || { [ -c certificate file ] }" + echo "" + echo " -a : Send a warning message through E-mail" + echo " -b : Will not print header" + echo " -c cert file : Print the expiration date for the PEM or PKCS12 formatted certificate in cert file" + echo " -e E-mail address : E-mail address to send expiration notices" + echo " -f cert file : File with a list of FQDNs and ports" + echo " -h : Print this screen" + echo " -i : Print the issuer of the certificate" + echo " -k password : PKCS12 file password" + echo " -n : Run as a Nagios plugin" + echo " -p port : Port to connect to (interactive mode)" + echo " -s commmon name : Server to connect to (interactive mode)" + echo " -t type : Specify the certificate type" + echo " -q : Don't print anything on the console" + echo " -v : Specify a specific protocol version to use (tls, ssl2, ssl3)" + echo " -V : Only print validation data" + echo " -x days : Certificate expiration interval (eg. if cert_date < days)" + echo "" +} + + +########################################################################## +# Purpose: Connect to a server ($1) and port ($2) to see if a certificate +# has expired +# Arguments: +# $1 -> Server name +# $2 -> TCP port to connect to +########################################################################## +check_server_status() { + + if [ "_${2}" = "_smtp" -o "_${2}" = "_25" ] + then + TLSFLAG="-starttls smtp" + + elif [ "_${2}" = "_ftp" -o "_${2}" = "_21" ] + then + TLSFLAG="-starttls ftp" + + elif [ "_${2}" = "_pop3" -o "_${2}" = "_110" ] + then + TLSFLAG="-starttls pop3" + + elif [ "_${2}" = "_imap" -o "_${2}" = "_143" ] + then + TLSFLAG="-starttls imap" + elif [ "_${2}" = "_submission" -o "_${2}" = "_587" ] + then + TLSFLAG="-starttls smtp -port ${2}" + elif [ "_${2}" = "_xmpp-client" -o "_${2}" = "_xmpp-server" -o \ + "_${2}" = "_5269" -o "_${2}" = "_5222" ] + then + TLSFLAG="-starttls xmpp" + else + TLSFLAG="" + fi + + if [ "${VERSION}" != "" ] + then + VER="-${VERSION}" + fi + + if [ "${TLSSERVERNAME}" = "TRUE" ] + then + TLSFLAG="${TLSFLAG} -servername $1" + fi + + echo "" | ${OPENSSL} s_client ${VER} -connect ${1}:${2} ${TLSFLAG} 2> ${ERROR_TMP} 1> ${CERT_TMP} + + if ${GREP} -i "Connection refused" ${ERROR_TMP} > /dev/null + then + prints ${1} ${2} "Connection refused" "Unknown" + RETCODE=3 + + elif ${GREP} -i "No route to host" ${ERROR_TMP} > /dev/null + then + prints ${1} ${2} "No route to host" "Unknown" + RETCODE=3 + + elif ${GREP} -i "gethostbyname failure" ${ERROR_TMP} > /dev/null + then + prints ${1} ${2} "Cannot resolve domain" "Unknown" + RETCODE=3 + + elif ${GREP} -i "Operation timed out" ${ERROR_TMP} > /dev/null + then + prints ${1} ${2} "Operation timed out" "Unknown" + RETCODE=3 + + elif ${GREP} -i "ssl handshake failure" ${ERROR_TMP} > /dev/null + then + prints ${1} ${2} "SSL handshake failed" "Unknown" + RETCODE=3 + + elif ${GREP} -i "connect: Connection timed out" ${ERROR_TMP} > /dev/null + then + prints ${1} ${2} "Connection timed out" "Unknown" + RETCODE=3 + + else + check_file_status ${CERT_TMP} $1 $2 + fi +} + +##################################################### +### Check the expiration status of a certificate file +### Accepts three parameters: +### $1 -> certificate file to process +### $2 -> Server name +### $3 -> Port number of certificate +##################################################### +check_file_status() { + + CERTFILE=${1} + HOST=${2} + PORT=${3} + + ### Check to make sure the certificate file exists + if [ ! -r ${CERTFILE} ] || [ ! -s ${CERTFILE} ] + then + echo "ERROR: The file named ${CERTFILE} is unreadable or doesn't exist" + echo "ERROR: Please check to make sure the certificate for ${HOST}:${PORT} is valid" + RETCODE=1 + return + fi + + ### Grab the expiration date from the X.509 certificate + if [ "${PKCSDBPASSWD}" != "" ] + then + # Extract the certificate from the PKCS#12 database, and + # send the informational message to /dev/null + ${OPENSSL} pkcs12 -nokeys -in ${CERTFILE} \ + -out ${CERT_TMP} -clcerts -password pass:${PKCSDBPASSWD} 2> /dev/null + + # Extract the expiration date from the certificate + CERTDATE=$(${OPENSSL} x509 -in ${CERT_TMP} -enddate -noout | \ + ${SED} 's/notAfter\=//') + + # Extract the issuer from the certificate + CERTISSUER=$(${OPENSSL} x509 -in ${CERT_TMP} -issuer -noout | \ + ${AWK} 'BEGIN {RS="/" } $0 ~ /^O=/ \ + { print substr($0,3,17)}') + + ### Grab the common name (CN) from the X.509 certificate + COMMONNAME=$(${OPENSSL} x509 -in ${CERT_TMP} -subject -noout | \ + ${SED} -e 's/.*CN=//' | \ + ${SED} -e 's/\/.*//') + + ### Grab the serial number from the X.509 certificate + SERIAL=$(${OPENSSL} x509 -in ${CERT_TMP} -serial -noout | \ + ${SED} -e 's/serial=//') + else + # Extract the expiration date from the ceriticate + CERTDATE=$(${OPENSSL} x509 -in ${CERTFILE} -enddate -noout -inform ${CERTTYPE} | \ + ${SED} 's/notAfter\=//') + + # Extract the issuer from the certificate + CERTISSUER=$(${OPENSSL} x509 -in ${CERTFILE} -issuer -noout -inform ${CERTTYPE} | \ + ${AWK} 'BEGIN {RS="/" } $0 ~ /^O=/ { print substr($0,3,17)}') + + ### Grab the common name (CN) from the X.509 certificate + COMMONNAME=$(${OPENSSL} x509 -in ${CERTFILE} -subject -noout -inform ${CERTTYPE} | \ + ${SED} -e 's/.*CN=//' | \ + ${SED} -e 's/\/.*//') + ### Grab the serial number from the X.509 certificate + SERIAL=$(${OPENSSL} x509 -in ${CERTFILE} -serial -noout -inform ${CERTTYPE} | \ + ${SED} -e 's/serial=//') + fi + + ### Split the result into parameters, and pass the relevant pieces to date2julian + set -- ${CERTDATE} + MONTH=$(getmonth ${1}) + + # Convert the date to seconds, and get the diff between NOW and the expiration date + CERTJULIAN=$(date2julian ${MONTH#0} ${2#0} ${4}) + CERTDIFF=$(date_diff ${NOWJULIAN} ${CERTJULIAN}) + + if [ ${CERTDIFF} -lt 0 ] + then + if [ "${ALARM}" = "TRUE" ] + then + echo "The SSL certificate for ${HOST} \"(CN: ${COMMONNAME})\" has expired!" \ + | ${MAIL} -s "Certificate for ${HOST} \"(CN: ${COMMONNAME})\" has expired!" ${ADMIN} + fi + + prints ${HOST} ${PORT} "Expired" "${CERTDATE}" "${CERTDIFF}" "${CERTISSUER}" "${COMMONNAME}" "${SERIAL}" + RETCODE=2 + + elif [ ${CERTDIFF} -lt ${WARNDAYS} ] + then + if [ "${ALARM}" = "TRUE" ] + then + echo "The SSL certificate for ${HOST} \"(CN: ${COMMONNAME})\" will expire on ${CERTDATE}" \ + | ${MAIL} -s "Certificate for ${HOST} \"(CN: ${COMMONNAME})\" will expire in ${WARNDAYS}-days or less" ${ADMIN} + fi + prints ${HOST} ${PORT} "Expiring" "${CERTDATE}" "${CERTDIFF}" "${CERTISSUER}" "${COMMONNAME}" "${SERIAL}" + RETCODE=1 + + else + prints ${HOST} ${PORT} "Valid" "${CERTDATE}" "${CERTDIFF}" "${CERTISSUER}" "${COMMONNAME}" "${SERIAL}" + RETCODE=0 + fi +} + +################################# +### Start of main program +################################# +while getopts abinv:e:f:c:hk:p:s:t:qx:V option +do + case "${option}" + in + a) ALARM="TRUE";; + b) NOHEADER="TRUE";; + c) CERTFILE=${OPTARG};; + e) ADMIN=${OPTARG};; + f) SERVERFILE=$OPTARG;; + h) usage + exit 1;; + i) ISSUER="TRUE";; + k) PKCSDBPASSWD=${OPTARG};; + n) NAGIOS="TRUE";; + p) PORT=$OPTARG;; + s) HOST=$OPTARG;; + t) CERTTYPE=$OPTARG;; + q) QUIET="TRUE";; + v) VERSION=$OPTARG;; + V) VALIDATION="TRUE";; + x) WARNDAYS=$OPTARG;; + \?) usage + exit 1;; + esac +done + +### Check to make sure a openssl utility is available +if [ ! -f ${OPENSSL} ] +then + echo "ERROR: The openssl binary does not exist in ${OPENSSL}." + echo "FIX: Please modify the \${OPENSSL} variable in the program header." + exit 1 +fi + +### Check to make sure a date utility is available +if [ ! -f ${DATE} ] +then + echo "ERROR: The date binary does not exist in ${DATE} ." + echo "FIX: Please modify the \${DATE} variable in the program header." + exit 1 +fi + +### Check to make sure a grep utility is available +if [ ! -f ${GREP} ] +then + echo "ERROR: The grep binary does not exist in ${GREP} ." + echo "FIX: Please modify the \${GREP} variable in the program header." + exit 1 +fi + +### Check to make sure the mktemp and printf utilities are available +if [ ! -f ${MKTEMP} ] || [ ! -f ${PRINTF} ] +then + echo "ERROR: Unable to locate the mktemp or printf binary." + echo "FIX: Please modify the \${MKTEMP} and \${PRINTF} variables in the program header." + exit 1 +fi + +### Check to make sure the sed and awk binaries are available +if [ ! -f ${SED} ] || [ ! -f ${AWK} ] +then + echo "ERROR: Unable to locate the sed or awk binary." + echo "FIX: Please modify the \${SED} and \${AWK} variables in the program header." + exit 1 +fi + +### Check to make sure a mail client is available it automated notifications are requested +if [ "${ALARM}" = "TRUE" ] && [ ! -f ${MAIL} ] +then + echo "ERROR: You enabled automated alerts, but the mail binary could not be found." + echo "FIX: Please modify the ${MAIL} variable in the program header." + exit 1 +fi + +# Place to stash temporary files +CERT_TMP=$($MKTEMP /var/tmp/cert.XXXXXX) +ERROR_TMP=$($MKTEMP /var/tmp/error.XXXXXX) + +### Baseline the dates so we have something to compare to +MONTH=$(${DATE} "+%m") +DAY=$(${DATE} "+%d") +YEAR=$(${DATE} "+%Y") +NOWJULIAN=$(date2julian ${MONTH#0} ${DAY#0} ${YEAR}) + +### Touch the files prior to using them +if [ ! -z "${CERT_TMP}" ] && [ ! -z "${ERROR_TMP}" ] +then + touch ${CERT_TMP} ${ERROR_TMP} +else + echo "ERROR: Problem creating temporary files" + echo "FIX: Check that mktemp works on your system" + exit 1 +fi + +### If a HOST and PORT were passed on the cmdline, use those values +if [ "${HOST}" != "" ] && [ "${PORT}" != "" ] +then + print_heading + check_server_status "${HOST}" "${PORT}" + +### If a file is passed to the "-f" option on the command line, check +### each certificate or server / port combination in the file to see if +### they are about to expire +elif [ -f "${SERVERFILE}" ] +then + print_heading + egrep -v '(^#|^$)' ${SERVERFILE} | while read HOST PORT + do + if [ "$PORT" = "FILE" ] + then + check_file_status ${HOST} "FILE" "${HOST}" + else + check_server_status "${HOST}" "${PORT}" + fi + done + +### Check to see if the certificate in CERTFILE is about to expire +elif [ "${CERTFILE}" != "" ] +then + print_heading + check_file_status ${CERTFILE} "FILE" "${CERTFILE}" + +### There was an error, so print a detailed usage message and exit +else + usage + exit 1 +fi + +### Remove the temporary files +rm -f ${CERT_TMP} ${ERROR_TMP} + +### Exit with a success indicator +if [ "${NAGIOS}" = "TRUE" ]; then + exit $RETCODE +else + exit 0 +fi diff --git a/update-motd b/update-motd new file mode 100755 index 0000000..f1c9454 --- /dev/null +++ b/update-motd @@ -0,0 +1,76 @@ +#!/bin/bash +#Script to update motd with relevant information. + +#Define output file +motd="/etc/motd" + +# Collect information +# $USER is automatically defined +HOSTNAME=`uname -n` +KERNEL=`uname -rm` +INSTALLED_KERNEL=$(pacman -Qi linux-lts | awk '/Version/ { print $3 }') +CPU=`awk -F '[ :][ :]+' '/^model name/ { print $2; exit; }' /proc/cpuinfo` +CPU_TEMP=$(($( $motd + +echo -e " + $A. $X + $A/#\ $X _ $A _ _ + $A/###\ $X __ _ _ __ ___| |__ $A| (_)_ __ _ ___ __ + $A/#####\ $X / _' | '__/ __| '_ \ $A| | | '_ \| | | \ \/ / + $A/##.-.##\ $X | (_| | | | (__| | | |$A| | | | | | |_| |> < + $A/##( )##\ $X \__,_|_| \___|_| |_|$A|_|_|_| |_|\__,_/_/\_\\ + $A/#.-- --.#\ $X +$A/' '\ $B +" >> $motd + +echo -e "$R===============================================================" >> $motd +echo -e " $R KERNEL $W= $KERNEL (installed: $INSTALLED_KERNEL) " >> $motd +echo -e " $R CPU $W= $CPU " >> $motd +echo -e " $R HOSTNAME $W= $HOSTNAME " >> $motd +echo -e " $R SYSTEM $W= $PACMAN packages can be updated " >> $motd +echo -e " $R USERS $W= Currently `users | wc -w` users logged on " >> $motd +echo -e "$R===============================================================" >> $motd +echo -e " $R CPU Usage $W= $LOAD1 1 min $LOAD5 5 min $LOAD15 15 min " >> $motd +echo -e " $R CPU Temperature $W= $CPU_TEMP" >> $motd +echo -e " $R Memory Used $W= $MEMORY1 / $MEMORY2 " >> $motd +echo -e " $R Swap in use $W= `free -m | tail -n 1 | awk '{print $3}'` MB " >> $motd +echo -e " $R Processes $W= You are running $PSU of $PSA processes " >> $motd +echo -e " $R System Uptime $W= $upDays days $upHours hours $upMins minutes $upSecs seconds " >> $motd +echo -e " $R Disk Space Used (md0) $W= $DISC " >> $motd +echo -e "$R===============================================================" >> $motd +echo -en "$W" >> $motd +zpool list >> $motd +echo -e "$R===============================================================" >> $motd +script -c "systemctl --failed" /dev/null | grep -E '●| UNIT' >> $motd +echo -e "$X" >> $motd