Library - System [CentOS7]

by alrux
0 deployments · 0 still active · last rev. 2 months ago

System library for CentOS nodes.

Compatible with: CentOS 7
						#!/bin/bash
#
# StackScript Library - System
#
# Copyright (c) 2015 ALRUX Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice, this
# list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# * Neither the name of ALRUX Inc. nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific prior
# written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.


###########################################################
# Updates
###########################################################

function system_update {
	# updates all installed packages

	yum -y update
}

function system_ensure_basics {
	# enables the EPEL repository and makes sure some basic packages are installed

	yum -y install epel-release
	yum -y install psmisc sudo wget htop git
}

function system_start_etc_dir_versioning {
	# starts version-tracking of /etc using git

	cd /etc
	git init
	git add .
	git commit -m "Initial state"
}

function system_record_etc_dir_changes {
	# records changes to /etc in a git commit with the provided message
	#
	# $1 - commit message (default: current timestamp)

	COMMENT="$1"
	cd /etc
	git add --all .
	if [[ -z "$COMMENT" ]]; then
		COMMENT=`date`
	fi
	git commit -m "$COMMENT"
}


###########################################################
# Mail
###########################################################

function postfix_install_loopback_only {
	# installs Postfix and sets it up to only accept mail on the loopback interface

	yum -y install postfix mailx
	yum -y remove sendmail

	/usr/sbin/postconf -e 'inet_interfaces = loopback-only'
	/usr/sbin/postconf -e 'mynetworks_style = host'
	# uncomment line below if you prefer mail to originate from user@example.com instead of user@host.example.com
	# /usr/sbin/postconf -e 'myorigin = $mydomain'
	/usr/sbin/postconf -e 'relayhost = $mydomain'
	/usr/sbin/postconf -e 'mydestination = $myhostname, localhost.$mydomain, localhost'

	touch /tmp/restart-postfix
}

function forward_root_mail {
	# forwards root's email to an off-site address
	#
	# $1 - off-site email address to forward to

	ADDRESS="$1"

	if [[ -z "$ADDRESS" ]]; then
		echo "forward_root_mail() requires the off-site email address as its first argument"
		return 1;
	fi

	echo "root: $ADDRESS" >> /etc/aliases
	newaliases
}


###########################################################
# SSH and firewall
###########################################################

function iptables_setup {
	# installs and sets up iptables (IPv4 and IPv6) with a "block all incoming, allow all outgoing" rule set

	yum -y install iptables-services patch

	# patch init script to add 'security' table - default install doesn't know how to handle it
	cat > ~/iptables.patch <<'EOD'
--- old/iptables.init	2014-06-10 01:02:35.000000000 -0400
+++ new/iptables.init	2015-07-21 22:13:52.205065750 -0400
@@ -147,6 +147,12 @@
     for i in $tables; do
 	echo -n "$i "
 	case "$i" in
+	    security)
+		$IPTABLES -t security -P INPUT $policy \
+		    && $IPTABLES -t security -P OUTPUT $policy \
+		    && $IPTABLES -t security -P FORWARD $policy \
+		    || let ret+=1
+		;;
 	    raw)
 		$IPTABLES -t raw -P PREROUTING $policy \
 		    && $IPTABLES -t raw -P OUTPUT $policy \
EOD

	patch -u /usr/libexec/iptables/iptables.init < ~/iptables.patch
	patch -u /usr/libexec/iptables/ip6tables.init < ~/iptables.patch

	cat > /etc/sysconfig/iptables <<EOD
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp -m limit --limit 10/sec -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
COMMIT
EOD

	cat > /etc/sysconfig/ip6tables <<EOD
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -p ipv6-icmp -m limit --limit 10/sec -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
COMMIT
EOD

	chkconfig iptables on

	touch /tmp/restart-iptables
	touch /tmp/restart-ip6tables
}

function iptables_open {
	# opens an port for incoming connections over the specified protocol
	#
	# $1 - port to open (required)
	# $2 - protocol (default: tcp)

	PORT="$1"
	PROTO=`lower $2`

	if [[ -z "$PORT" ]]; then
		echo "iptables_open() requires a port as its first argument"
		return 1;
	fi

	if [[ "$PROTO" != "tcp" ]] && [[ "$PROTO" != "udp" ]]; then
		PROTO="tcp"
	fi

	iptables -A INPUT -p "$PROTO" -m "$PROTO" --dport "$PORT" -j ACCEPT
	ip6tables -A INPUT -p "$PROTO" -m "$PROTO" --dport "$PORT" -j ACCEPT
	service iptables save
	service ip6tables save

	touch /tmp/restart-iptables
	touch /tmp/restart-ip6tables
}

function ssh_set_config {
	# sets a configuration option for sshd
	#
	# $1 - The name of the setting to change (required)
	# $2 - The value of the setting to change (required)

	NAME="$1"
	VALUE="$2"

	if [[ -z "$NAME" ]] || [[ -z "$VALUE" ]]; then
		echo "ssh_set_config() requires a setting name and a value as arguments"
		return 1;
	fi

	sed -i "s/^[\s#]*\($NAME\).*/\1 $VALUE/" /etc/ssh/sshd_config
}

function ssh_setup {
	# sets sshd to listen on given port and disables root and password-based login.
	#
	# $1 - The port to listen on (required)

	PORT="$1"

	if [[ -z "$PORT" ]]; then
		echo "ssh_setup() requires a port number as its first argument"
		return 1;
	fi

	ssh_set_config "Port" "$PORT"
	# semanage port -a -t ssh_port_t -p tcp "$PORT"

	ssh_set_config "PermitRootLogin" "no"
	ssh_set_config "PasswordAuthentication" "no"
	ssh_set_config "GSSAPIAuthentication" "no"
	ssh_set_config "UsePAM" "yes"
	ssh_set_config "X11Forwarding" "no"
	ssh_set_config "Subsystem\s\+sftp" "internal-sftp"

	iptables_open "$PORT"
	touch /tmp/restart-sshd
}

function ssh_sftp_only {
	# sets up a user or group with sftp-only access to ssh
	#
	# $1 - "user" or "group" (case-insensitive; default: user)
	# $2 - user or group name (required)
	# $3 - chroot target (default: %h)

	NAME="$2"
	CHROOT_DIR="$3"

	if [[ -z "$NAME" ]]; then
		echo "ssh_sftp_only() requires a username as its second argument"
		return 1;
	fi

	if [[ `lower $1` == "group" ]]; then
		MATCH="Group"
	else
		MATCH="User"
	fi

	if [[ -z "$CHROOT_DIR" ]]; then
		CHROOT_DIR="%h"
	fi

	cat >> /etc/ssh/sshd_config <<EOD
Match $MATCH $NAME
		ChrootDirectory $CHROOT_DIR
		ForceCommand internal-sftp
		AllowTcpForwarding no

EOD

	touch /tmp/restart-sshd
}


###########################################################
# Users
###########################################################

function user_home_dir {
	# returns the home directory of the given user
	#
	# $1 - username (required)

	USERNAME="$1"

	if [[ -z "$USERNAME" ]]; then
		echo "user_home_dir() requires a username as its first argument"
		return 1;
	fi

	grep "^$USERNAME" /etc/passwd | cut -f6 -d:
}

function user_gen_keys {
	# creates a user and generates a key pair
	#
	# $1 - username (required)

	USERNAME="$1"

	if [[ -z "$USERNAME" ]]; then
		echo "user_gen_keys() requires a username as its first argument"
		return 1;
	fi

	HOSTNAME=$(hostname)
	HOME_DIR=$(user_home_dir "${USERNAME}")

	if [[ ! -d "$HOME_DIR" ]]; then
		echo "user_gen_keys() could not find the home directory of $USERNAME"
		return 1;
	fi

	mkdir -p "$HOME_DIR/.ssh"
	chmod go-w "$HOME_DIR"
	chmod 700 "$HOME_DIR/.ssh"
	ssh-keygen -q -t rsa -b 4096 -C "$USERNAME@$HOSTNAME" -f "$HOME_DIR/.ssh/id_rsa" -N ""
	chown -R "$USERNAME":"$USERNAME" "$HOME_DIR/.ssh"
	chmod 600 "$HOME_DIR/.ssh/id_rsa"
	cat "$HOME_DIR/.ssh/id_rsa.pub"
}

function user_add_pubkey {
	# adds a public key to authorized_keys for the specified user.
	#
	# $1 - username (required)
	# $2 - public key (required)

	USERNAME="$1"
	USERPUBKEY="$2"

	if [[ -z "$USERNAME" ]] || [[ -z "$USERPUBKEY" ]]; then
		echo "user_add_pubkey() requires a username and a pubkey as arguments"
		return 1;
	fi

	HOME_DIR=$(user_home_dir "${USERNAME}")

	if [[ ! -d "$HOME_DIR" ]]; then
		echo "user_gen_keys() could not find the home directory of $USERNAME"
		return 1;
	fi

	mkdir -p $HOME_DIR/.ssh
	chmod go-w "$HOME_DIR"
	chmod 700 "$HOME_DIR/.ssh"
	echo "$USERPUBKEY" >> "$HOME_DIR/.ssh/authorized_keys"
	chown -R "$USERNAME":"$USERNAME" "$HOME_DIR/.ssh"
	chmod 600 "$HOME_DIR/.ssh/authorized_keys"
}

function get_github_user_keys {
	# gets public keys from a GitHub user account.
	#
	# $1 - GitHub user (required)

	GH_USER="$1"

	if [[ -z "$GH_USER" ]]; then
		echo "get_github_user_keys() requires a GitHub user as its first argument"
		return 1;
	fi

	wget -q -O- "https://github.com/${GH_USER}.keys"
}

function user_lock {
	# locks user account
	#
	# $1 - username (required)

	USERNAME="$1"

	if [[ -z "$USERNAME" ]]; then
		echo "user_lock_passwd() requires a username as its first argument"
		return 1;
	fi

	passwd -l $USERNAME
}

function user_sudoer_nopasswd {
	# gives the user passwordless sudo/su
	#
	# $1 - username (required)

	USERNAME="$1"

	if [[ -z "$USERNAME" ]]; then
		echo "user_sudoer_nopasswd() requires a username as its first argument"
		return 1;
	fi

	if [[ -d /etc/sudoers.d ]]; then
	  SUDOERS="/etc/sudoers.d/$USERNAME"
	else
	  SUDOERS=/etc/sudoers
	fi

	echo "$USERNAME ALL=NOPASSWD: ALL" >> "$SUDOERS"
	chmod 0440 "$SUDOERS"
}

function user_niceties {
	sed -i -e "s/alias ll='ls -l/alias ll='ls -al/" /etc/profile.d/colorls.sh
}

###########################################################
# utility functions
###########################################################


function system_primary_ip {
	# returns the primary IP assigned to eth0

	echo $(ifconfig eth0 | awk -F: '/inet addr:/ {print $2}' | awk '{ print $1 }')
}

function get_rdns {
	# calls host on an IP address and returns its reverse dns

	if [[ ! -e /usr/bin/host ]]; then
		yum -y install bind-utils > /dev/null
	fi

	echo $(host $1 | awk '/pointer/ {print $5}' | sed 's/\.$//')
}

function get_rdns_primary_ip {
	# returns the reverse dns of the primary IP assigned to this system

	echo $(get_rdns $(system_primary_ip))
}

function set_hostname {
	# sets the hostname
	#
	# $1 - The hostname to set

	if [[ -z "$1" ]]; then
		echo "set_hostname() requires a host name as its first argument"
		return 1;
	fi

	echo "HOSTNAME=$1" >> /etc/sysconfig/network
	hostname "$1"
	# echo $(system_primary_ip) $(get_rdns_primary_ip) $(hostname) >> /etc/hosts
	echo $(system_primary_ip) $(hostname) >> /etc/hosts
}

function set_timezone {
	# sets the timezone
	#
	# $1 - The timezone name to be set (default: "America/Toronto")

	if [[ -z "$1" ]];
		then ZONE="America/Toronto"
		else ZONE="$1"
	fi

	if [[ ! -e "/usr/share/zoneinfo/$ZONE" ]]; then
		echo "invalid timezone"
		return 1;
	fi

	ln -sf "/usr/share/zoneinfo/$ZONE" /etc/localtime
}

function add_host {
	# adds an entry to the hosts file
	#
	# $1 - The IP address to set a hosts entry for
	# $2 - The FQDN to set to the IP

	IPADDR="$1"
	FQDN="$2"

	if [[ -z "$IPADDR" ]] || [[ -z "$FQDN" ]]; then
		echo "undefined IP address and/or FQDN"
		return 1;
	fi

	echo $IPADDR $FQDN  >> /etc/hosts
}

function restart_services {
	# restarts services that have a file named /tmp/restart-[[service-name]]

	for SERVICE in $(ls /tmp/restart-* | cut -d- -f2); do
		service $SERVICE restart
		rm -f /tmp/restart-$SERVICE
	done
}

function disable_services {
	# disables services that have a file named /tmp/disable-[[service-name]]

	for SERVICE in $(ls /tmp/disable-* | cut -d- -f2); do
		chkconfig --level=12345 $SERVICE off
		service $SERVICE stop
		rm -f /tmp/disable-$SERVICE
	done
}

function random_string {
	# generates a random string of given length
	#
	# $1 - The length of the string to generate (default: 20)

	if [[ -z "$1" ]];
		then LEN=20
		else LEN="$1"
	fi

	echo $(</dev/urandom tr -dc A-Za-z0-9 | head -c $LEN)
}

function lower {
	# lower case the first argument

	echo $1 | tr '[:upper:]' '[:lower:]'
}