second commit
This commit is contained in:
Executable
+221
@@ -0,0 +1,221 @@
|
||||
#!/bin/bash
|
||||
|
||||
DEFAULT_BACKUP_FOLDER="$HOME/RECOVERY_MARIADB"
|
||||
|
||||
confirm() {
|
||||
read -rp "$1 ([y]es or [N]o): "
|
||||
case $(echo "$REPLY" | tr '[:upper:]' '[:lower:]') in
|
||||
y | yes) echo "yes" ;;
|
||||
*) echo "no" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
synopsis() {
|
||||
echo "usage: "
|
||||
printf "\t list | console | connections\n"
|
||||
printf "\t ---------------------------\n"
|
||||
printf "\t use <DB_NAME>\n"
|
||||
printf "\t create <DB_NAME> [PASSWORD]\n"
|
||||
printf "\t ---------------------------\n"
|
||||
printf "\t backup <DB_NAME> [FOLDER]\n"
|
||||
printf "\t restore <DB_NAME> <FILE>\n"
|
||||
printf "\t ---------------------------\n"
|
||||
printf "\t rename <DB_NAME> <NEW_NAME>\n"
|
||||
}
|
||||
|
||||
list() {
|
||||
lxc exec ct1 -- sh -c "echo \"SELECT schema_name FROM information_schema.schemata where schema_name not in ('information_schema','mariadb','mysql','performance_schema')\" | mariadb -u root --skip-column-names -r "
|
||||
}
|
||||
|
||||
console() {
|
||||
if [[ -z $1 ]]; then
|
||||
lxc exec ct1 -- mariadb -u root
|
||||
else
|
||||
lxc exec ct1 -- sh -c "echo \"$1\" | mariadb -u root"
|
||||
fi
|
||||
}
|
||||
|
||||
connections() {
|
||||
lxc exec ct1 -- sh -c "echo \"select id, user, host, db, command, time, state, info, progress from information_schema.processlist\" | mariadb -u root "
|
||||
}
|
||||
|
||||
use() {
|
||||
lxc exec ct1 -- mariadb -u root "$DB_NAME"
|
||||
}
|
||||
|
||||
create() {
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source /opt/debian-bash/lib/functions.sh
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
mapfile -t DBs < <(list)
|
||||
|
||||
local NEW_DB="${1:-$DB_NAME}"
|
||||
local NEW_PASSWORD="${2:-$NEW_DB}"
|
||||
|
||||
if ! containsElement DBs "$NEW_DB"; then
|
||||
lxc exec ct1 -- sh -c "echo \"\
|
||||
CREATE DATABASE \\\`$NEW_DB\\\`; \
|
||||
GRANT ALL ON \\\`$NEW_DB\\\`.* TO \\\`$NEW_DB\\\`@'%' IDENTIFIED BY '$NEW_PASSWORD'; \
|
||||
FLUSH PRIVILEGES; \
|
||||
\" | mariadb -u root"
|
||||
else
|
||||
echo "$NEW_DB already exists!"
|
||||
fi
|
||||
}
|
||||
|
||||
backup() {
|
||||
|
||||
if [[ ! -d "$FOLDER" ]]; then
|
||||
echo "error: Folder required!"
|
||||
file "$FOLDER"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mkdir -p "$FOLDER"
|
||||
DATE=$(date '+%F')
|
||||
ARCHIVE="$FOLDER"/$DB_NAME-$DATE.mariadb.gz
|
||||
|
||||
if [[ -f $ARCHIVE ]]; then
|
||||
VERSION_CONTROL=numbered mv -b "$ARCHIVE" "$FOLDER"/"$DB_NAME"-"$DATE"-daily.mariadb.gz
|
||||
fi
|
||||
|
||||
echo "backup $DB_NAME into $FOLDER"
|
||||
mariadb-dump -h ct1.lxd -u "$DB_NAME" -p"$DB_NAME" "$DB_NAME" | gzip >"$ARCHIVE"
|
||||
echo "archive file created: $ARCHIVE"
|
||||
}
|
||||
|
||||
restore() {
|
||||
|
||||
echo "restore $DB_NAME $FILE"
|
||||
if [[ ! -f "$FILE" ]]; then
|
||||
echo "error: Backup file (*.mariadb.gz) required!"
|
||||
file "$FILE"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
PROCESSES=$(lxc exec ct1 -- sh -c "echo \"select id, user, host, db, command, time, state, info, progress from information_schema.processlist\" | mariadb -u root")
|
||||
|
||||
set +e
|
||||
PROCESS_COUNT=$(echo "$PROCESSES" | grep -c "$DB_NAME")
|
||||
if [[ $PROCESS_COUNT -gt 0 ]]; then
|
||||
echo "FAILURE: There are some connections to database, please consider stopping bound services"
|
||||
echo
|
||||
echo "$PROCESSES"
|
||||
exit 2
|
||||
fi
|
||||
set -e
|
||||
|
||||
if [[ "yes" == $(confirm "RESTORATION will drop DATABASE, please acknowledge with care!!!") ]]; then
|
||||
if list | grep -q "$DB_NAME"; then
|
||||
echo "backup <$DB_NAME> for safety reason"
|
||||
backup
|
||||
echo "drop database <$DB_NAME>"
|
||||
lxc exec ct1 -- sh -c "echo \"DROP DATABASE \\\`$DB_NAME\\\`\" | mariadb -u root"
|
||||
fi
|
||||
|
||||
echo "create <$DB_NAME>"
|
||||
create
|
||||
# lxc exec ct1 -- sh -c "CREATE DATABASE \\\`$DB_NAME\\\`\" | mariadb -u root"
|
||||
gunzip -c "$FILE" | grep -av "^CREATE DATABASE" | grep -av "^USE" | mariadb -h ct1.lxd -u "$DB_NAME" -p"$DB_NAME" "$DB_NAME"
|
||||
echo RESTORATION completed successfully
|
||||
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
rename() {
|
||||
echo "rename $DB_NAME to $NEW_NAME"
|
||||
local DB_NAME_FOUND=false
|
||||
for database in $(list); do
|
||||
if [[ $database == "$DB_NAME" ]]; then
|
||||
DB_NAME_FOUND=true
|
||||
fi
|
||||
if [[ $database == "$NEW_NAME" ]]; then
|
||||
echoerr "$NEW_NAME already exists! please provide another name instead of <$NEW_NAME> or run list command"
|
||||
exit 20
|
||||
fi
|
||||
done
|
||||
if [[ ! $DB_NAME_FOUND ]]; then
|
||||
echoerr "source <$DB_NAME> does not exist!"
|
||||
exit 20
|
||||
fi
|
||||
|
||||
if [[ "$DB_NAME" == "$NEW_NAME" ]]; then
|
||||
echowarn "no need to rename; no change required <$DB_NAME>"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "create new database <$NEW_NAME>"
|
||||
create "$NEW_NAME"
|
||||
|
||||
for table in $(console "use '$DB_NAME'; show tables"); do
|
||||
if [[ $table != "Tables_in_$DB_NAME" ]]; then
|
||||
echo "renaming table \`$DB_NAME\`.$table to \`$NEW_NAME\`.$table"
|
||||
console "use '$DB_NAME'; rename table \\\`$DB_NAME\\\`.$table to \\\`$NEW_NAME\\\`.$table;"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "every table has been renamed, so remove old database <$DB_NAME>"
|
||||
console "drop user \\\`$DB_NAME\\\`"
|
||||
console "drop database \\\`$DB_NAME\\\`"
|
||||
}
|
||||
|
||||
# MAIN
|
||||
set -Eeuo pipefail
|
||||
# shellcheck source=/dev/null
|
||||
. "$MIAOU_BASEDIR/lib/functions.sh"
|
||||
|
||||
[[ $# -lt 1 ]] && synopsis && exit 1
|
||||
ACTION=$1
|
||||
|
||||
case $ACTION in
|
||||
console)
|
||||
shift
|
||||
TAIL=$*
|
||||
console "$TAIL"
|
||||
;;
|
||||
list)
|
||||
list
|
||||
;;
|
||||
connections)
|
||||
connections
|
||||
;;
|
||||
use)
|
||||
[[ $# -lt 2 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
use
|
||||
;;
|
||||
create)
|
||||
[[ $# -lt 2 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
DB_PASSWORD=${3:-$DB_NAME}
|
||||
create
|
||||
;;
|
||||
backup)
|
||||
[[ $# -lt 2 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
FOLDER=${3:-$DEFAULT_BACKUP_FOLDER}
|
||||
backup
|
||||
;;
|
||||
restore)
|
||||
[[ $# -lt 3 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
FILE=$3
|
||||
FOLDER=${4:-$DEFAULT_BACKUP_FOLDER}
|
||||
DB_PASSWORD="$DB_NAME"
|
||||
restore
|
||||
;;
|
||||
rename)
|
||||
[[ $# -lt 3 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
NEW_NAME=$3
|
||||
rename
|
||||
;;
|
||||
*)
|
||||
synopsis
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Executable
+200
@@ -0,0 +1,200 @@
|
||||
#!/bin/bash
|
||||
|
||||
confirm() {
|
||||
read -p "$1 ([y]es or [N]o): "
|
||||
case $(echo $REPLY | tr '[A-Z]' '[a-z]') in
|
||||
y | yes) echo "yes" ;;
|
||||
*) echo "no" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
synopsis() {
|
||||
echo "usage: "
|
||||
printf "\t list | console | connections\n"
|
||||
printf "\t ---------------------------\n"
|
||||
printf "\t use <DB_NAME>\n"
|
||||
printf "\t lookup <DB_NAME> <TERM>\n"
|
||||
printf "\t create <DB_NAME> [PASSWORD]\n"
|
||||
printf "\t ---------------------------\n"
|
||||
printf "\t backup <DB_NAME> [FOLDER]\n"
|
||||
printf "\t restore <DB_NAME> <FILE> [--yes]\n"
|
||||
printf "\t ---------------------------\n"
|
||||
printf "\t rename <DB_NAME> <NEW_NAME>\n"
|
||||
}
|
||||
|
||||
list() {
|
||||
lxc exec ct1 -- su - postgres -c "psql -Atc \"SELECT datname FROM pg_database WHERE datistemplate=false AND datname<>'postgres';\""
|
||||
}
|
||||
|
||||
console() {
|
||||
if [[ -z $1 ]]; then
|
||||
lxc exec ct1 -- su - postgres
|
||||
else
|
||||
lxc exec ct1 -- su - postgres -c "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
connections() {
|
||||
PROCESSES=$(console "psql -c \"select pid as process_id, usename as username, datname as database_name, client_addr as client_address, application_name, backend_start, state, state_change from pg_stat_activity WHERE datname<>'postgres' ORDER BY datname, usename;\"")
|
||||
printf "$PROCESSES\n"
|
||||
}
|
||||
|
||||
use() {
|
||||
echo >&2 "about to connect to <${DB_NAME}> ..."
|
||||
if [[ -z $1 ]]; then
|
||||
lxc exec ct1 -- su - postgres -c "psql $DB_NAME"
|
||||
else
|
||||
local sql="psql -A -t $DB_NAME -c \\\"$1;\\\""
|
||||
local command="su - postgres -c \"$sql\""
|
||||
lxc exec ct1 -- sh -c "$command"
|
||||
fi
|
||||
}
|
||||
|
||||
create() {
|
||||
echo >&2 "about to create to <${DB_NAME}> ..."
|
||||
source /opt/debian-bash/lib/functions.sh
|
||||
local DBs=($(list))
|
||||
if ! $(containsElement DBs $DB_NAME); then
|
||||
local SQL="CREATE USER \\\\\\\"$DB_NAME\\\\\\\" WITH PASSWORD '$DB_PASSWORD'"
|
||||
local command="su - postgres sh -c \"psql -c \\\"$SQL\\\"\" && su - postgres sh -c \"createdb -O $DB_NAME $DB_NAME\" && echo CREATE DB"
|
||||
# echo $command
|
||||
lxc exec ct1 -- sh -c "$command"
|
||||
else
|
||||
echo $DB_NAME already exists!
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
lookup() {
|
||||
if [[ ${#TERM} -ge 4 ]]; then
|
||||
echo >&2 "about to lookup term <${TERM}> over all tables of database <$DB_NAME> ..."
|
||||
local command="pg_dump --data-only --inserts $DB_NAME 2>/dev/null | grep --color \"$TERM\""
|
||||
lxc exec ct1 -- su - postgres -c "$command"
|
||||
else
|
||||
echo "term <$TERM> should contain 4 chars minimum!" && exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
backup() {
|
||||
if [[ ! -d "$FOLDER" ]]; then
|
||||
echo "error: Folder required!"
|
||||
file $FOLDER
|
||||
exit 2
|
||||
fi
|
||||
DATE=$(date '+%F')
|
||||
ARCHIVE="$FOLDER"/$DB_NAME-$DATE.postgres.gz
|
||||
|
||||
if [[ -f $ARCHIVE ]]; then
|
||||
VERSION_CONTROL=numbered mv -b $ARCHIVE "$FOLDER"/$DB_NAME-$DATE-daily.postgres.gz
|
||||
fi
|
||||
|
||||
echo "backup $DB_NAME $FOLDER"
|
||||
PGPASSWORD=$DB_NAME pg_dump -U $DB_NAME $DB_NAME -h ct1.lxd | gzip >"$ARCHIVE"
|
||||
echo "archive file created: $ARCHIVE"
|
||||
}
|
||||
|
||||
restore() {
|
||||
echo "restore $DB_NAME $FILE"
|
||||
if [[ ! -f "$FILE" ]]; then
|
||||
echo "error: Backup file (*.postgres.gz) required!"
|
||||
file $FILE
|
||||
exit 2
|
||||
fi
|
||||
PROCESSES=$(console "psql -c \"select pid as process_id, usename as username, datname as database_name, client_addr as client_address, application_name, backend_start, state, state_change from pg_stat_activity WHERE datname='$DB_NAME';\"")
|
||||
PROCESS_COUNT=$(echo "$PROCESSES" | wc -l)
|
||||
if [[ $PROCESS_COUNT -gt 3 ]]; then
|
||||
echo "FAILURE: There are some connections to database, please consider stopping bound services"
|
||||
echo
|
||||
printf "$PROCESSES\n"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ $YES == "true" || "yes" == $(confirm "RESTORATION will drop DATABASE, please acknowledge with care!!!") ]]; then
|
||||
FOLDER="$HOME/RECOVERY_POSTGRES"
|
||||
mkdir -p "$FOLDER"
|
||||
backup
|
||||
echo "backup successful, now drop and restore"
|
||||
lxc exec ct1 -- su - postgres -c "dropdb $DB_NAME && createdb -O $DB_NAME $DB_NAME"
|
||||
gunzip -c "$FILE" | grep -v "^CREATE DATABASE" | PGPASSWORD=$DB_NAME PGOPTIONS='--client-min-messages=warning' psql -X -q -1 -v ON_ERROR_STOP=1 --pset pager=off -U $DB_NAME -h ct1.lxd $DB_NAME 2>&1 >/dev/null
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
rename() {
|
||||
echo "rename <$DB_NAME> to <$DB_NEW_NAME>"
|
||||
mapfile -t LIST <<<"$(list)"
|
||||
found=false
|
||||
for db in "${LIST[@]}"; do
|
||||
[[ "$db" == "$DB_NEW_NAME" ]] && echoerr "destination database <$DB_NEW_NAME> already exists! Please provide another name." && exit 11
|
||||
[[ "$db" == "$DB_NAME" ]] && found=true
|
||||
done
|
||||
$found || (echoerr "source database <$DB_NAME> not found!" && exit 12)
|
||||
|
||||
console "psql -c \"ALTER DATABASE \\\"$DB_NAME\\\" RENAME TO \\\"$DB_NEW_NAME\\\" \""
|
||||
console "psql -c \"ALTER USER \\\"$DB_NAME\\\" RENAME TO \\\"$DB_NEW_NAME\\\" \""
|
||||
console "psql -c \"ALTER USER \\\"$DB_NEW_NAME\\\" PASSWORD '$DB_NEW_NAME' \""
|
||||
|
||||
}
|
||||
|
||||
# MAIN
|
||||
. "$MIAOU_BASEDIR/lib/init.sh"
|
||||
|
||||
[[ $# -lt 1 ]] && synopsis && exit 1
|
||||
ACTION=$1
|
||||
|
||||
case $ACTION in
|
||||
console)
|
||||
shift
|
||||
TAIL="$@"
|
||||
console "$TAIL"
|
||||
;;
|
||||
list)
|
||||
list
|
||||
;;
|
||||
connections)
|
||||
connections
|
||||
;;
|
||||
use)
|
||||
[[ $# -lt 2 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
shift 2
|
||||
TAIL="$@"
|
||||
use "$TAIL"
|
||||
;;
|
||||
create)
|
||||
[[ $# -lt 2 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
DB_PASSWORD=${3:-$DB_NAME}
|
||||
create
|
||||
;;
|
||||
lookup)
|
||||
[[ $# -lt 3 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
TERM=$3
|
||||
lookup
|
||||
;;
|
||||
backup)
|
||||
[[ $# -lt 2 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
FOLDER=${3:-.}
|
||||
backup
|
||||
;;
|
||||
restore)
|
||||
[[ $# -lt 3 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
FILE=$3
|
||||
YES=true
|
||||
restore
|
||||
;;
|
||||
rename)
|
||||
[[ $# -lt 3 ]] && synopsis && exit 1
|
||||
DB_NAME=$2
|
||||
DB_NEW_NAME=$3
|
||||
rename
|
||||
;;
|
||||
*)
|
||||
synopsis
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Executable
+180
@@ -0,0 +1,180 @@
|
||||
#!/bin/bash
|
||||
|
||||
function check_container_missing() {
|
||||
if container_exists "$CONTAINER"; then
|
||||
echoerr "$CONTAINER already created!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function usage() {
|
||||
echo 'USAGE with options:'
|
||||
echo -e "\t\tlxc-miaou-create <CONTAINER_NAME> -o sameuser[,nesting,ssh]"
|
||||
}
|
||||
|
||||
function check() {
|
||||
check_container_missing || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
function set_options() {
|
||||
declare -a options=("$@")
|
||||
length=${#options[@]}
|
||||
if [[ "$length" -ne 0 ]]; then
|
||||
if [[ "$length" -ne 2 ]]; then
|
||||
echoerr "unrecognized options: $@" && usage && exit 30
|
||||
else
|
||||
prefix="${options[0]}"
|
||||
option="${options[1]}"
|
||||
if [[ "$prefix" == '-o' ]]; then
|
||||
IFS=',' read -r -a options <<<"$option"
|
||||
for i in ${options[@]}; do
|
||||
case "$i" in
|
||||
sameuser) OPTION_SAMEUSER=true ;;
|
||||
nesting) OPTION_NESTING=true ;;
|
||||
ssh) OPTION_SSH=true ;;
|
||||
*) echoerr "unrecognized options: $@" && usage && exit 32 ;;
|
||||
esac
|
||||
done
|
||||
# echo "OPTION_SAMEUSER=$OPTION_SAMEUSER, OPTION_NESTING=$OPTION_NESTING, OPTION_SSH=$OPTION_SSH"
|
||||
else
|
||||
echoerr "unrecognized options prefix: $prefix" && usage && exit 31
|
||||
fi
|
||||
fi
|
||||
shift
|
||||
fi
|
||||
}
|
||||
|
||||
function create() {
|
||||
local PREFIX="miaou:create"
|
||||
|
||||
if [[ "$OPTION_SAMEUSER" == true ]]; then
|
||||
miaou_user=$(whoami)
|
||||
fi
|
||||
|
||||
echo -n "creating new container <$CONTAINER> based on image <$CONTAINER_RELEASE>... "
|
||||
bridge_gw=$(lxc network get lxdbr0 ipv4.address | cut -d'/' -f1)
|
||||
user_data="$(
|
||||
cat <<EOF
|
||||
#cloud-config
|
||||
timezone: 'Indian/Reunion'
|
||||
apt:
|
||||
preserve_sources_list: false
|
||||
conf: |
|
||||
Acquire::Retries "60";
|
||||
DPkg::Lock::Timeout "60";
|
||||
primary:
|
||||
- arches: [default]
|
||||
uri: http://debian.mithril.re/debian
|
||||
security:
|
||||
- arches: [default]
|
||||
uri: http://debian.mithril.re/debian-security
|
||||
sources_list: |
|
||||
# generated by miaou-cloud
|
||||
deb \$PRIMARY \$RELEASE main
|
||||
deb \$PRIMARY \$RELEASE-updates main
|
||||
deb \$SECURITY \$RELEASE-security main
|
||||
package_update: true
|
||||
package_upgrade: true
|
||||
package_reboot_if_required: true
|
||||
packages:
|
||||
- git
|
||||
- file
|
||||
- bc
|
||||
- bash-completion
|
||||
write_files:
|
||||
- path: /etc/sudoers.d/10-add_TOOLBOX_to_secure_path
|
||||
content: >
|
||||
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/TOOLBOX"
|
||||
runcmd:
|
||||
- [ systemctl, mask, systemd-hostnamed.service ]
|
||||
- [ systemctl, disable, e2scrub_reap.service ]
|
||||
- [ systemctl, disable, systemd-resolved.service, --now ]
|
||||
- [ systemctl, reset-failed ]
|
||||
- [ rm, /etc/resolv.conf]
|
||||
- [ rm, /etc/sudoers.d/90-cloud-init-users]
|
||||
- "echo nameserver $bridge_gw > /etc/resolv.conf"
|
||||
final_message: "Container from datasource \$datasource is finally up, after \$UPTIME seconds"
|
||||
EOF
|
||||
)"
|
||||
lxc init images:debian/$CONTAINER_RELEASE/cloud "$CONTAINER" --config user.user-data="$user_data" -q
|
||||
|
||||
# allow directory `SHARED` to be read-write mounted
|
||||
lxc config set "$CONTAINER" raw.idmap "both $(id -u) 0" -q
|
||||
mkdir -p "$HOME/LXD/SHARED/$CONTAINER"
|
||||
|
||||
lxc config device add "$CONTAINER" SHARED disk source="$HOME/LXD/SHARED/$CONTAINER" path=/mnt/SHARED -q
|
||||
lxc config device add "$CONTAINER" TOOLBOX disk source=/TOOLBOX path=/TOOLBOX -q
|
||||
lxc config device add "$CONTAINER" DEBIAN_BASH disk source=$(realpath /opt/debian-bash) path=/opt/debian-bash -q
|
||||
lxc config set "$CONTAINER" environment.PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/debian-bash/tools:/TOOLBOX -q
|
||||
|
||||
if [[ "$OPTION_NESTING" == true ]]; then
|
||||
lxc config set $CONTAINER security.nesting true -q
|
||||
lxc config device add "$CONTAINER" miaou disk source=/opt/miaou path=/opt/miaou -q
|
||||
fi
|
||||
|
||||
lxc start "$CONTAINER" -q
|
||||
|
||||
# initializing debian-bash
|
||||
lxc exec "$CONTAINER" -- /opt/debian-bash/init.sh
|
||||
|
||||
# default configuration files (btm,)
|
||||
lxc exec "$CONTAINER" -- mkdir -p /root/.config/bottom
|
||||
lxc file push "$MIAOU_BASEDIR/templates/bottom/bottom.toml" "$CONTAINER/root/.config/bottom/bottom.toml" -q
|
||||
|
||||
# purge cloud-init after success
|
||||
lxc exec "$CONTAINER" -- systemd-run -q -p After=cloud-final.service -p Type=oneshot --no-block bash -c '\
|
||||
cloud-init status --wait &&\
|
||||
cp /var/lib/cloud/data/status.json /root/cloud-status.json &&\
|
||||
systemctl stop cloud-{config,final,init-local,init}.service &&\
|
||||
systemctl disable cloud-{config,final,init-local,init}.service &&\
|
||||
systemctl stop cloud-config.target cloud-init.target &&\
|
||||
apt-get purge -y cloud-init &&\
|
||||
rm -rf /var/lib/cloud && \
|
||||
userdel -rf debian \
|
||||
'
|
||||
|
||||
if [[ "$OPTION_SAMEUSER" == true ]]; then
|
||||
if ! lxc exec "$CONTAINER" -- grep "$miaou_user" /etc/passwd; then
|
||||
lxc exec "$CONTAINER" -- useradd -ms /bin/bash -G sudo "$miaou_user"
|
||||
fi
|
||||
if ! lxc exec "$CONTAINER" -- passwd -S "$miaou_user" | cut -d ' ' -f2 | grep -q ^P; then
|
||||
shadow_passwd=$(load_yaml_from_expanded credential.shadow)
|
||||
shadow_remainder=$(lxc exec "$CONTAINER" -- bash -c "grep $miaou_user /etc/shadow | cut -d':' -f3-")
|
||||
lxc exec "$CONTAINER" -- /opt/debian-bash/tools/append_or_replace "^$miaou_user:.*:" "$miaou_user:$shadow_passwd:$shadow_remainder" /etc/shadow >/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$OPTION_SSH" == true ]]; then
|
||||
lxc exec "$CONTAINER" -- /opt/debian-bash/tools/idem_apt_install openssh-server
|
||||
fi
|
||||
|
||||
if [[ "$OPTION_SSH" == true && "$OPTION_SAMEUSER" == true ]]; then
|
||||
lxc-miaou-enable-ssh "$CONTAINER"
|
||||
fi
|
||||
|
||||
PREFIX="" echoinfo OK
|
||||
|
||||
echo "hint: \`lxc login $CONTAINER [--env user=<USER>]\`"
|
||||
[[ "$OPTION_SAMEUSER" == true ]] && echo "hint: \`lxc sameuser $CONTAINER\`"
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
## MAIN
|
||||
. "$MIAOU_BASEDIR/lib/init.sh"
|
||||
OPTION_SAMEUSER=false
|
||||
OPTION_NESTING=false
|
||||
OPTION_SSH=false
|
||||
PREFIX="miaou"
|
||||
|
||||
arg1_required "$@" || (usage && exit 1)
|
||||
readonly CONTAINER=$1
|
||||
readonly CONTAINER_RELEASE="bookworm"
|
||||
|
||||
shift
|
||||
set_options "$@"
|
||||
readonly FULL_OPTIONS="$@"
|
||||
|
||||
check
|
||||
create
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
|
||||
function check_container_exists() {
|
||||
if ! container_exists "$CONTAINER"; then
|
||||
echoerr "container <$CONTAINER> does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function check() {
|
||||
check_container_exists || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
function enable_ssh() {
|
||||
echo "lxc: enable ssh in container <$CONTAINER> for user <$SSH_USER>"
|
||||
|
||||
if ! container_running "$CONTAINER"; then
|
||||
echowarn "container <$CONTAINER> seems to be asleep, starting ..."
|
||||
lxc start "$CONTAINER"
|
||||
echowarn DONE
|
||||
fi
|
||||
|
||||
lxc exec "$CONTAINER" -- bash <<EOF
|
||||
set -Eeuo pipefail
|
||||
if ! id "$SSH_USER" &>/dev/null; then
|
||||
echo "adding new user <$SSH_USER>"
|
||||
useradd -ms /bin/bash -G sudo "$SSH_USER"
|
||||
else
|
||||
echo "bash: $SSH_USER exists already!"
|
||||
fi
|
||||
EOF
|
||||
|
||||
miaou_user=$(whoami)
|
||||
shadow_passwd=$(load_yaml_from_expanded credential.shadow)
|
||||
shadow_remainder=$(lxc exec "$CONTAINER" -- bash -c "grep $SSH_USER /etc/shadow | cut -d':' -f3-")
|
||||
lxc exec "$CONTAINER" -- /opt/debian-bash/tools/append_or_replace "^$SSH_USER:.*:" "$SSH_USER:$shadow_passwd:$shadow_remainder" /etc/shadow >/dev/null
|
||||
|
||||
lxc exec "$CONTAINER" -- /opt/debian-bash/tools/idem_apt_install openssh-server
|
||||
previous_users=($(
|
||||
lxc exec "$CONTAINER" -- bash <<EOF
|
||||
set -Eeuo pipefail
|
||||
if [[ -f /etc/ssh/sshd_config ]] && grep -q AllowUsers /etc/ssh/sshd_config ; then
|
||||
cat /etc/ssh/sshd_config | grep AllowUsers | cut -d' ' -f 2-
|
||||
fi
|
||||
EOF
|
||||
))
|
||||
|
||||
if containsElement previous_users "$SSH_USER"; then
|
||||
echo "sshd_config: AllowUsers $SSH_USER already done!"
|
||||
else
|
||||
echo "previous_users ${previous_users[*]}"
|
||||
previous_users+=("$SSH_USER")
|
||||
echo -n "building template for sshd_config..."
|
||||
USERS=${previous_users[*]} tera -e --env-key env -t "$MIAOU_BASEDIR/templates/dev-container-ssh/sshd_config.j2" -o "/tmp/sshd_config" "$MIAOU_CONFIGDIR/miaou.expanded.yaml" >/dev/null
|
||||
echo 'OK'
|
||||
echo -n "copying sshd_config over container <$CONTAINER> ... "
|
||||
lxc file push --uid 0 --gid 0 "/tmp/sshd_config" "$CONTAINER/etc/ssh/sshd_config" &>/dev/null
|
||||
echo 'OK'
|
||||
lxc exec "$CONTAINER" -- systemctl reload sshd.service
|
||||
fi
|
||||
|
||||
lxc exec "$CONTAINER" -- mkdir -p "/home/$SSH_USER/.ssh"
|
||||
lxc exec "$CONTAINER" -- chown "$SSH_USER:$SSH_USER" "/home/$SSH_USER/.ssh"
|
||||
lxc exec "$CONTAINER" -- chmod 760 "/home/$SSH_USER/.ssh"
|
||||
lxc file push --uid 0 --gid 0 "/home/$miaou_user/.ssh/id_rsa.pub" "$CONTAINER/home/$SSH_USER/.ssh/authorized_keys" &>/dev/null
|
||||
lxc exec "$CONTAINER" -- chown "$SSH_USER:$SSH_USER" "/home/$SSH_USER/.ssh/authorized_keys"
|
||||
lxc exec "$CONTAINER" -- chmod 600 "/home/$SSH_USER/.ssh/authorized_keys"
|
||||
|
||||
echo "create symbolic link for curl from TOOLBOX as required for Codium remote-ssh"
|
||||
lxc exec "$CONTAINER" -- ln -sf /TOOLBOX/curl /usr/bin/
|
||||
|
||||
echo "SUCCESS: container $CONTAINER listening on port 22"
|
||||
}
|
||||
|
||||
## MAIN
|
||||
. "$MIAOU_BASEDIR/lib/init.sh"
|
||||
|
||||
arg1_required "$@"
|
||||
readonly CONTAINER=$1
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
readonly SSH_USER=$(id -un)
|
||||
else
|
||||
readonly SSH_USER="$2"
|
||||
fi
|
||||
|
||||
check
|
||||
enable_ssh
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
lxc list -c nDm -f compact status=running | tail -n+2 | sort -k2 -h -r
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
lxc list -c nmD -f compact status=running | tail -n+2 | sort -k2 -h -r
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
function restart_dnsmasq() {
|
||||
echo -n "lxd: restart dnsmasq... "
|
||||
lxc network get lxdbr0 raw.dnsmasq >/tmp/dnsmaq.conf
|
||||
lxc network set lxdbr0 raw.dnsmasq - </tmp/dnsmaq.conf
|
||||
echo "OK"
|
||||
}
|
||||
|
||||
## MAIN
|
||||
. "$MIAOU_BASEDIR/lib/init.sh"
|
||||
restart_dnsmasq
|
||||
Executable
+479
@@ -0,0 +1,479 @@
|
||||
#!/bin/bash
|
||||
|
||||
usage() {
|
||||
PREFIX="miaou:usage" echo '<init>'
|
||||
exit 0
|
||||
}
|
||||
|
||||
yqm() {
|
||||
#read only
|
||||
yq "$1" "$EXPANDED_CONF"
|
||||
}
|
||||
|
||||
yqmi() {
|
||||
# for update
|
||||
yq "$1" "$EXPANDED_CONF" -i
|
||||
}
|
||||
|
||||
yqmt() {
|
||||
# tabular
|
||||
yq "$1" "$EXPANDED_CONF" -o t
|
||||
}
|
||||
|
||||
compute_fqdn_middlepart() {
|
||||
case "$1" in
|
||||
prod)
|
||||
local fqdn_middlepart="."
|
||||
;;
|
||||
beta)
|
||||
local fqdn_middlepart=".beta."
|
||||
;;
|
||||
dev)
|
||||
local fqdn_middlepart=".dev."
|
||||
;;
|
||||
*)
|
||||
echowarn "unknown target <${target}>, please fix with correct value from {prod, beta, dev} and try again..."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
builtin echo "$fqdn_middlepart"
|
||||
}
|
||||
|
||||
# archive_conf(FILE)
|
||||
# save patch in archived folder of current file
|
||||
function archive_conf() {
|
||||
PREFIX="miaou:conf:archive"
|
||||
|
||||
file="$1"
|
||||
filename=$(basename "$file")
|
||||
mkdir -p "$MIAOU_CONFIGDIR/archived/$filename"
|
||||
previous="$MIAOU_CONFIGDIR/archived/$filename/previous"
|
||||
|
||||
# shellcheck disable=SC2012
|
||||
latest_patch=$(ls -1tr "$MIAOU_CONFIGDIR/archived/$filename/" | tail -n1)
|
||||
|
||||
if [[ -z "$latest_patch" ]]; then
|
||||
echo -n "archiving first file <$file> ..."
|
||||
cp "$file" "$previous"
|
||||
PREFIX="" echoinfo OK
|
||||
elif [[ "$file" -nt "$latest_patch" ]]; then
|
||||
patchname="$MIAOU_CONFIGDIR/archived/$filename/$(date +%F_%T)"
|
||||
if ! diff "$previous" "$file" >"$patchname"; then
|
||||
echo -n "archiving patch <$patchname> ..."
|
||||
cp "$file" "$previous"
|
||||
PREFIX="" echoinfo OK
|
||||
else
|
||||
rm "$patchname"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function archive_allconf() {
|
||||
mkdir -p "$MIAOU_CONFIGDIR"
|
||||
archive_conf "$CONF"
|
||||
archive_conf "$DEFAULTS"
|
||||
}
|
||||
|
||||
function check_expand_conf() {
|
||||
PREFIX="miaou:conf:check"
|
||||
if ! "$FORCE" && [ -f "$EXPANDED_CONF" ] && [ "$EXPANDED_CONF" -nt "$CONF" ] && [ "$EXPANDED_CONF" -nt "$DEFAULTS" ]; then
|
||||
echo "already expanded!"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function expand_conf() {
|
||||
PREFIX="miaou:conf"
|
||||
|
||||
if [[ -f "$EXPANDED_CONF" ]]; then
|
||||
current_target=$(grep -Es "^target:" /etc/miaou/defaults.yaml | cut -d ' ' -f2)
|
||||
previous_target=$(grep -Es "^target:" "$EXPANDED_CONF" | cut -d ' ' -f2)
|
||||
[[ "$current_target" != "$previous_target" ]] && echoerr "TARGET <$previous_target> mismatched <$current_target>" && exit 101
|
||||
fi
|
||||
|
||||
# initialize expanded conf by merging default
|
||||
# shellcheck disable=SC2016
|
||||
yq eval-all '. as $item ireduce ({}; . * $item )' "$CONF" "$DEFAULTS" >"$EXPANDED_CONF"
|
||||
|
||||
# append unique container unless overridden
|
||||
mapfile -t services_app_only < <(yqmt '.services.[].[] | has("container") | select ( . == false) | [(parent|key)+" " +key]')
|
||||
|
||||
for i in "${services_app_only[@]}"; do
|
||||
read -r -a item <<<"$i"
|
||||
domain=${item[0]}
|
||||
subdomain=${item[1]}
|
||||
app=$(yqm ".services.\"$domain\".\"$subdomain\".app")
|
||||
container=$(get_container_for_domain_subdomain_app "$domain" "$subdomain" "$app")
|
||||
yqmi ".services.\"$domain\".\"$subdomain\".container=\"$container\""
|
||||
done
|
||||
|
||||
# append enabled=true unless overridden
|
||||
mapfile -t services_app_only < <(yqmt '.services.[].[] | has("enabled") | select ( . == false) | [(parent|key)+" " +key] | unique ')
|
||||
# echo "found <${#services_app_only[@]}> enabled services"
|
||||
for i in "${services_app_only[@]}"; do
|
||||
read -r -a item <<<"$i"
|
||||
domain=${item[0]}
|
||||
subdomain=${item[1]}
|
||||
yqmi ".services.\"$domain\".\"$subdomain\".enabled=true"
|
||||
done
|
||||
|
||||
# compute fqdn
|
||||
target=$(yqm '.target')
|
||||
fqdn_middlepart=$(compute_fqdn_middlepart "$target")
|
||||
|
||||
# write fqdn_middlepart
|
||||
yqmi ".expanded.fqdn_middlepart = \"$fqdn_middlepart\""
|
||||
|
||||
# add monitored.containers section
|
||||
yqmi '.expanded.monitored.containers = ([ .services[] | to_entries | .[] | .value | select (.enabled == true ) | .container ] | unique)'
|
||||
|
||||
# add monitored.hosts section
|
||||
yqmi '.expanded.monitored.hosts = [( .services[][] | select (.enabled == true ) | {"domain": ( parent | key ), "subdomain": key, "fqdn": key + (parent | parent | parent | .expanded.fqdn_middlepart) + ( parent | key ), "container":.container, "port":.port, "app":.app })]'
|
||||
|
||||
# add services section
|
||||
if [[ ${#services_app_only[@]} -gt 0 ]]; then
|
||||
yqmi '.expanded.services = [( .services[][] | select (.enabled == true ) | {"domain": ( parent | key ), "subdomain": key, "fqdn": key + (parent | parent | parent | .expanded.fqdn_middlepart) + ( parent | key ), "container":.container, "port":.port, "app":.app, "name": .name // ""})]'
|
||||
else
|
||||
yqmi '.expanded.services = []'
|
||||
fi
|
||||
|
||||
# add firewall section, bridge_subnet + mail_passthrough if any
|
||||
bridge_subnet=$(lxc network get lxdbr0 ipv4.address)
|
||||
yqmi ".firewall.bridge_subnet = \"$bridge_subnet\""
|
||||
|
||||
container_mail_passthrough=$(yqm ".firewall.container_mail_passthrough")
|
||||
}
|
||||
|
||||
function build_routes() {
|
||||
PREFIX="miaou:routes"
|
||||
|
||||
mapfile -t fqdns < <(yqm '.expanded.services[].fqdn')
|
||||
echo "found <${#fqdns[@]}> fqdn"
|
||||
raw_dnsmasq=''
|
||||
for i in "${fqdns[@]}"; do
|
||||
raw_dnsmasq+="address=/$i/$DMZ_IP\\n"
|
||||
# append domains to conf
|
||||
echo "re-routing any connection from <$i> to internal container <$DMZ_CONTAINER.lxd>"
|
||||
done
|
||||
|
||||
builtin echo -e "$raw_dnsmasq" | lxc network set $BRIDGE raw.dnsmasq -
|
||||
}
|
||||
|
||||
function build_dmz_reverseproxy() {
|
||||
PREFIX="miaou:build:dmz"
|
||||
echo -n "building configuration for nginx ... "
|
||||
mkdir -p "$MIAOU_CONFIGDIR/nginx"
|
||||
tera -t "$MIAOU_BASEDIR/templates/nginx/_default.j2" "$EXPANDED_CONF" -o "$MIAOU_CONFIGDIR/nginx/_default" &>/dev/null
|
||||
|
||||
tera -t "$MIAOU_BASEDIR/templates/nginx/hosts.j2" "$EXPANDED_CONF" -o "$MIAOU_CONFIGDIR/nginx/hosts" &>/dev/null
|
||||
PREFIX="" echo OK
|
||||
|
||||
echo -n "pushing configuration to <$DMZ_CONTAINER> ... "
|
||||
for f in "$MIAOU_CONFIGDIR"/nginx/*; do
|
||||
lxc file push --uid=0 --gid=0 "$f" "$DMZ_CONTAINER/etc/nginx/sites-available/" &>/dev/null
|
||||
done
|
||||
PREFIX="" echo OK
|
||||
|
||||
cat <<EOF | PREFIX="miaou:build:dmz" lxc_exec "$DMZ_CONTAINER"
|
||||
cd /etc/nginx/sites-enabled/
|
||||
for i in ../sites-available/*; do
|
||||
# echo dmz: enabling... \$i
|
||||
ln -sf \$i
|
||||
done
|
||||
nginx -tq
|
||||
systemctl restart nginx
|
||||
EOF
|
||||
echo "nginx reloaded successfully!"
|
||||
}
|
||||
|
||||
function monit_show() {
|
||||
PREFIX="monit:show"
|
||||
: $PREFIX
|
||||
|
||||
readarray -t hosts < <(yqmt '.expanded.monitored.hosts[] | [ .container, .port, .fqdn, .app ]')
|
||||
echo "================="
|
||||
echo "${#hosts[@]} available hosts"
|
||||
echo "================="
|
||||
|
||||
for host in "${hosts[@]}"; do
|
||||
read -r -a item <<<"$host"
|
||||
container=${item[0]}
|
||||
port=${item[1]}
|
||||
fqdn=${item[2]}
|
||||
app=${item[3]}
|
||||
|
||||
[[ -n ${PREFIX:-} ]] && printf "${DARK}%25.25s${NC} " "${PREFIX}"
|
||||
|
||||
if curl -m $MAX_WAIT -I -4so /dev/null "http://$container:$port"; then
|
||||
builtin echo -ne "${GREEN}✔${NC}"
|
||||
else
|
||||
builtin echo -ne "${RED}✘${NC}"
|
||||
fi
|
||||
printf "\t%10.10s\thttps://%-40s\thttp://%s\n" "$app" "$fqdn" "$container:$port"
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
function build_monit() {
|
||||
|
||||
# test whether monitored items actually run safely
|
||||
PREFIX="monit:build"
|
||||
echo -n "testing monitored hosts ..."
|
||||
readarray -t hosts < <(yqmt '.expanded.monitored.hosts[] | [ .container, .port, .fqdn ]')
|
||||
for host in "${hosts[@]}"; do
|
||||
read -r -a item <<<"$host"
|
||||
container=${item[0]}
|
||||
port=${item[1]}
|
||||
fqdn=${item[2]}
|
||||
|
||||
if ! (lxc exec "$container" -- ss -tln | grep -q "\(0.0.0.0\|*\):$port"); then
|
||||
echoerr
|
||||
echoerr "no HTTP server responds on <$container.lxd:$port>"
|
||||
echoerr "please review configuration <miaou.yaml> for fqdn: $fqdn"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! curl_check_unsecure "https://$fqdn"; then
|
||||
echoerr
|
||||
echoerr "DMZ does not seem to dispatch <https://$fqdn> please review DMZ Nginx proxy"
|
||||
exit 3
|
||||
elif [[ "$target" != 'dev' ]] && ! curl_check "https://$fqdn"; then
|
||||
PREFIX="" echo
|
||||
echowarn "T=$target missing valid certificate for fqdn <https://$fqdn> please review DMZ certbot"
|
||||
fi
|
||||
|
||||
done
|
||||
PREFIX="" echo OK
|
||||
|
||||
# templates for monit
|
||||
echo -n "copying templates for monit ..."
|
||||
mkdir -p "$MIAOU_CONFIGDIR/monit"
|
||||
tera -t "$MIAOU_BASEDIR/templates/monit/containers.j2" "$EXPANDED_CONF" -o "$MIAOU_CONFIGDIR/monit/containers" >/dev/null
|
||||
tera -t "$MIAOU_BASEDIR/templates/monit/hosts.j2" "$EXPANDED_CONF" -o "$MIAOU_CONFIGDIR/monit/hosts" >/dev/null
|
||||
PREFIX="" echo OK
|
||||
}
|
||||
|
||||
# count_service_for_container(container: string)
|
||||
# returns how many services run inside container according to expanded conf
|
||||
function count_service_for_container() {
|
||||
container_mail_passthrough="$1"
|
||||
count=$(yqm ".expanded.services.[] | select(.container == \"$container_mail_passthrough\") | .fqdn" | wc -l)
|
||||
builtin echo "$count"
|
||||
}
|
||||
|
||||
function build_nftables() {
|
||||
PREFIX="miaou:nftables:build"
|
||||
mkdir -p "$MIAOU_CONFIGDIR/nftables.rules.d"
|
||||
|
||||
container_mail_passthrough=$(yqm '.firewall.container_mail_passthrough')
|
||||
if [[ "$container_mail_passthrough" != null ]]; then
|
||||
ip_mail_passthrough=$(lxc list "$container_mail_passthrough" -c4 -f csv | grep eth0 | cut -d ' ' -f1)
|
||||
[[ -z "$ip_mail_passthrough" ]] && echoerr "container <$container_mail_passthrough> passthrough unknown ip!" && exit 55
|
||||
echo "passthrough=$container_mail_passthrough/$ip_mail_passthrough"
|
||||
|
||||
count=$(count_service_for_container "$container_mail_passthrough")
|
||||
[[ $count == 0 ]] && echowarn "no service detected => no passthrough, no change!"
|
||||
[[ $count -gt 1 ]] && echoerr "count <$count> services detected on container <$container_mail_passthrough>, please disable some and leave only one service for safety!!!" && exit 56
|
||||
|
||||
ip_mail_passthrough=$ip_mail_passthrough tera -e --env-key env -t "$MIAOU_BASEDIR/templates/nftables/lxd.table.j2" "$EXPANDED_CONF" -o "$MIAOU_CONFIGDIR/nftables.rules.d/lxd.table" &>/dev/null
|
||||
else
|
||||
echo "no container passthrough"
|
||||
tera -t "$MIAOU_BASEDIR/templates/nftables/lxd.table.j2" "$EXPANDED_CONF" -o "$MIAOU_CONFIGDIR/nftables.rules.d/lxd.table" &>/dev/null
|
||||
fi
|
||||
|
||||
if ! diff -q "$MIAOU_CONFIGDIR/nftables.rules.d/lxd.table" /etc/nftables.rules.d/lxd.table; then
|
||||
sudo_required "reloading nftables"
|
||||
echo -n "reloading nftables..."
|
||||
sudo cp "$MIAOU_CONFIGDIR/nftables.rules.d/lxd.table" /etc/nftables.rules.d/lxd.table
|
||||
sudo systemctl reload nftables
|
||||
PREFIX="" echo OK
|
||||
fi
|
||||
}
|
||||
|
||||
# check whether http server responds 200 OK, required <url>, ie: http://example.com:8001, https://example.com
|
||||
function curl_check() {
|
||||
arg1_required "$@"
|
||||
# echo "curl $1"
|
||||
curl -m $MAX_WAIT -sLI4 "$1" | grep -q "^HTTP.* 200"
|
||||
}
|
||||
|
||||
# check whether https server responds 200 OK, even unsecured certificate (auto-signed in mode DEV)
|
||||
function curl_check_unsecure() {
|
||||
arg1_required "$@"
|
||||
curl -m $MAX_WAIT -skLI4 "$1" | grep -q "^HTTP.* 200"
|
||||
}
|
||||
|
||||
function get_dmz_ip() {
|
||||
if ! container_running "$DMZ_CONTAINER"; then
|
||||
echowarn "Container running dmz <$DMZ_CONTAINER> seems down"
|
||||
echoerr "please \`lxc start $DMZ_CONTAINER\` or initialize first!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dmz_ip=$(host "$DMZ_CONTAINER.lxd" | cut -d ' ' -f4)
|
||||
if ! valid_ipv4 "$dmz_ip"; then
|
||||
echowarn "dmz seems up but no valid ip <$dmz_ip> found!"
|
||||
echoerr "please fix this networking issue, then retry..."
|
||||
exit 1
|
||||
else
|
||||
builtin echo "$dmz_ip"
|
||||
fi
|
||||
}
|
||||
|
||||
function fetch_container_of_type() {
|
||||
local type="$1"
|
||||
readarray -t dmzs < <(yqm ".containers.[].[] | select(.==\"$type\") | parent | key")
|
||||
case ${#dmzs[@]} in
|
||||
0) : ;;
|
||||
1) builtin echo "${dmzs[0]}" ;;
|
||||
*) for d in "${dmzs[@]}"; do
|
||||
builtin echo "$d"
|
||||
done ;;
|
||||
esac
|
||||
}
|
||||
|
||||
function get_container_for_domain_subdomain_app() {
|
||||
local domain="$1"
|
||||
local subdomain="$2"
|
||||
local app="$3"
|
||||
readarray -t containers < <(fetch_container_of_type "$app")
|
||||
case ${#containers[@]} in
|
||||
0) echoerr "no container of type <$app> found amongst containers for $subdomain.$domain\nHINT : Please, either :\n1. define at least one container for recipe <$app>\n2. remove all services related to recipe <$app>" && exit 1 ;;
|
||||
1) builtin echo "${containers[0]}" ;;
|
||||
*)
|
||||
for d in "${containers[@]}"; do
|
||||
echowarn "container of type $app found in <$d>"
|
||||
done
|
||||
echoerr "multiple containers (${#containers[@]}) provided same app <$app>, therefore container is mandatory alongside $subdomain.$domain" && exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
function get_unique_container_dmz() {
|
||||
readarray -t containers < <(fetch_container_of_type "dmz")
|
||||
case ${#containers[@]} in
|
||||
0) echoerr "no container of type <dmz> found amongst containers" && exit 1 ;;
|
||||
1) builtin echo "${containers[0]}" ;;
|
||||
|
||||
*)
|
||||
for d in "${containers[@]}"; do
|
||||
echowarn "container of type dmz found in <$d>"
|
||||
done
|
||||
echoerr "multiple dmz (${#containers[@]}) are not allowed, please select only one " && exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
function prepare_dmz_container() {
|
||||
"$MIAOU_BASEDIR"/recipes/dmz/install.sh "$DMZ_CONTAINER"
|
||||
}
|
||||
|
||||
function check_resolv_conf() {
|
||||
local bridge_gw resolver
|
||||
bridge_gw=$(lxc network get lxdbr0 ipv4.address | cut -d'/' -f1)
|
||||
resolver=$(grep nameserver /etc/resolv.conf | head -n1 | cut -d ' ' -f2)
|
||||
|
||||
PREFIX="resolver:check" echo "container resolver is <$resolver>"
|
||||
PREFIX="resolver:check" echo "container bridge is <$bridge_gw>"
|
||||
[[ "$bridge_gw" != "$resolver" ]] && return 21
|
||||
return 0
|
||||
}
|
||||
|
||||
function prepare_containers() {
|
||||
PREFIX="miaou:prepare"
|
||||
readarray -t containers < <(yqmt ".containers.[] | [ key, .[] ] ")
|
||||
for i in "${containers[@]}"; do
|
||||
read -r -a item <<<"$i"
|
||||
container=${item[0]}
|
||||
for ((j = 1; j < ${#item[@]}; j++)); do
|
||||
service="${item[$j]}"
|
||||
recipe_install="$MIAOU_BASEDIR/recipes/$service/install.sh"
|
||||
if [[ -f "$recipe_install" ]]; then
|
||||
echo "install [$service] onto container <$container>"
|
||||
"$recipe_install" "$container"
|
||||
else
|
||||
echoerr "FAILURE, for container <$container>, install recipe [$service] not found!"
|
||||
echoerr "please review configuration, mismatch recipe name maybe?"
|
||||
exit 50
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
function build_services() {
|
||||
PREFIX="miaou:build:services"
|
||||
echo "building services..."
|
||||
readarray -t services < <(yqmt '.expanded.services[] | [ .[] ]')
|
||||
for i in "${services[@]}"; do
|
||||
|
||||
read -r -a item <<<"$i"
|
||||
fqdn=${item[2]}
|
||||
container=${item[3]}
|
||||
port=${item[4]}
|
||||
app=${item[5]}
|
||||
name=${item[6]:-}
|
||||
|
||||
recipe="$MIAOU_BASEDIR/recipes/$app/crud.sh"
|
||||
if [[ -f "$recipe" ]]; then
|
||||
echo "read [$app:$name] onto container <$container>"
|
||||
if ! "$recipe" -r --port "$port" --container "$container" --name "$name" --fqdn "$fqdn"; then
|
||||
echoinfo "CREATE RECIPE"
|
||||
"$recipe" -c --port "$port" --container "$container" --name "$name" --fqdn "$fqdn"
|
||||
echoinfo "CREATE RECIPE: OK"
|
||||
fi
|
||||
else
|
||||
echowarn "for container <$container>, crud recipe [$app] not found!"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
### MAIN
|
||||
|
||||
. "$MIAOU_BASEDIR/lib/init.sh"
|
||||
|
||||
readonly CONF="/etc/miaou/miaou.yaml"
|
||||
readonly DEFAULTS="/etc/miaou/defaults.yaml"
|
||||
readonly EXPANDED_CONF="$MIAOU_CONFIGDIR/miaou.expanded.yaml"
|
||||
readonly BRIDGE="lxdbr0"
|
||||
readonly MAX_WAIT=3 # timeout in seconds
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
declare -a options=("$@")
|
||||
|
||||
FORCE=false
|
||||
if containsElement options "-f" || containsElement options "--force"; then
|
||||
FORCE=true
|
||||
fi
|
||||
|
||||
if containsElement options "history"; then
|
||||
echo "TODO: HISTORY"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if containsElement options "config"; then
|
||||
editor /etc/miaou/miaou.yaml
|
||||
if diff -q /etc/miaou/miaou.yaml $HOME/.config/miaou/archived/miaou.yaml/previous; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if check_expand_conf; then
|
||||
archive_allconf
|
||||
expand_conf
|
||||
check_resolv_conf
|
||||
build_nftables
|
||||
prepare_containers
|
||||
|
||||
DMZ_CONTAINER=$(get_unique_container_dmz)
|
||||
readonly DMZ_CONTAINER
|
||||
build_services
|
||||
|
||||
DMZ_IP=$(get_dmz_ip)
|
||||
readonly DMZ_IP
|
||||
build_dmz_reverseproxy
|
||||
|
||||
build_routes
|
||||
build_monit
|
||||
|
||||
fi
|
||||
monit_show
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
readonly DOMAIN=$1
|
||||
readonly PROTOCOL=${2:-https}
|
||||
readonly TIMEOUT=10 # max seconds to wait
|
||||
|
||||
result=0
|
||||
|
||||
function usage {
|
||||
echo 'usage: <DOMAIN> [ https | 443 | smtps | 587 | pop3 | 993 | imap | 995 | ALL ]'
|
||||
exit -1
|
||||
}
|
||||
|
||||
function check_ssl {
|
||||
local protocol=$1
|
||||
case $protocol in
|
||||
SMTPS )
|
||||
local extra="-starttls smtp -showcerts"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo -n "$protocol "
|
||||
|
||||
|
||||
certificate_info=$(echo | timeout $TIMEOUT openssl s_client $extra -connect $DOMAIN:$2 2>/dev/null)
|
||||
|
||||
issuer=$(echo "$certificate_info" | openssl x509 -noout -text 2>/dev/null | grep Issuer: | cut -d: -f2)
|
||||
date=$( echo "$certificate_info" | openssl x509 -noout -enddate 2>/dev/null | cut -d'=' -f2)
|
||||
date_s=$(date -d "${date}" +%s)
|
||||
now_s=$(date -d now +%s)
|
||||
date_diff=$(( (date_s - now_s) / 86400 ))
|
||||
|
||||
if [[ -z $date ]]; then
|
||||
echo -n "does not respond "
|
||||
echo -ne "\033[31;1m"
|
||||
echo FAILURE
|
||||
(( result += 1 ))
|
||||
elif [[ $date_diff -gt 20 ]]; then
|
||||
echo -n "issuer:$issuer "
|
||||
echo -n "will expire in $date_diff days "
|
||||
echo -ne "\033[32;1m"
|
||||
echo ok
|
||||
elif [[ $date_diff -gt 0 ]];then
|
||||
echo -n "issuer:$issuer "
|
||||
echo -n "will expire in $date_diff days "
|
||||
echo -ne "\033[31;1m"
|
||||
echo WARNING
|
||||
(( result += 1 ))
|
||||
else
|
||||
echo -n "issuer:$issuer "
|
||||
echo -n "has already expired $date_diff ago "
|
||||
echo -ne "\033[31;1m"
|
||||
echo FAILURE
|
||||
(( result += 1 ))
|
||||
fi
|
||||
echo -ne "\033[0m"
|
||||
}
|
||||
|
||||
#MAIN
|
||||
[[ -z "$DOMAIN" ]] && usage
|
||||
case $PROTOCOL in
|
||||
https | 443 )
|
||||
check_ssl HTTPS 443;;
|
||||
smtps | 587 )
|
||||
check_ssl SMTPS 587;;
|
||||
pop3 | 995 )
|
||||
check_ssl POP3 995;;
|
||||
imap | 993 )
|
||||
check_ssl IMAP 993;;
|
||||
all | ALL )
|
||||
check_ssl HTTPS 443
|
||||
check_ssl SMTPS 587
|
||||
check_ssl POP3 995
|
||||
check_ssl IMAP 993
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
exit "$result"
|
||||
Reference in New Issue
Block a user