You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

266 lines
6.5 KiB
Bash

#!/bin/bash
set -euo pipefail
# Services Manager for 42l
ABS_FULLPATH=$(realpath $0)
ABS_FOLDER=$(dirname $(realpath $0))
SRV_FOLDER=$ABS_FOLDER/services
IMG_FOLDER=$ABS_FOLDER/images
# load global vars
if [ -f "$ABS_FOLDER/global-vars.sh" ]; then
set -a
. $ABS_FOLDER/global-vars.sh
set +a
fi
## service-related functions
check_service_exists()
{
SRV_EXISTS=`ls $SRV_FOLDER | grep -w $1 || true`
if [ "$SRV_EXISTS" == "" ]; then
echo "Specified service does not exist."
exit 1
fi
}
check_image_exists()
{
IMG_EXISTS=`ls $IMG_FOLDER | grep -w $1 || true`
if [ ! $IMG_EXISTS ]; then
echo "Specified image does not exist."
exit 1
fi
}
list_services()
{
ls $SRV_FOLDER
}
build_service()
{
check_service_exists $1
if [ -f "$SRV_FOLDER/$1/buildtime.env" ]; then
env $(cat $SRV_FOLDER/$1/buildtime.env | xargs) docker compose -f $SRV_FOLDER/$1/docker-compose.yml build
else
docker compose -f $SRV_FOLDER/$1/docker-compose.yml build
fi
}
start_service()
{
check_service_exists $1
if [ -f "$SRV_FOLDER/$1/buildtime.env" ]; then
env $(cat $SRV_FOLDER/$1/buildtime.env | xargs) docker compose -f $SRV_FOLDER/$1/docker-compose.yml up -d
else
docker compose -f $SRV_FOLDER/$1/docker-compose.yml up -d
fi
}
stop_service()
{
check_service_exists $1
if [ -f "$SRV_FOLDER/$1/buildtime.env" ]; then
env $(cat $SRV_FOLDER/$1/buildtime.env | xargs) docker compose -f $SRV_FOLDER/$1/docker-compose.yml down
else
docker compose -f $SRV_FOLDER/$1/docker-compose.yml down
fi
}
restart_service()
{
check_service_exists $1
stop_service $1
start_service $1
}
reload_service()
{
check_service_exists $1
if [ -f "$SRV_FOLDER/$1/buildtime.env" ]; then
env $(cat $SRV_FOLDER/$1/buildtime.env | xargs) docker compose -f $SRV_FOLDER/$1/docker-compose.yml restart
else
docker compose -f $SRV_FOLDER/$1/docker-compose.yml restart
fi
}
pull_service()
{
check_service_exists $1
if [ -f "$SRV_FOLDER/$1/buildtime.env" ]; then
env $(cat $SRV_FOLDER/$1/buildtime.env | xargs) docker compose -f $SRV_FOLDER/$1/docker-compose.yml pull
else
docker compose -f $SRV_FOLDER/$1/docker-compose.yml pull
fi
}
## image-related functions
list_images()
{
ls $IMG_FOLDER
}
build_image()
{
check_image_exists $1
if [ -f "$IMG_FOLDER/$1/buildtime.env" ]; then
docker build -t "local/$1" $(for i in `cat $IMG_FOLDER/$1/buildtime.env`; do out+="--build-arg $i "; done; echo $out; out="") $IMG_FOLDER/$1/
else
docker build -t "local/$1" $IMG_FOLDER/$1/
fi
}
start_image()
{
check_image_exists $1
if [ -f "$IMG_FOLDER/$1/runtime.env" ]; then
export $(cat $IMG_FOLDER/$1/runtime.env | xargs)
fi
if [ -f $IMG_FOLDER/$1/start.sh ]; then
$IMG_FOLDER/$1/start.sh
else
echo "$1 is not runnable (base image)."
fi
}
image_main()
{
if [ "$1" = "list" ]; then
list_images
elif [ -n "$2" ]; then
case $1 in
build) build_image $2;;
start) start_image $2;;
*) usage;;
esac
else
usage
fi
}
## audit-related functions
audit_all() {
audit_tabheader
for srv in $(ls $SRV_FOLDER); do
# do not audit services marked as disabled
if [ ! -f "$SRV_FOLDER/$srv/.disabled" ]; then
audit_single $srv ""
fi
done
}
audit_single() {
if [ "$2" == "+header" ]; then
audit_tabheader
fi
printf "%-14s\t" "$1"
RUNNING=$(docker ps -q -f name=$1)
if [[ -z $RUNNING ]]; then
c_red "DOWN\t"
else
c_green "UP!\t"
fi
chkbool "$(grep "pids_limit" $SRV_FOLDER/$1/docker-compose.yml | xargs | sed 's/pids_limit: //g')"
echo -ne "\t\t"
chkbool "$(grep "cpu_shares" $SRV_FOLDER/$1/docker-compose.yml | xargs | sed 's/cpu_shares: //g')"
echo -ne "\t\t"
chkbool "$(grep "mem_limit" $SRV_FOLDER/$1/docker-compose.yml | xargs | sed 's/mem_limit: //g')"
echo -ne "\t\t"
if ! [[ -z $RUNNING ]]; then
chkbool "$(docker inspect --format '{{ .Id }}:ReadonlyRootfs={{ .HostConfig.ReadonlyRootfs }}' $1 | grep -o "true" | sed 's/true/YES/g')"
echo -ne "\t\t"
#chkbool $(ps -p $(docker inspect --format='{{ .State.Pid }}' $1) -o user | tail -n 1 | sed 's/root//g')
chkbool "$(ps -p $(docker inspect --format='{{ .State.Pid }}' $1) -o user | tail -n 1 | sed 's/root//g' | sed -E 's/.+/YES/g')"
echo -ne "\t\t"
chkbool "$(docker inspect --format '{{ .Id }}:SecurityOpt={{ .HostConfig.SecurityOpt }}' $1 | grep "no-new-privileges" | sed -E 's/.+/YES/g')"
echo -ne "\t\t"
chkbool "$($(docker run --rm -it --net container:$1 nicolaka/netshoot netstat -tnul | tail -n +3 | awk '$1=$1' | cut -d ' ' -f4 | grep -q '0.0.0.0') || echo "YES")"
echo -ne "\t\t"
fi
echo ""
}
audit_tabheader() {
echo -e "NAME\t\tSTATUS\tPID LIMIT\tCPU SHARES\tRAM LIMIT\tREAD-ONLY\tUNPRIVILEGED\tRESTR.PRIV.\tFIXED ADDR."
}
chkbool() {
if [[ -z "$1" ]]; then
c_red "NO"
else
c_green "$1"
fi
}
c_red() {
echo -ne '\033[0;31m'$1'\033[0m'
}
c_green() {
echo -ne '\033[0;32m'$1'\033[0m'
}
## main
register_manager() {
if [ ! -f "/usr/local/bin/manager" ]; then
echo "Create symbolic link…"
sudo ln -s $ABS_FULLPATH /usr/local/bin/manager
fi
echo "Add autocompletions for the fish shell…"
mkdir -p ~/.config/fish/completions/
cp $ABS_FOLDER/tools/setup/conf/manager.fish ~/.config/fish/completions/
}
usage()
{
echo -e "Usage:\t$0 list\n"
echo -e "\t$0 <build|start|stop|restart|reload|pull> <service_name>"
echo -e "\t$0 image list"
echo -e "\t$0 image <build|start> <service_name>"
echo -e "\t$0 audit [service_name]"
echo -e "\t$0 register"
}
if [ -z "${1:-""}" ]; then
usage
elif [ "$1" = "list" ]; then
list_services
elif [ "$1" = "audit" ] && [ -z "${2:-""}" ]; then
audit_all
elif [ "$1" = "register" ] && [ -z "${2:-""}" ]; then
register_manager
elif [ -n "$2" ]; then
case $1 in
build) build_service $2;;
start) start_service $2;;
stop) stop_service $2;;
restart) restart_service $2;;
reload) reload_service $2;;
pull) pull_service $2;;
image) image_main $2 ${3:-""};;
audit) audit_single $2 "+header";;
*) usage;;
esac
else
usage
fi