mirror of
https://github.com/m8tin/cis.git
synced 2025-12-06 07:48:26 +01:00
Compare commits
62 Commits
master
...
4abe3ba061
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4abe3ba061 | ||
|
|
864e31101d | ||
|
|
0f32d1bdb6 | ||
|
|
d56650d31d | ||
|
|
269a1428cd | ||
|
|
583d7e5629 | ||
|
|
25e4a4cc38 | ||
|
|
c3724afcf6 | ||
|
|
49fde6b5f3 | ||
|
|
89b0d2b1ef | ||
|
|
581314c0d9 | ||
|
|
ac7f693ec9 | ||
|
|
5901afdaae | ||
|
|
d40f52b6d1 | ||
|
|
446ecb3744 | ||
|
|
d0eb35441f | ||
|
|
ca4914c63c | ||
|
|
e3f3be3725 | ||
|
|
edf1992015 | ||
|
|
6a3707a00a | ||
|
|
28f1cafc55 | ||
|
|
2ac52819ae | ||
|
|
6db70ed434 | ||
|
|
a58d9d6f66 | ||
|
|
9230cc1b73 | ||
|
|
03efc9a187 | ||
|
|
5cc79adc0b | ||
|
|
c15723b499 | ||
|
|
8f3c21e486 | ||
|
|
ae6093a3cc | ||
|
|
59e7f9abdd | ||
|
|
9597e32b62 | ||
|
|
f92251e409 | ||
|
|
875e617f17 | ||
|
|
997e0d6a94 | ||
|
|
b0c49c3779 | ||
|
|
106720ebd7 | ||
|
|
03e3b0e026 | ||
|
|
25d3637020 | ||
|
|
46693b5c41 | ||
|
|
a3f1cfd590 | ||
|
|
e164eee884 | ||
|
|
64d6a350c0 | ||
|
|
3d1ecf6fa8 | ||
|
|
4943625351 | ||
|
|
f1df97c5b7 | ||
|
|
3004f630fc | ||
|
|
4ca994c698 | ||
|
|
bf19c05148 | ||
|
|
e782848efd | ||
|
|
91816bedf8 | ||
|
|
f845d44eb6 | ||
|
|
1c337b8dc1 | ||
|
|
3ce89e3fc0 | ||
|
|
c389261301 | ||
|
|
03017579c3 | ||
|
|
ae48c7b4e0 | ||
|
|
c71ce67a5a | ||
|
|
5cdc7524e2 | ||
|
|
da4aedb0b6 | ||
|
|
4f31db9237 | ||
|
|
a0a9501c31 |
30
manual/README.md
Normal file
30
manual/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
Manual
|
||||
======
|
||||
|
||||
Each script `${FULL_SCRIPTNAME}.sh` has it's corresponding manual here:
|
||||
- `manuals/${FULL_SCRIPTNAME}.sh.md`
|
||||
|
||||
According to the rule above, this directory `manuals/core/*`
|
||||
has the same structure as `../core/*` and contains all manuals explaining the core functionality.
|
||||
|
||||
And this directory `manuals/script/*`
|
||||
has the same structure as `../script/*` and contains all manuals explaining the purpose of each script.
|
||||
|
||||
|
||||
|
||||
Core functionality
|
||||
------------------
|
||||
|
||||
This set of scripts is the absolute minimum to provide the core functionality:
|
||||
- `setupCoreOntoThisHost.sh` bootstraps an empty new host, after the scripts repository was cloned
|
||||
[Read this for further details](./setupCoreOntoThisHost.sh.md)
|
||||
- `setupCoreOntoThisHost.sh` needs [`../core/addAndCheckGitRepository.sh`](./core/addAndCheckGitRepository.sh.md)
|
||||
- `setupCoreOntoThisHost.sh` needs [`../core/addNormalUser.sh`](./core/addNormalUser.sh.md)
|
||||
- `setupCoreOntoThisHost.sh` needs [`../core/addToCrontabEveryHour.sh`](./core/addToCrontabEveryHour.sh.md)
|
||||
- `setupCoreOntoThisHost.sh` needs [`../core/defineAuthorizedKeysOfUser.sh`](./core/defineAuthorizedKeysOfUser.sh.md)
|
||||
- `setupCoreOntoThisHost.sh` needs [`../core/ensureUsageOfDefinitions.sh`](./core/ensureUsageOfDefinitions.sh.md)
|
||||
|
||||
|
||||
|
||||
The scripts
|
||||
-----------
|
||||
8
manual/core/addNormalUser.sh.md
Normal file
8
manual/core/addNormalUser.sh.md
Normal file
@@ -0,0 +1,8 @@
|
||||
[core/addNormalUser.sh](..//core/addNormalUser.sh)
|
||||
==================================================
|
||||
|
||||
This script adds a normal user to the host on which it was called.
|
||||
|
||||
__Parameters:__
|
||||
1. USER (mandantory)
|
||||
Name of the user who will be added to the host
|
||||
8
manual/core/defineAuthorizedKeysOfUser.sh.md
Normal file
8
manual/core/defineAuthorizedKeysOfUser.sh.md
Normal file
@@ -0,0 +1,8 @@
|
||||
[core/defineAuthorizedKeysOfUser.sh](../core/defineAuthorizedKeysOfUser.sh)
|
||||
===========================================================================
|
||||
|
||||
This script defines the `authorized_keys` file of the given user.
|
||||
It will create a link pointing to the file in definitions to distribute the same settings across all hosts.
|
||||
|
||||
__Parameters:__
|
||||
1. USER (mandantory) Name of the user who will receive the ssh settings.
|
||||
9
manual/script/host/README.md
Normal file
9
manual/script/host/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
script/host
|
||||
===========
|
||||
|
||||
This folder contains scripts managing the host.
|
||||
For example:
|
||||
- certificates
|
||||
- git
|
||||
- ssh
|
||||
- users
|
||||
4
manual/script/host/user/README.md
Normal file
4
manual/script/host/user/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
script/host/user
|
||||
================
|
||||
|
||||
This folder contains scripts managing users of a host.
|
||||
375
script/host/nginx/update_nginx_conf.sh
Normal file
375
script/host/nginx/update_nginx_conf.sh
Normal file
@@ -0,0 +1,375 @@
|
||||
#!/bin/bash
|
||||
NGINX_DIR="/etc/nginx"
|
||||
HOSTNAME=$(hostname)
|
||||
|
||||
if [ ! -d "$NGINX_DIR" ]; then
|
||||
exit
|
||||
fi
|
||||
rm $NGINX_DIR/site-*/*
|
||||
TEMP_HOST_FILE=`mktemp`
|
||||
cp /etc/hosts $TEMP_HOST_FILE
|
||||
INDENT=4
|
||||
|
||||
function appendProxyServerBlock() {
|
||||
INDENT=$((INDENT+4))
|
||||
local DOMAIN
|
||||
local PORT
|
||||
local INCLUDE_DOMAIN
|
||||
local SSL
|
||||
local FILE
|
||||
DOMAIN=$1
|
||||
PORT=$2
|
||||
INCLUDE_DOMAIN=$3
|
||||
FILE=$4
|
||||
if [[ "$5" == "ssl" ]]; then
|
||||
SSL=" ssl"
|
||||
fi
|
||||
|
||||
if [[ -z "$DOMAIN" || -z "$INCLUDE_DOMAIN" ]]; then
|
||||
return
|
||||
fi
|
||||
echo "$(echo "" | pr -to $INDENT)append proxy server block: '${DOMAIN}'${SSL}"
|
||||
cat >> "$FILE" << EOF
|
||||
server {
|
||||
listen ${PORT}${SSL};
|
||||
|
||||
server_name ${DOMAIN};
|
||||
EOF
|
||||
|
||||
if [[ ! -z "$SSL" ]]; then
|
||||
cat >> "$FILE" << EOF
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/${DOMAIN}/server.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/${DOMAIN}/server.key;
|
||||
|
||||
add_header Strict-Transport-Security max-age=15552000;
|
||||
EOF
|
||||
else
|
||||
cat >> "$FILE" << EOF
|
||||
server_name www.${DOMAIN};
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat >> "$FILE" << EOF
|
||||
root /var/www;
|
||||
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host \$http_host;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade \$http_upgrade;
|
||||
proxy_set_header Connection \$connection_upgrade;
|
||||
|
||||
proxy_redirect off;
|
||||
location /.well-known/acme-challenge {
|
||||
root /tmp/acme;
|
||||
}
|
||||
|
||||
include /etc/nginx/site-${INCLUDE_DOMAIN}/*;
|
||||
}
|
||||
EOF
|
||||
INDENT=$((INDENT-4))
|
||||
}
|
||||
|
||||
function appendProxy() {
|
||||
INDENT=$((INDENT+4))
|
||||
local DOMAIN
|
||||
local INCLUDE_DOMAIN
|
||||
local FILE
|
||||
DOMAIN=$1
|
||||
INCLUDE_DOMAIN=$2
|
||||
FILE="$NGINX_DIR/sites-enabled/${DOMAIN}"
|
||||
|
||||
|
||||
if [[ -z "$TARGET_PUBLIC_PORT" ]]; then
|
||||
TARGET_PUBLIC_PORT=80
|
||||
fi
|
||||
|
||||
if [[ -z "$TARGET_PUBLIC_PORT_SSL" ]]; then
|
||||
TARGET_PUBLIC_PORT_SSL=443
|
||||
fi
|
||||
|
||||
echo "$(echo "" | pr -to $INDENT)creating proxy for domain ${DOMAIN}"
|
||||
#VHost-Datei leeren
|
||||
printf "" > "$FILE"
|
||||
|
||||
#Proxy mit ssl (redirect HTTP -> HTTPS)
|
||||
if [ -f "$NGINX_DIR/ssl/${DOMAIN}/server.crt" ]; then
|
||||
|
||||
if [ -f "$NGINX_DIR/ssl/${DOMAIN}/allow_http" ]; then
|
||||
appendProxyServerBlock "${DOMAIN}" "${TARGET_PUBLIC_PORT}" "${INCLUDE_DOMAIN}" "$FILE"
|
||||
else
|
||||
cat >> "${FILE}" << EOF
|
||||
server {
|
||||
listen ${TARGET_PUBLIC_PORT};
|
||||
server_name ${DOMAIN};
|
||||
server_name www.${DOMAIN};
|
||||
location /.well-known/acme-challenge {
|
||||
root /tmp/acme;
|
||||
}
|
||||
root /var/www;
|
||||
${CUSTOM_NGINX_HTTP_CONFIG}
|
||||
|
||||
add_header Strict-Transport-Security max-age=15552000;
|
||||
location / {
|
||||
return 301 https://${DOMAIN}:${TARGET_PUBLIC_PORT_SSL}\$request_uri;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
appendProxyServerBlock "${DOMAIN}" "${TARGET_PUBLIC_PORT_SSL}" "${INCLUDE_DOMAIN}" "$FILE" "ssl"
|
||||
|
||||
# Proxy ohne ssl
|
||||
else
|
||||
appendProxyServerBlock "${DOMAIN}" "${TARGET_PUBLIC_PORT}" "${INCLUDE_DOMAIN}" "$FILE"
|
||||
fi
|
||||
INDENT=$((INDENT-4))
|
||||
}
|
||||
|
||||
function configureProxyForTargetDomain() {
|
||||
INDENT=$((INDENT+4))
|
||||
local DOMAIN=$1
|
||||
|
||||
echo "$(echo "" | pr -to $INDENT)configure proxy for domain $DOMAIN"
|
||||
appendProxy "${DOMAIN}" "${DOMAIN}"
|
||||
|
||||
# Proxy für Domain mit www Präfix
|
||||
if [ -f "$NGINX_DIR/ssl/www.${DOMAIN}/server.crt" ]; then
|
||||
FILE="$NGINX_DIR/sites-enabled/www.${DOMAIN}"
|
||||
printf "" > "$FILE"
|
||||
appendProxyServerBlock "www.${DOMAIN}" "${TARGET_PUBLIC_PORT_SSL}" "${DOMAIN}" "$FILE" "ssl"
|
||||
fi
|
||||
|
||||
mkdir -p "$NGINX_DIR/site-${DOMAIN}"
|
||||
if [[ -z "$PROXY_CONTEXTS" ]]; then
|
||||
if [[ -z "$CONTAINER_HTTPS_PORT" ]]; then
|
||||
cat >> $NGINX_DIR/site-${DOMAIN}/${TARGET_CONTAINER} << EOF
|
||||
location / {
|
||||
proxy_pass http://${CONTAINER_IP}:${CONTAINER_HTTP_PORT};
|
||||
}
|
||||
EOF
|
||||
else
|
||||
cat >> $NGINX_DIR/site-${DOMAIN}/${TARGET_CONTAINER} << EOF
|
||||
location / {
|
||||
proxy_pass https://${CONTAINER_IP}:${CONTAINER_HTTPS_PORT};
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
else
|
||||
if [[ ! -z "$ROOT_REDIRECT" ]]; then
|
||||
cat >> $NGINX_DIR/site-${DOMAIN}/${TARGET_CONTAINER} << EOF
|
||||
location = / {
|
||||
return 302 \$scheme://${DOMAIN}/${ROOT_REDIRECT};
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
for PROXY_CONTEXT in $PROXY_CONTEXTS; do
|
||||
if [[ -z "$CONTAINER_HTTPS_PORT" ]]; then
|
||||
cat >> $NGINX_DIR/site-${DOMAIN}/${TARGET_CONTAINER} << EOF
|
||||
location /${PROXY_CONTEXT} {
|
||||
proxy_pass http://${CONTAINER_IP}:${CONTAINER_HTTP_PORT}/${PROXY_CONTEXT};
|
||||
}
|
||||
EOF
|
||||
else
|
||||
cat >> $NGINX_DIR/site-${DOMAIN}/${TARGET_CONTAINER} << EOF
|
||||
location /${PROXY_CONTEXT} {
|
||||
proxy_pass https://${CONTAINER_IP}:${CONTAINER_HTTPS_PORT}/${PROXY_CONTEXT};
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
done;
|
||||
fi
|
||||
|
||||
|
||||
CONTAINER_CONFIG_DIR="/invra/state/$(cat /invra/hostowner)/containers"
|
||||
for FWD in $PROXY_FORWARDS; do
|
||||
SOURCE_PATH="`echo $FWD | cut -d: -f1`"
|
||||
TARGET_URL="`echo $FWD | cut -d: -s -f2-`"
|
||||
CONTINUE=0
|
||||
|
||||
# Prüfen ob Proxy bereits durch neues Schema im invra/state angelegt wurde
|
||||
while read PROXY_FILE; do
|
||||
CUR_HOST_FILE="$(dirname "$(dirname "$PROXY_FILE")")/current-host"
|
||||
CUR_HOST=$(cat $CUR_HOST_FILE)
|
||||
if [[ -f "$CUR_HOST_FILE" && ( "$CUR_HOST" != "$HOSTNAME" || "$DOMAIN" != "$HOSTNAME" ) ]]; then
|
||||
CONTINUE=1
|
||||
break
|
||||
fi
|
||||
#Process Substitution nutzen, damit CONTINUE-Variable die Schleife überlebt
|
||||
done < <(grep -lER "^/?${SOURCE_PATH}/?$" ${CONTAINER_CONFIG_DIR}/*/httpproxy/${DOMAIN} 2> /dev/null)
|
||||
|
||||
if [ $CONTINUE -eq 1 ]; then
|
||||
continue
|
||||
fi
|
||||
echo "$(echo "" | pr -to $INDENT)create proxy for context-path '${SOURCE_PATH}' to URL '${TARGET_URL}'"
|
||||
cat >> $NGINX_DIR/site-${DOMAIN}/${TARGET_CONTAINER} << EOF
|
||||
location /${SOURCE_PATH} {
|
||||
proxy_pass ${TARGET_URL};
|
||||
}
|
||||
EOF
|
||||
|
||||
done
|
||||
INDENT=$((INDENT-4))
|
||||
}
|
||||
|
||||
function getVar() {
|
||||
local _VAR_NAME=$1
|
||||
local _CONF_FILE=$2
|
||||
local _RESULT
|
||||
_RESULT=$(grep -E "^${_VAR_NAME}=" "${_CONF_FILE}" | grep -oE "[^=]+$")
|
||||
_CLEAN_RESULT=$(echo "$_RESULT" | sed -E 's/[()"]//g')
|
||||
GET_VAR_RESULT=$_CLEAN_RESULT
|
||||
}
|
||||
|
||||
echo "creating proxy forwards..."
|
||||
TARGET_PUBLIC_PORT=""
|
||||
TARGET_PUBLIC_PORT_SSL=""
|
||||
for CONTAINER in /invra/state/$(cat /invra/hostowner)/containers/*; do
|
||||
CONTAINER_HOST="$(cat $CONTAINER/current-host)"
|
||||
TARGET_CONTAINER="$(basename "$CONTAINER")"
|
||||
|
||||
echo " creating forward proxies for container '${TARGET_CONTAINER}' on Host '${CONTAINER_HOST}'"
|
||||
for DOMAIN_FILE in $CONTAINER/httpproxy/*; do
|
||||
if [ ! -f "${DOMAIN_FILE}" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
TARGET_DOMAIN="$(basename "$DOMAIN_FILE")"
|
||||
|
||||
#Proxy für Domain, die direkt auf diesen Host verweisen, überspringen => werden für die gehosteten Containern später angelegt
|
||||
if [[ "$TARGET_DOMAIN" == "$HOSTNAME" && "$CONTAINER_HOST" == "$HOSTNAME" ]]; then
|
||||
echo " skipping '${TARGET_DOMAIN}'"
|
||||
continue
|
||||
fi
|
||||
|
||||
appendProxy "${TARGET_DOMAIN}" "${TARGET_DOMAIN}"
|
||||
|
||||
cat "$DOMAIN_FILE" | while read PROXY_CONTEXT; do
|
||||
|
||||
echo " with context path '${PROXY_CONTEXT}'"
|
||||
|
||||
mkdir -p "$NGINX_DIR/site-${TARGET_DOMAIN}"
|
||||
case $PROXY_CONTEXT in
|
||||
/) TARGET_LOCATION="" ;;
|
||||
/*) TARGET_LOCATION="${PROXY_CONTEXT}" ;;
|
||||
*) TARGET_LOCATION="/${PROXY_CONTEXT}"; PROXY_CONTEXT="${TARGET_LOCATION}" ;;
|
||||
esac
|
||||
|
||||
if [[ "$CONTAINER_HOST" == "$HOSTNAME" ]]; then
|
||||
# neuer docker client
|
||||
CONTAINER_IP=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}:{{end}}' $TARGET_CONTAINER | cut -d: -f1)
|
||||
if [ -z "$CONTAINER_IP" ]; then
|
||||
# alter docker client
|
||||
CONTAINER_IP=$(docker inspect -f '{{.NetworkSettings.IPAddress}}' $TARGET_CONTAINER)
|
||||
if [ -z "$CONTAINER_IP" ]; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
CONTAINER_CONFIG="/persistent/${TARGET_CONTAINER}/containersettings"
|
||||
getVar "CONTAINER_HTTP_PORT" "${CONTAINER_CONFIG}"
|
||||
CONTAINER_HTTP_PORT=${GET_VAR_RESULT:-8080}
|
||||
|
||||
cat >> "$NGINX_DIR/site-${TARGET_DOMAIN}/${TARGET_CONTAINER}" << EOF
|
||||
location ${PROXY_CONTEXT} {
|
||||
proxy_pass http://${CONTAINER_IP}:${CONTAINER_HTTP_PORT};
|
||||
}
|
||||
EOF
|
||||
CONTAINER_CONFIG=""
|
||||
CONTAINER_HTTP_PORT=""
|
||||
else
|
||||
cat >> "$NGINX_DIR/site-${TARGET_DOMAIN}/fw-${TARGET_CONTAINER}" << EOF
|
||||
location ${PROXY_CONTEXT} {
|
||||
proxy_pass https://${CONTAINER_HOST}${TARGET_LOCATION};
|
||||
proxy_set_header Host ${TARGET_DOMAIN};
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
done
|
||||
done
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "creating proxies for local container..."
|
||||
for CONTAINER_CONFIG in /persistent/*/containersettings; do
|
||||
|
||||
getVar "TARGET_CONTAINER" "${CONTAINER_CONFIG}"
|
||||
TARGET_CONTAINER=$GET_VAR_RESULT
|
||||
getVar "CONTAINER_HTTP_PORT" "${CONTAINER_CONFIG}"
|
||||
CONTAINER_HTTP_PORT=${GET_VAR_RESULT:-8080}
|
||||
getVar "CONTAINER_HTTPS_PORT" "${CONTAINER_CONFIG}"
|
||||
CONTAINER_HTTPS_PORT=$GET_VAR_RESULT
|
||||
getVar "TARGET_DOMAIN" "${CONTAINER_CONFIG}"
|
||||
TARGET_DOMAIN=$GET_VAR_RESULT
|
||||
getVar "TARGET_PUBLIC_PORT" "${CONTAINER_CONFIG}"
|
||||
TARGET_PUBLIC_PORT=$GET_VAR_RESULT
|
||||
getVar "TARGET_PUBLIC_PORT_SSL" "${CONTAINER_CONFIG}"
|
||||
TARGET_PUBLIC_PORT_SSL=$GET_VAR_RESULT
|
||||
getVar "ADDITIONAL_TARGET_DOMAIN" "${CONTAINER_CONFIG}"
|
||||
ADDITIONAL_TARGET_DOMAIN=$GET_VAR_RESULT
|
||||
getVar "PROXY_CONTEXTS" "${CONTAINER_CONFIG}"
|
||||
PROXY_CONTEXTS=$GET_VAR_RESULT
|
||||
getVar "PROXY_FORWARDS" "${CONTAINER_CONFIG}"
|
||||
PROXY_FORWARDS=$GET_VAR_RESULT
|
||||
getVar "CUSTOM_NGINX_HTTP_CONFIG" "${CONTAINER_CONFIG}"
|
||||
CUSTOM_NGINX_HTTP_CONFIG=""
|
||||
grep -E "CUSTOM_NGINX_HTTP_CONFIG" "${CONTAINER_CONFIG}" > /dev/null
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo "CUSTOM_NGINX_HTTP_CONFIG wird in containersettings nicht mehr unterstützt"
|
||||
fi
|
||||
getVar "ROOT_REDIRECT" "${CONTAINER_CONFIG}"
|
||||
ROOT_REDIRECT=$GET_VAR_RESULT
|
||||
|
||||
if [[ "${TARGET_CONTAINER}" == "" ]]; then
|
||||
echo "'${CONTAINER_CONFIG}' enthält keinen TARGET_CONTAINER"
|
||||
continue
|
||||
fi
|
||||
|
||||
CURRENT_HOST_FILE="/invra/state/$(cat /invra/hostowner)/containers/${TARGET_CONTAINER}/current-host"
|
||||
if [ -f "$CURRENT_HOST_FILE" ]; then
|
||||
CURRENT_HOST="$(cat "$CURRENT_HOST_FILE")"
|
||||
if [ ! -z "$CURRENT_HOST" ] && [[ "$CURRENT_HOST" != "$HOSTNAME" ]]; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
echo " configuring container '$TARGET_CONTAINER'"
|
||||
# neuer docker client
|
||||
CONTAINER_IP=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}:{{end}}' $TARGET_CONTAINER | cut -d: -f1)
|
||||
if [ -z "$CONTAINER_IP" ]; then
|
||||
# alter docker client
|
||||
CONTAINER_IP=$(docker inspect -f '{{.NetworkSettings.IPAddress}}' $TARGET_CONTAINER)
|
||||
if [ -z "$CONTAINER_IP" ]; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
echo " updating ip from container $TARGET_CONTAINER"
|
||||
sed -i "s/.*$TARGET_CONTAINER\.cont.*//" $TEMP_HOST_FILE
|
||||
sed -i '/^\s*$/d' "${TEMP_HOST_FILE}" #löscht alle Zeilen, die nur unsichtbare Zeichen enthalten
|
||||
sed -i "/# DOCKER-IPS/a\\$CONTAINER_IP $TARGET_CONTAINER\.cont" $TEMP_HOST_FILE
|
||||
|
||||
if [ -z "$TARGET_DOMAIN" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
for DOMAIN_I in ${TARGET_DOMAIN}; do
|
||||
OLDSETTINGSFILE="$NGINX_DIR/site-${DOMAIN_I}/${TARGET_CONTAINER}"
|
||||
if [ -f $OLDSETTINGSFILE ]; then
|
||||
rm $OLDSETTINGSFILE
|
||||
fi
|
||||
configureProxyForTargetDomain "${DOMAIN_I}"
|
||||
done
|
||||
|
||||
if [[ ! -z "${ADDITIONAL_TARGET_DOMAIN}" ]]; then
|
||||
for DOMAIN_I in ${ADDITIONAL_TARGET_DOMAIN}; do
|
||||
appendProxy "${DOMAIN_I}" "$(echo ${TARGET_DOMAIN} | awk '{print $1}')"
|
||||
done
|
||||
fi
|
||||
|
||||
done
|
||||
cat $TEMP_HOST_FILE > /etc/hosts
|
||||
rm $TEMP_HOST_FILE
|
||||
service nginx configtest
|
||||
service nginx reload
|
||||
|
||||
33
script/host/zfs/snapshot/snapshot-cleanup.sh
Normal file
33
script/host/zfs/snapshot/snapshot-cleanup.sh
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
MIN_MIN=$(date --date="- 5 minutes" -u "+%Y%m%d%H%M")
|
||||
HOUR_MIN=$(date --date="- 1 days" -u "+%Y%m%d%H")
|
||||
DAY_MIN=$(date --date="- 7 days" -u "+%Y%m%d")
|
||||
MONTH_MIN=$(date --date="- 3 years" -u "+%Y%m")
|
||||
|
||||
zfs list -Hr -o name -t snapshot -r "zpool1/persistent" | grep -E "^zpool1/persistent/[a-zA-Z0-9_-]+@(SNAPHOURLY|SNAPDAILY|SNAPMONTHLY|SNAPMINUTLY)_[0-9]{6,12}$" | while read SNAPSHOT; do
|
||||
SNAPSHOT_TIME=$(echo "$SNAPSHOT" | grep -oE "[0-9]+$")
|
||||
if [[ ${#SNAPSHOT_TIME} == 12 && "$SNAPSHOT_TIME" < "${MIN_MIN}" ]]; then
|
||||
zfs destroy "${SNAPSHOT}"
|
||||
fi
|
||||
if [[ ${#SNAPSHOT_TIME} == 10 && "$SNAPSHOT_TIME" < "${HOUR_MIN}" ]]; then
|
||||
zfs destroy "${SNAPSHOT}"
|
||||
fi
|
||||
if [[ ${#SNAPSHOT_TIME} == 8 && "${SNAPSHOT_TIME}" < "${DAY_MIN}" ]]; then
|
||||
zfs destroy "${SNAPSHOT}"
|
||||
fi
|
||||
if [[ ${#SNAPSHOT_TIME} == 6 && "${SNAPSHOT_TIME}" < "${MONTH_MIN}" ]]; then
|
||||
zfs destroy "${SNAPSHOT}"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
|
||||
MONTH_MIN_QA=$(date --date="- 1 month" -u "+%Y%m")
|
||||
|
||||
zfs list -Hr -o name -t snapshot -r "zpool1/persistent" | grep -E "^zpool1/persistent/[a-zA-Z0-9_-]+-qa@SNAPMONTHLY_[0-9]{6}$" | while read SNAPSHOT_QA; do
|
||||
SNAPSHOT_TIME_QA=$(echo "$SNAPSHOT_QA" | grep -oE "[0-9]+$")
|
||||
if [[ "${SNAPSHOT_TIME_QA}" < "${MONTH_MIN_QA}" ]]; then
|
||||
zfs destroy "${SNAPSHOT_QA}"
|
||||
fi
|
||||
done
|
||||
|
||||
13
script/host/zfs/snapshot/snapshot-minutly.sh
Normal file
13
script/host/zfs/snapshot/snapshot-minutly.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
||||
|
||||
_TIMESTAMP="$(date -u "+%Y%m%d%H%M")"
|
||||
_ZFS_FILESYSTEM="${1:?"Missing first parameter ZFS_FILESYSTEM."}"
|
||||
echo "${_ZFS_FILESYSTEM}" | grep -E '\-prod$' &> /dev/null \
|
||||
&& zfs snapshot "${_ZFS_FILESYSTEM}@SNAPMINUTLY_${_TIMESTAMP}" \
|
||||
&& exit 0
|
||||
|
||||
echo "Snapshot konnte nicht angelegt werden:"
|
||||
echo " - ${_ZFS_FILESYSTEM}@SNAPMINUTLY_${_TIMESTAMP}"
|
||||
echo " (Minuten-Snapshots sollen nur auf 'PROD'-Containeren angelegt werden, sodass diese dann syncronisiert werden)"
|
||||
exit 1
|
||||
57
script/host/zfs/snapshot/snapshot.sh
Normal file
57
script/host/zfs/snapshot/snapshot.sh
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
HOUR=$(date -u "+%Y%m%d%H")
|
||||
DAY=${HOUR:0:8}
|
||||
MONTH=${HOUR:0:6}
|
||||
HOSTOWNER=$(cat /invra/hostowner)
|
||||
if [ ! -d /tmp/locks ]; then
|
||||
mkdir /tmp/locks
|
||||
fi
|
||||
|
||||
zfs list -Hr -o name zpool1/persistent | grep -v -- -BACKUP | tail -n +2 | while read DATASET; do
|
||||
CONTAINER=${DATASET#zpool1/persistent/}
|
||||
(
|
||||
flock -n 9 || exit 1
|
||||
|
||||
MODE_FILE="/invra/state/$HOSTOWNER/containers/$CONTAINER/snapshot-mode"
|
||||
HOURLY=1
|
||||
DAILY=1
|
||||
MONTHLY=1
|
||||
|
||||
if [ -f "$MODE_FILE" ]; then
|
||||
grep -i "NONE" "$MODE_FILE" &> /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
exit
|
||||
fi
|
||||
grep -i "HOURLY" "$MODE_FILE" &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
HOURLY=0
|
||||
fi
|
||||
grep -i "DAILY" "$MODE_FILE" &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
DAILY=0
|
||||
fi
|
||||
grep -i "MONTHLY" "$MODE_FILE" &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
MONTHLY=0
|
||||
fi
|
||||
fi
|
||||
SNAPSHOT_HOUR="${DATASET}@SNAPHOURLY_${HOUR}"
|
||||
SNAPSHOT_DAY="${DATASET}@SNAPDAILY_${DAY}"
|
||||
SNAPSHOT_MONTH="${DATASET}@SNAPMONTHLY_${MONTH}"
|
||||
|
||||
zfs list -H -t snapshot -o name -r "$DATASET" | grep -E "^${SNAPSHOT_HOUR}$" > /dev/null
|
||||
if [[ $? -ne 0 && $HOURLY -eq 1 ]]; then
|
||||
zfs snapshot "${SNAPSHOT_HOUR}"
|
||||
fi
|
||||
|
||||
zfs list -H -t snapshot -o name -r "$DATASET" | grep -E "^${SNAPSHOT_DAY}$" > /dev/null
|
||||
if [[ $? -ne 0 && $DAILY -eq 1 ]]; then
|
||||
zfs snapshot "${SNAPSHOT_DAY}"
|
||||
fi
|
||||
|
||||
zfs list -H -t snapshot -o name -r "$DATASET" | grep -E "^${SNAPSHOT_MONTH}$" > /dev/null
|
||||
if [[ $? -ne 0 && $MONTHLY -eq 1 ]]; then
|
||||
zfs snapshot "${SNAPSHOT_MONTH}"
|
||||
fi
|
||||
) 9>>/tmp/locks/snapshot.${CONTAINER}.lock
|
||||
done
|
||||
27
script/host/zfs/sync/synccontainer-all.sh
Normal file
27
script/host/zfs/sync/synccontainer-all.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
HOSTOWNER=$(cat /invra/hostowner)
|
||||
BACKUPHOST=$(hostname)
|
||||
STATE_DIR=/invra/state/${HOSTOWNER}/containers/;
|
||||
|
||||
screen -ls | grep -oE "[0-9]+\.synccontainer\.[a-zA-Z0-9_-]+" | while read -r SCREEN_SESSION; do
|
||||
CONTAINER=$(echo "$SCREEN_SESSION" | grep -oE "[^.]+$")
|
||||
PID=$(echo "$SCREEN_SESSION" | grep -oE "^[0-9]+")
|
||||
grep -iE "^${BACKUPHOST}$" ${STATE_DIR}/${CONTAINER}/standby-hosts > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "quit screen session ${SCREEN_SESSION}"
|
||||
screen -XS "$PID" quit
|
||||
fi
|
||||
done
|
||||
|
||||
grep -lrE "^${BACKUPHOST}$" /invra/state/${HOSTOWNER}/containers/*/standby-hosts > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
grep -lrE "^${BACKUPHOST}$" /invra/state/${HOSTOWNER}/containers/*/standby-hosts | while read -r STANDBY_FILE; do
|
||||
CONTAINER=$(basename $(dirname ${STANDBY_FILE}))
|
||||
screen -ls | grep -oE "[0-9]+\.synccontainer\.$CONTAINER" > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "starte container sync"
|
||||
screen -dmS "synccontainer.$CONTAINER" /invra/scripts/hosts/zfs/synccontainer.sh "$CONTAINER"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
30
script/host/zfs/sync/synccontainer-check.sh
Normal file
30
script/host/zfs/sync/synccontainer-check.sh
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
TMP="$(mktemp)"
|
||||
(
|
||||
HOSTNAME="$(hostname)"
|
||||
HOSTOWNER="$(cat /invra/hostowner)"
|
||||
MAX_BEHIND=0
|
||||
CURRENT_UNIXTIME=$(date -u +%s)
|
||||
echo "OK#Checks running"
|
||||
for CONTAINER_PATH in /invra/state/${HOSTOWNER}/containers/*; do
|
||||
grep -E "^${HOSTNAME}$" "${CONTAINER_PATH}/standby-hosts" &> /dev/null || continue;
|
||||
CONTAINER_NAME="$(basename "$CONTAINER_PATH")";
|
||||
TS=$(zfs list -o name -r -t snapshot "zpool1/persistent/${CONTAINER_NAME}-BACKUP" | grep "@SYNC_${HOSTNAME}" | head -n1 | grep -oP "\\d{4}-\\d{2}-\\d{2}_\\d{2}:\\d{2}:\\d{2}")
|
||||
LAST_SNAPSHOT_TIME="$(echo "${TS}" | sed "s/_/ /g")"
|
||||
LAST_SNAPSHOT_UNIXTIME=$(date -u --date="TZ=\"UTC\" ${LAST_SNAPSHOT_TIME}" +%s)
|
||||
SECONDS_BEHIND=$[ $CURRENT_UNIXTIME - $LAST_SNAPSHOT_UNIXTIME ]
|
||||
if [ "$SECONDS_BEHIND" -gt "$MAX_BEHIND" ]; then
|
||||
MAX_BEHIND="$SECONDS_BEHIND"
|
||||
fi
|
||||
if [ "$SECONDS_BEHIND" -gt 30 ]; then
|
||||
echo "LAGGING_SYNC_${CONTAINER_NAME}_${HOSTNAME}?FAIL#${SECONDS_BEHIND} behind"
|
||||
|
||||
fi
|
||||
done
|
||||
echo $CURRENT_UNIXTIME
|
||||
) > "$TMP"
|
||||
chmod 655 "$TMP"
|
||||
mkdir -p /var/www/html/monitoring &>/dev/null
|
||||
mv "$TMP" /var/www/html/monitoring/synccontainer.check.txt
|
||||
|
||||
|
||||
64
script/host/zfs/sync/synccontainer-receiver.sh
Normal file
64
script/host/zfs/sync/synccontainer-receiver.sh
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
CONTAINER=${1:?"CONTAINER missing"}
|
||||
CONTAINER=$(echo $1 | sed -E 's|[^a-zA-Z0-9_-]*||g')
|
||||
(
|
||||
flock -n 9 || exit 1
|
||||
|
||||
BACKUPHOST=$(hostname)
|
||||
HOSTOWNER=$(cat /invra/hostowner)
|
||||
SOURCEHOST=$(cat /invra/state/${HOSTOWNER}/containers/${CONTAINER}/current-host)
|
||||
|
||||
MOUNTPOINT="none"
|
||||
DATASET="zpool1/persistent/${CONTAINER}-BACKUP"
|
||||
SNAPSHOT_PREFIX="${DATASET}@SYNC_${BACKUPHOST}_"
|
||||
|
||||
LAST_SNAPSHOT_NAME=""
|
||||
RESUME_TOKEN=""
|
||||
zfs list -Hr -o name -s name "${DATASET}" | grep -E "^${DATASET}$" > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
LAST_SNAPSHOT_NAME=$(zfs list -H -o name -S name -t snapshot -r "${DATASET}" | grep -E "^${SNAPSHOT_PREFIX}" | head -n 1)
|
||||
RESUME_TOKEN="$(zfs get -o value -H receive_resume_token "${DATASET}")"
|
||||
fi
|
||||
|
||||
if [[ "x$RESUME_TOKEN" != "x" && "x$RESUME_TOKEN" != "x-" ]]; then
|
||||
echo "Resume token present trying to resume at $RESUME_TOKEN"
|
||||
LAST_SNAPSHOT_NAME="RESUME"
|
||||
fi
|
||||
|
||||
if [[ "x${LAST_SNAPSHOT_NAME}" != "x" && "${LAST_SNAPSHOT_NAME}" != "RESUME" ]]; then
|
||||
zfs rollback -r "${LAST_SNAPSHOT_NAME}"
|
||||
fi
|
||||
|
||||
# Beiim zfs receive in der nächsten Zeile fehlt noch das "-s" für resumable streams. Der tzrlxsrv kann das aber momentan nicht. Fehlermeldung: cannot receive resume stream: kernel modules must be upgraded to receive this stream.
|
||||
(while sleep 1; do echo; done) | ssh -o ConnectTimeout=20 -C invencom@${SOURCEHOST} "sudo /invra/scripts/hosts/zfs/synccontainer-sender.sh \"${BACKUPHOST}\" \"${CONTAINER}\" \"${LAST_SNAPSHOT_NAME#$SNAPSHOT_PREFIX}\"" \"${RESUME_TOKEN}\" | zfs receive -v "${DATASET}"
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Dataset gegen Veränderungen sichern
|
||||
zfs set readonly=on "${DATASET}"
|
||||
zfs set "mountpoint=${MOUNTPOINT}" "${DATASET}"
|
||||
|
||||
# Aufsetzpunkte fremder Synchronisierer wegräumen
|
||||
zfs list -t snapshot -o name -r "${DATASET}" | grep -- "${DATASET}@SYNC" | grep -v -i "_${BACKUPHOST}_" | while read SNAP; do
|
||||
echo "Destroying $SNAP"
|
||||
zfs destroy $SNAP
|
||||
done
|
||||
|
||||
# Alte Snapshots wegräumen
|
||||
while read -r ZEILE
|
||||
do
|
||||
if [ "$ZEILE" = "" ]; then
|
||||
break
|
||||
fi
|
||||
if [[ "$ZEILE" > "$LAST_SNAPSHOT_NAME" ]]; then
|
||||
break
|
||||
fi
|
||||
zfs destroy "$ZEILE"
|
||||
done < <(zfs list -Hr -o name -s name -t snapshot "${DATASET}" | grep -E "^${SNAPSHOT_PREFIX}")
|
||||
) 9>>/tmp/synccontainer.${CONTAINER}.lock
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
53
script/host/zfs/sync/synccontainer-sender.sh
Normal file
53
script/host/zfs/sync/synccontainer-sender.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
BACKUPHOST=${1:?"BACKUPHOST missing"}
|
||||
CONTAINER=${2:?"CONTAINER missing"}
|
||||
BACKUPHOST=$(echo $1 | sed -E 's|[^a-zA-Z0-9._-]*||g')
|
||||
CONTAINER=$(echo $2 | sed -E 's|[^a-zA-Z0-9_-]*||g')
|
||||
LAST_SNAPSHOT=$(echo $3 | sed -E 's|[^a-zA-Z0-9._:-]*||g')
|
||||
NEW_SNAPSHOT=$(date -u "+%Y-%m-%d_%H:%M:%S")
|
||||
|
||||
if [[ "${LAST_SNAPSHOT}" == "RESUME" ]]; then
|
||||
RESUME_TOKEN=$(echo $4 | sed -E 's|[^a-zA-Z0-9._:-]*||g')
|
||||
zfs send -t "${RESUME_TOKEN}"
|
||||
exit
|
||||
fi
|
||||
|
||||
DATASET="zpool1/persistent/$CONTAINER"
|
||||
SNAPSHOT_PREFIX="${DATASET}@SYNC_${BACKUPHOST}_"
|
||||
LAST_SNAPSHOT_NAME="${SNAPSHOT_PREFIX}${LAST_SNAPSHOT}"
|
||||
NEW_SNAPSHOT_NAME="${SNAPSHOT_PREFIX}${NEW_SNAPSHOT}"
|
||||
SNAPSHOT_FOUND=""
|
||||
|
||||
# Existiert der Snapshot?
|
||||
while read -r ZEILE
|
||||
do
|
||||
if [[ "$ZEILE" == "$LAST_SNAPSHOT_NAME" ]]; then
|
||||
SNAPSHOT_FOUND="1"
|
||||
continue
|
||||
fi
|
||||
done < <(zfs list -H -o name -s name -t snapshot "${DATASET}" | grep -E "^${SNAPSHOT_PREFIX}")
|
||||
|
||||
# Falls ja, alle anderen Snapshots wegräumen - eine frühere Version des Skripts hat hier nur die Älteren weggeräumt. Das führt allerdings zum Vollmüllen
|
||||
# mit neueren Snapshots, wenn der Sync immer wieder fehlschlägt - im Einzelfall bis zur Unbenutzbarkeit des Senders
|
||||
if [[ "${SNAPSHOT_FOUND}x" == "1x" ]]; then
|
||||
while read -r ZEILE
|
||||
do
|
||||
if [[ "$ZEILE" == "$LAST_SNAPSHOT_NAME" ]]; then
|
||||
continue
|
||||
fi
|
||||
zfs destroy "$ZEILE"
|
||||
done < <(zfs list -H -o name -s name -t snapshot "${DATASET}" | grep -E "^${SNAPSHOT_PREFIX}")
|
||||
fi
|
||||
|
||||
zfs snapshot "$NEW_SNAPSHOT_NAME"
|
||||
|
||||
if [[ "$LAST_SNAPSHOT" != "" ]]; then
|
||||
if [[ "$SNAPSHOT_FOUND" == "" ]]; then
|
||||
echo "Angeforderter Snapshot '${LAST_SNAPSHOT}' nicht vorhanden"
|
||||
exit 1;
|
||||
fi
|
||||
zfs send -I "${LAST_SNAPSHOT_NAME}" "${NEW_SNAPSHOT_NAME}"
|
||||
else
|
||||
zfs send "${NEW_SNAPSHOT_NAME}"
|
||||
fi
|
||||
23
script/host/zfs/sync/synccontainer.sh
Normal file
23
script/host/zfs/sync/synccontainer.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
BACKUPHOST=$(hostname)
|
||||
CONTAINER=${1:?"Kein Container angegeben"}
|
||||
DATASET="zpool1/persistent/$CONTAINER"
|
||||
SNAPSHOT_PREFIX="${DATASET}@SYNC_${BACKUPHOST}_"
|
||||
|
||||
while true; do
|
||||
|
||||
/invra/scripts/hosts/zfs/synccontainer-receiver.sh "$CONTAINER"
|
||||
sleep 5
|
||||
|
||||
# LAST_SNAPSHOT_NAME=$(zfs list -Hr -o name -S name -t snapshot "${DATASET}" | grep -E "^${SNAPSHOT_PREFIX}" | head -n 1)
|
||||
# LAST_SNAPSHOT_TIME=${LAST_SNAPSHOT_NAME#${SNAPSHOT_PREFIX}}
|
||||
# LAST_SNAPSHOT_TIME="$(echo "${LAST_SNAPSHOT_TIME}" | sed "s/_/ /g")"
|
||||
# LAST_SNAPSHOT_UNIXTIME=$(date -u --date="TZ=\"UTC\" ${LAST_SNAPSHOT_TIME}" +%s)
|
||||
# CURRENT_UNIXTIME=$(date -u +%s)
|
||||
# SECONDS_BEHIND=$[ $CURRENT_UNIXTIME - $LAST_SNAPSHOT_UNIXTIME ]
|
||||
# mkdir -p /var/www/html/monitoring > /dev/null 2>&1
|
||||
# echo $CURRENT_UNIXTIME > "/var/www/html/monitoring/containersync.${CONTAINER}"
|
||||
# echo "OK: $SECONDS_BEHIND seconds behind" >> "/var/www/html/monitoring/containersync.${CONTAINER}"
|
||||
|
||||
done
|
||||
|
||||
@@ -21,9 +21,8 @@ You can modify the appearance and place your own `check.css` or `logo.png` into
|
||||
- /cis/definitions/YOUR.DOMAIN/monitor/check.css
|
||||
- /cis/definitions/YOUR.DOMAIN/monitor/logo.png
|
||||
|
||||
After the change, you have to call `/cis/script/monitor/setupMonitoringHost.sh` again,
|
||||
because it creates links in '/var/www/html/' and gives the definitions priority over the script.
|
||||
Additional you need to configure a webserver to publish the site.
|
||||
After the change, you must call `/cis/script/monitor/setupMonitoringHost.sh` again,
|
||||
because it creates links in '/var/www/html/' and gives the definitions priority over the script.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ _SOCKET='~/.ssh/%r@%h:%p'
|
||||
|
||||
|
||||
function checkOrStartSSHMaster() {
|
||||
timeout --preserve-status 1 ssh -O check -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} 2>&1 | grep -q -F 'Master running' \
|
||||
timeout --preserve-status 3 ssh -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} exit 0 &> /dev/null \
|
||||
&& return 0
|
||||
|
||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
||||
|
||||
@@ -13,7 +13,7 @@ _SOCKET='~/.ssh/%r@%h:%p'
|
||||
|
||||
|
||||
function checkOrStartSSHMaster() {
|
||||
timeout --preserve-status 1 ssh -O check -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} 2>&1 | grep -q -F 'Master running' \
|
||||
timeout --preserve-status 3 ssh -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} exit 0 &> /dev/null \
|
||||
&& return 0
|
||||
|
||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
function checkPostgresSSLCertificate() {
|
||||
local _SERVER
|
||||
_SERVER="${1:?"FQDN of server missing"}"
|
||||
readonly _SERVER
|
||||
|
||||
local _RESULT
|
||||
_RESULT="$(echo | openssl s_client -starttls postgres -connect "${_SERVER}":5432 -servername "${_SERVER}" 2> /dev/null | openssl x509 -noout -enddate | grep -F 'notAfter=' | cut -d'=' -f2)"
|
||||
readonly _RESULT
|
||||
|
||||
[ -z "${_RESULT}" ] \
|
||||
&& echo "FAIL#Unable to get cert's end date from ${_SERVER}:5432" \
|
||||
&& return 1
|
||||
|
||||
local _ENDDATE
|
||||
_ENDDATE="$(date --date="${_RESULT}" --utc +%s)"
|
||||
readonly _ENDDATE
|
||||
|
||||
! echo "${_ENDDATE}" | grep -q -E "^[0-9]*$" \
|
||||
&& echo "FAIL#Unable to parse end date of certificate" \
|
||||
&& return 1
|
||||
|
||||
local _NOW _REMAINING_DAYS
|
||||
_NOW="$(date --date now +%s)"
|
||||
_REMAINING_DAYS="$(( (_ENDDATE - _NOW) / 86400 ))"
|
||||
readonly _NOW _REMAINING_DAYS
|
||||
|
||||
[ -z "${_REMAINING_DAYS}" ] \
|
||||
&& echo "WARN#Only ${_REMAINING_DAYS} days left" \
|
||||
&& return 1
|
||||
|
||||
echo "OK#${_REMAINING_DAYS} days remaining"
|
||||
return 0
|
||||
}
|
||||
|
||||
checkPostgresSSLCertificate "${@}" && exit 0 || exit 1
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
_URL="${1:?"URL of site missing"}"
|
||||
|
||||
#curl:
|
||||
# --connect-timeout SECONDS Maximum time allowed for connection
|
||||
# -k Allow connections to SSL sites without certs (H)
|
||||
@@ -12,51 +14,4 @@
|
||||
#grep:
|
||||
# -q Quite, no output just status codes
|
||||
# -F Interpret search term as plain text
|
||||
function checkUrl() {
|
||||
local _URL _SEARCH_STRING
|
||||
_URL="${1:?"URL of site missing"}"
|
||||
_SEARCH_STRING="${2}"
|
||||
readonly _URL _SEARCH_STRING
|
||||
|
||||
local _RESULT
|
||||
if [ -z "${_SEARCH_STRING}" ]; then
|
||||
_RESULT="$(curl --connect-timeout 10 --max-time 10 --no-progress-meter --verbose "${_URL}" 2>&1 | grep -o -E "(expire.*|HTTP.*200 OK)")"
|
||||
else
|
||||
_RESULT="$(curl --connect-timeout 10 --max-time 10 --no-progress-meter --verbose "${_URL}" 2>&1 | grep -o -E "(expire.*|HTTP.*200 OK|${_SEARCH_STRING})")"
|
||||
fi
|
||||
readonly _RESULT
|
||||
|
||||
! echo "${_RESULT}" | grep -q -F '200 OK' \
|
||||
&& echo "FAIL#Status code 200 not found" \
|
||||
&& return 1
|
||||
|
||||
! [ -z "${_SEARCH_STRING}" ] \
|
||||
&& ! echo "${_RESULT}" | grep -q -F "${_SEARCH_STRING}" \
|
||||
&& echo "FAIL#Search string not found" \
|
||||
&& return 1
|
||||
|
||||
local _ENDDATE
|
||||
_ENDDATE="$(echo "${_RESULT}" | grep -F 'expire' | cut -d':' -f2-)"
|
||||
_ENDDATE="$(date --date="${_ENDDATE}" --utc +%s)"
|
||||
readonly _ENDDATE
|
||||
|
||||
! echo "${_ENDDATE}" | grep -q -E "^[0-9]*$" \
|
||||
&& echo "FAIL#Unable to parse end date of certificate" \
|
||||
&& return 1
|
||||
|
||||
local _NOW _REMAINING_DAYS
|
||||
_NOW="$(date --date now +%s)"
|
||||
_REMAINING_DAYS="$(( (_ENDDATE - _NOW) / 86400 ))"
|
||||
readonly _NOW _REMAINING_DAYS
|
||||
|
||||
# less than 30 days remaining => should be warned
|
||||
[ "${_REMAINING_DAYS}" -le "30" ] \
|
||||
&& echo "WARN#Certificate: only ${_REMAINING_DAYS} days left" \
|
||||
&& return 1
|
||||
|
||||
echo "OK#Certificate: ${_REMAINING_DAYS} days remaining"
|
||||
return 0
|
||||
}
|
||||
|
||||
#((curl --connect-timeout 10 --max-time 10 -k -s --head --no-progress-meter "${_URL}" | grep -qF '200 OK') && echo OK) || echo FAIL
|
||||
checkUrl "${1}" "${2}" && exit 0 || exit 1
|
||||
((curl --connect-timeout 10 --max-time 10 -k -s --head --no-progress-meter "${_URL}" | grep -qF '200 OK') && echo OK) || echo FAIL
|
||||
|
||||
@@ -14,7 +14,7 @@ _SOCKET='~/.ssh/%r@%h:%p'
|
||||
|
||||
|
||||
function checkOrStartSSHMaster() {
|
||||
timeout --preserve-status 1 ssh -O check -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} 2>&1 | grep -q -F 'Master running' \
|
||||
timeout --preserve-status 3 ssh -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} exit 0 &> /dev/null \
|
||||
&& return 0
|
||||
|
||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
||||
|
||||
@@ -30,7 +30,7 @@ _DEBUG_PATH="/tmp/monitor/"
|
||||
|
||||
|
||||
function checkOrStartSSHMaster() {
|
||||
timeout --preserve-status 1 ssh -O check -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} 2>&1 | grep -q -F 'Master running' \
|
||||
timeout --preserve-status 3 ssh -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} exit 0 &> /dev/null \
|
||||
&& return 0
|
||||
|
||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
||||
|
||||
@@ -13,7 +13,7 @@ _SOCKET='~/.ssh/%r@%h:%p'
|
||||
|
||||
|
||||
function checkOrStartSSHMaster() {
|
||||
timeout --preserve-status 1 ssh -O check -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} 2>&1 | grep -q -F 'Master running' \
|
||||
timeout --preserve-status 3 ssh -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} exit 0 &> /dev/null \
|
||||
&& return 0
|
||||
|
||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
###########################################################################
|
||||
# Dockerfile to build a Container to update TLS Certificates automatically.
|
||||
# Based on latest Ubuntu LTS
|
||||
###########################################################################
|
||||
# See https://hub.docker.com/_/ubuntu
|
||||
FROM ubuntu:latest
|
||||
|
||||
# Update repositories
|
||||
RUN echo Version 20251030v1
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y apt-utils
|
||||
|
||||
#### BEGIN INSTALLATION ###################################################
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y cron curl dnsutils openssh-client
|
||||
|
||||
ADD acme.sh-3.1.1.tar.gz /tmp/acme.sh-setup/
|
||||
COPY renewCerts.sh /renewCerts.sh
|
||||
COPY start.sh /start.sh
|
||||
|
||||
# Run the command on container startup
|
||||
CMD ["bash", "/start.sh"]
|
||||
@@ -1,52 +0,0 @@
|
||||
Issuing SSL certificates
|
||||
========================
|
||||
There are two modes you can use the script `renewCerts.sh`.
|
||||
|
||||
1. dns
|
||||
2. http
|
||||
|
||||
|
||||
|
||||
Dns mode
|
||||
--------
|
||||
This mode is meant to use inside a docker container defined by the `Dockerfile`.
|
||||
To configure, build and run the Container there is a file `docker-compose.yml.template`.
|
||||
You can copy this file to `docker-compose.yml` and set the needed environment variables there.
|
||||
|
||||
- __AUTOACME_CONTAINER_HOSTNAME__
|
||||
is used to enable the use of the host name within the container.
|
||||
For example, for meaningful commit messages.
|
||||
- __AUTOACME_GIT_REPOSITORY_VIA_SSH__ (optional)
|
||||
is used to specify a Git repository to which the keys and certificates are transferred.
|
||||
Therefore, SSH keys are generated on first launch (`docker compose up -d`) and the repository is cloned to `~/acmeResults/`.
|
||||
The public key must be granted __write access__ to the repository
|
||||
(e.g. as repository's deploy key).
|
||||
The key can be viewed via `docker compose logs`.
|
||||
- __AUTOACME_PATH_IN_GIT_REPOSITORY__ (optional)
|
||||
specifies a path inside the repository were the certiticates are saved.
|
||||
(e.g. AUTOACME_PATH_IN_GIT_REPOSITORY="/foo/bar/" => /root/autoACME/foo/bar/your-domain.net/fullchain.crt)
|
||||
- __AUTOACME_DNS_PROVIDER__
|
||||
sets the provider modul of acme.sh used to communicate with your domain provider.
|
||||
(For further information see: https://github.com/acmesh-official/acme.sh/wiki/dnsapi)
|
||||
|
||||
You may have to set additional environment variables depending on your provider...
|
||||
|
||||
|
||||
|
||||
### Manual docker commands
|
||||
Instead of using `docker compose` you can build and run the container manually:
|
||||
```
|
||||
docker build -t cis/autoacme .
|
||||
docker run --name autoacme -d cis/autoacme
|
||||
```
|
||||
This may be useful for investiagtion...
|
||||
|
||||
|
||||
Http mode
|
||||
---------
|
||||
If you plan to use `renewCerts.sh` directly on your host computer this mode may fit your needs.
|
||||
Here you need a `nginx` webserver. The domain have to point to it and following configuration is needed:
|
||||
|
||||
1. The content of folder `/var/www/letsencrypt/.well-known/acme-challenge/` has to be accessable via `http://your-domain.net/.well-known/acme-challenge/`
|
||||
2. The certificates are stored to `/etc/nginx/ssl`. If this folder is a git repository then changes will be commited and pushed.
|
||||
3. An entry into the crontab is needed to do automatic updates.
|
||||
Binary file not shown.
@@ -1,18 +0,0 @@
|
||||
services:
|
||||
autoacme:
|
||||
container_name: autoacme
|
||||
image: cis/autoacme
|
||||
build: .
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
environment:
|
||||
AUTOACME_CONTAINER_HOSTNAME: ${HOSTNAME:?"HINT - You may run 'export HOSTNAME' first."}
|
||||
AUTOACME_GIT_REPOSITORY_VIA_SSH: 'ssh://git@git.your-domain.net/your-repo.git'
|
||||
# Optionally you can set a path inside the git repository, requires a repository.
|
||||
AUTOACME_PATH_IN_GIT_REPOSITORY: 'hosts/all/etc/ssl/domains/'
|
||||
# See: https://github.com/acmesh-official/acme.sh/wiki/dnsapi and search your provider like 'hetzner' e.g.
|
||||
AUTOACME_DNS_PROVIDER: 'dns_hetzner'
|
||||
HETZNER_Token: 'your-token'
|
||||
|
||||
@@ -1,550 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# curl http://your.domain.net/.well-known/acme-challenge/test
|
||||
# curl http://85.183.145.8/.well-known/acme-challenge/test
|
||||
# /var/www/letsencrypt/.well-known/acme-challenge
|
||||
|
||||
function checkConfigViaHttp() {
|
||||
local _DOMAIN _MODE _LOCAL_FOLDER
|
||||
_MODE="${1:?"checkConfigViaHttp(): Missing first parameter MODE"}"
|
||||
_DOMAIN="${2:?"checkConfigViaHttp(): Missing second parameter DOMAIN"}"
|
||||
_LOCAL_FOLDER="/var/www/letsencrypt/.well-known/acme-challenge/"
|
||||
readonly _DOMAIN _MODE _LOCAL_FOLDER
|
||||
|
||||
local _LOCAL_FILE _LOCAL_URL _PUBLIC_URL
|
||||
_LOCAL_FILE="${_LOCAL_FOLDER}${_DOMAIN}"
|
||||
_LOCAL_URL="http://localhost/.well-known/acme-challenge/${_DOMAIN}"
|
||||
_PUBLIC_URL="http://${_DOMAIN}/.well-known/acme-challenge/${_DOMAIN}"
|
||||
readonly _LOCAL_FILE _LOCAL_URL _PUBLIC_URL
|
||||
|
||||
# Skip check if mode is not http
|
||||
[ "${_MODE}" != "http" ] \
|
||||
&& return 0
|
||||
|
||||
# Fail because wildcard certificate
|
||||
[ "${_MODE}" == "http" ] \
|
||||
&& isWildcardCertificate "${_DOMAIN}" \
|
||||
&& echo "Wildcard certificates are not supported via HTTP." \
|
||||
&& return 1
|
||||
|
||||
_CHECK="Available on $(hostname)@$(date)"
|
||||
|
||||
echo -n "Check domain '${_DOMAIN}'..." \
|
||||
&& [ -d "${_LOCAL_FOLDER}" ] \
|
||||
&& echo "${_CHECK}" > "${_LOCAL_FILE}" \
|
||||
&& curl -4s "${_PUBLIC_URL}" | grep -q "${_CHECK}" \
|
||||
&& echo " Done" \
|
||||
&& return 0
|
||||
|
||||
echo
|
||||
echo "FAILED: configuration of domain '${_DOMAIN}' is INCORRECT:"
|
||||
echo -n " ${_PUBLIC_URL} was not found."
|
||||
|
||||
curl -4s "${_LOCAL_URL}" | grep -q "${_CHECK}" \
|
||||
&& echo " (check DNS first)" \
|
||||
|| echo " (check Webserver first)"
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function isActive() {
|
||||
local _DOMAIN _MODE _RESULT_CERTS
|
||||
_RESULT_CERTS="${RESULT_CERTS:?"isActive(): Missing global parameter RESULT_CERTS"}"
|
||||
_MODE="${1:?"isActive(): Missing first parameter MODE"}"
|
||||
_DOMAIN="${2:?"isActive(): Missing second parameter DOMAIN"}"
|
||||
readonly _DOMAIN _MODE _RESULT_CERTS
|
||||
|
||||
# If mode is dns the domain is active always
|
||||
[ "${_MODE}" == "dns" ] \
|
||||
&& return 0
|
||||
|
||||
nginx -T 2> /dev/null | grep -q "${_RESULT_CERTS}${_DOMAIN}/fullchain.crt" \
|
||||
&& return 0
|
||||
|
||||
echo "Domain '${_DOMAIN}' is inaktiv and therefore it will be skipped."
|
||||
return 1
|
||||
}
|
||||
|
||||
function isGitRepository() {
|
||||
local _FOLDER
|
||||
_FOLDER="${1:?"isGitRepository(): Missing first parameter FOLDER"}"
|
||||
readonly _FOLDER
|
||||
|
||||
git -C "${_FOLDER}" ls-tree main &> /dev/null \
|
||||
&& return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function isWildcardCertificate() {
|
||||
local _DOMAIN
|
||||
_DOMAIN="${1:?"isWildcardCertificate(): Missing first parameter DOMAIN"}"
|
||||
readonly _DOMAIN
|
||||
|
||||
echo "${_DOMAIN}" | grep -q -F "_." \
|
||||
&& return 0
|
||||
|
||||
echo "${_DOMAIN}" | grep -q -F "*." \
|
||||
&& return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function tryGitPush() {
|
||||
local _DOMAIN _NOW _RESULT_CERTS
|
||||
_RESULT_CERTS="${RESULT_CERTS:?"tryGitPush(): Missing global parameter RESULT_CERTS"}"
|
||||
_DOMAIN="${1:?"tryGitPush(): Missing first parameter DOMAIN"}"
|
||||
_NOW="$(date +%Y%m%d_%H%M)"
|
||||
readonly _DOMAIN _NOW _RESULT_CERTS
|
||||
|
||||
! isGitRepository "${_RESULT_CERTS}" \
|
||||
&& echo \
|
||||
&& echo "Folder '${_RESULT_CERTS}' is not part of a git repository, therefore nothing will be pushed." \
|
||||
&& return 1
|
||||
|
||||
pushd "${_RESULT_CERTS}" > /dev/null
|
||||
git pull > /dev/null
|
||||
git add * > /dev/null
|
||||
git commit -m "${_NOW} - Certificate for '${_DOMAIN}' was updated." \
|
||||
&& git push > /dev/null \
|
||||
&& popd > /dev/null \
|
||||
&& echo "SUCCESS: certificate for '${_DOMAIN}' pushed." \
|
||||
&& return 0
|
||||
|
||||
popd > /dev/null
|
||||
echo "FAILED: unable to push certificate for '${_DOMAIN}'."
|
||||
return 0
|
||||
}
|
||||
|
||||
function own() {
|
||||
! [ -d "${RESULT_CERTS:?"own(): Missing global parameter RESULT_CERTS"}" ] \
|
||||
&& echo "Trying to derive domain names from subfolders of '${RESULT_CERTS}', but it is not a folder!" \
|
||||
&& return 1
|
||||
|
||||
local _DOMAINS _MODE
|
||||
_DOMAINS=("${RESULT_CERTS}"*)
|
||||
_MODE="${1:?"own(): Missing first parameter MODE"}"
|
||||
readonly _DOMAINS _MODE
|
||||
|
||||
local _domain
|
||||
for _domain in "${_DOMAINS[@]}"; do
|
||||
# just take names of folders
|
||||
! [ -d "${_domain}" ] && continue
|
||||
# cut pfad (like basename)
|
||||
_domain="${_domain##*/}"
|
||||
|
||||
# folder default => skip
|
||||
[ "${_domain}" == "default" ] && continue
|
||||
|
||||
case "${_MODE}" in
|
||||
dns)
|
||||
# dns supports all options
|
||||
;;
|
||||
http)
|
||||
# http and wildcard certifikate => skip
|
||||
isWildcardCertificate "${_domain}" && continue
|
||||
# ssl on domain inaktiv => skip
|
||||
! isActive "{_MODE}" "${_domain}" && continue
|
||||
;;
|
||||
*)
|
||||
echo "Unknown mode: ${_MODE}"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
single "${_MODE}" "${_domain}" "${2}"
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function continueIssuingCertificate() {
|
||||
local _CERT_FILE_FULLCHAIN _DOMAIN
|
||||
_CERT_FILE_FULLCHAIN="${1:?"continueIssuingCertificate(): Missing first parameter CERT_FILE_FULLCHAIN"}"
|
||||
_DOMAIN="${2:?"continueIssuingCertificate(): Missing second parameter DOMAIN"}"
|
||||
local _CERT_FILE_FULLCHAIN _DOMAIN
|
||||
|
||||
local _PRETTY_DOMAIN
|
||||
_PRETTY_DOMAIN="$(printPrettyDomain ${_DOMAIN})"
|
||||
readonly _PRETTY_DOMAIN
|
||||
|
||||
# forced => should be issued
|
||||
[ "${3:-""}" == "--force" ] \
|
||||
&& echo "Certificate for domain '${_PRETTY_DOMAIN}' is forced to be issued." \
|
||||
&& return 0
|
||||
|
||||
# no cert => should be issued
|
||||
! [ -f "${_CERT_FILE_FULLCHAIN}" ] \
|
||||
&& echo "No certificate for domain '${_PRETTY_DOMAIN}', so it will be issued." \
|
||||
&& return 0
|
||||
|
||||
local _ENDDATE _NOW _REMAINING_DAYS
|
||||
_ENDDATE="$(openssl x509 -enddate -noout -in ${_CERT_FILE_FULLCHAIN} | cut -d= -f2)"
|
||||
_ENDDATE="$(date --date="${_ENDDATE}" --utc +%s)"
|
||||
|
||||
_NOW="$(date --date now +%s)"
|
||||
_REMAINING_DAYS="$(( (_ENDDATE - _NOW) / 86400 ))"
|
||||
readonly _ENDDATE _NOW _REMAINING_DAYS
|
||||
|
||||
# less than 30 days remaining => should be issued
|
||||
[ "${_REMAINING_DAYS}" -le "30" ] \
|
||||
&& echo "Certificate for domain '${_PRETTY_DOMAIN}' (${_REMAINING_DAYS} days remaining) will be issued." \
|
||||
&& return 0
|
||||
|
||||
echo "Certificate for domain '${_PRETTY_DOMAIN}' (${_REMAINING_DAYS} days remaining) will be skipped."
|
||||
return 1
|
||||
}
|
||||
|
||||
function printBaseDomain() {
|
||||
local _DOMAIN
|
||||
_DOMAIN="${1:?"printBaseDomain(): Missing first parameter DOMAIN"}"
|
||||
readonly _DOMAIN
|
||||
|
||||
local _BASE_DOMAIN
|
||||
# cut front '*.' or '_.'
|
||||
_BASE_DOMAIN="${_DOMAIN#\*.}"
|
||||
_BASE_DOMAIN="${_BASE_DOMAIN#_.}"
|
||||
readonly _BASE_DOMAIN
|
||||
|
||||
echo "${_BASE_DOMAIN}" \
|
||||
&& return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function printPrettyDomain() {
|
||||
local _BASE_DOMAIN _DOMAIN
|
||||
_DOMAIN="${1:?"printPrettyDomain(): Missing first parameter DOMAIN"}"
|
||||
_BASE_DOMAIN="$(printBaseDomain ${_DOMAIN})"
|
||||
readonly _BASE_DOMAIN _DOMAIN
|
||||
|
||||
isWildcardCertificate "${_DOMAIN}" \
|
||||
&& echo "*.${_BASE_DOMAIN}" \
|
||||
&& return 0
|
||||
|
||||
echo "${_DOMAIN}" \
|
||||
&& return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function printFullDomainFolder() {
|
||||
local _BASE_DOMAIN _DOMAIN _RESULT_CERTS
|
||||
_RESULT_CERTS="${RESULT_CERTS:?"printFullDomainFolder(): Missing global parameter RESULT_CERTS"}"
|
||||
_DOMAIN="${1:?"printFullDomainFolder(): Missing first parameter DOMAIN"}"
|
||||
_BASE_DOMAIN="$(printBaseDomain ${_DOMAIN})"
|
||||
readonly _BASE_DOMAIN _DOMAIN _RESULT_CERTS
|
||||
|
||||
isWildcardCertificate "${_DOMAIN}" \
|
||||
&& echo "${_RESULT_CERTS}_.${_BASE_DOMAIN}/" \
|
||||
&& return 0
|
||||
|
||||
echo "${_RESULT_CERTS}${_DOMAIN}/" \
|
||||
&& return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function prepareFullDomainFolder() {
|
||||
local _DOMAIN _DOMAIN_FOLDER
|
||||
_DOMAIN="${1:?"prepareFullDomainFolder(): Missing first parameter DOMAIN"}"
|
||||
_DOMAIN_FOLDER="$(printFullDomainFolder "${_DOMAIN}")"
|
||||
readonly _DOMAIN _DOMAIN_FOLDER
|
||||
|
||||
[ -d "${_DOMAIN_FOLDER}" ] \
|
||||
&& return 0
|
||||
|
||||
# create folder for results
|
||||
echo -n "Creating folder '${_DOMAIN_FOLDER}'... " \
|
||||
&& mkdir -p "${_DOMAIN_FOLDER}" \
|
||||
&& echo "Done"
|
||||
|
||||
[ -d "${_DOMAIN_FOLDER}" ] \
|
||||
&& return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function prepareAndCheckAliasDomain() {
|
||||
local _ALIAS_DOMAIN _DOMAIN
|
||||
_DOMAIN="${1:?"prepareAndCheckAliasDomain(): Missing first parameter DOMAIN"}"
|
||||
_ALIAS_DOMAIN="${2:?"prepareAndCheckAliasDomain(): Missing second parameter ALIAS_DOMAIN"}"
|
||||
readonly _ALIAS_DOMAIN _DOMAIN
|
||||
|
||||
local _BASE_DOMAIN _CHALLENGE_ALIAS_DOMAIN_FILE _DOMAIN_FOLDER
|
||||
_BASE_DOMAIN="$(printBaseDomain ${_DOMAIN})"
|
||||
_DOMAIN_FOLDER="$(printFullDomainFolder ${_DOMAIN})"
|
||||
_CHALLENGE_ALIAS_DOMAIN_FILE="${_DOMAIN_FOLDER}challenge-alias-domain"
|
||||
readonly _BASE_DOMAIN _CHALLENGE_ALIAS_DOMAIN_FILE _DOMAIN_FOLDER
|
||||
|
||||
[ -d "${_DOMAIN_FOLDER}" ] \
|
||||
&& [ "$(dig +short _acme-challenge.${_BASE_DOMAIN} CNAME)" == "_acme-challenge.${_ALIAS_DOMAIN}." ] \
|
||||
&& echo "${_ALIAS_DOMAIN}" > "${_CHALLENGE_ALIAS_DOMAIN_FILE}" \
|
||||
&& echo "SUCCESS: alias domain '${_ALIAS_DOMAIN}' is used when issuing certificates for '${_BASE_DOMAIN}' via DNS." \
|
||||
&& return 0
|
||||
|
||||
echo "FAILED: unable to use alias domain '${_ALIAS_DOMAIN}' to issue certificates for '${_BASE_DOMAIN}'."
|
||||
echo " You have to configure your domain '${_BASE_DOMAIN}' first before you can use the alias domain as proof."
|
||||
echo " So check if there is a CNAME entry '_acme-challenge.${_BASE_DOMAIN}' pointing to:"
|
||||
echo " - '_acme-challenge.${_ALIAS_DOMAIN}'"
|
||||
return 1
|
||||
}
|
||||
|
||||
function single() {
|
||||
local _ACME_FILE _DOMAIN _MODE _RESULT_CERTS
|
||||
_RESULT_CERTS="${RESULT_CERTS:?"single(): Missing global parameter RESULT_CERTS"}"
|
||||
_ACME_FILE="${ACME_FILE:?"single(): Missing global parameter ACME_FILE"}"
|
||||
_MODE="${1:?"single(): Missing first parameter MODE"}"
|
||||
_DOMAIN="${2:?"single(): Missing second parameter DOMAIN"}"
|
||||
readonly _ACME_FILE _DOMAIN _MODE _RESULT_CERTS
|
||||
|
||||
local _BASE_DOMAIN _CHALLENGE_ALIAS_DOMAIN_FILE _DOMAIN_FOLDER _PRETTY_DOMAIN
|
||||
_BASE_DOMAIN="$(printBaseDomain ${_DOMAIN})"
|
||||
_DOMAIN_FOLDER="$(printFullDomainFolder ${_DOMAIN})"
|
||||
_PRETTY_DOMAIN="$(printPrettyDomain ${_DOMAIN})"
|
||||
_CHALLENGE_ALIAS_DOMAIN_FILE="${_DOMAIN_FOLDER}challenge-alias-domain"
|
||||
readonly _BASE_DOMAIN _CHALLENGE_ALIAS_DOMAIN_FILE _DOMAIN_FOLDER _PRETTY_DOMAIN
|
||||
|
||||
! [ -f "${_ACME_FILE}" ] \
|
||||
&& echo "Program 'acme.sh' seams not to be installed. Try run 'renewCerts.sh --setup'." \
|
||||
&& return 1
|
||||
|
||||
# cancel on broken configuration
|
||||
! checkConfigViaHttp "${_MODE}" "${_DOMAIN}" \
|
||||
&& return 1
|
||||
|
||||
# cancel if folder is not prepared
|
||||
! [ -d "${_DOMAIN_FOLDER}" ] \
|
||||
&& echo "Certificate of domain '${_PRETTY_DOMAIN}' skipped because of missing folder:" \
|
||||
&& echo " - '${_DOMAIN_FOLDER}'" \
|
||||
&& return 1
|
||||
|
||||
# check enddate if third parameter is not --force
|
||||
! continueIssuingCertificate "${_DOMAIN_FOLDER}fullchain.crt" "${_DOMAIN}" "${3:-""}" \
|
||||
&& return 0
|
||||
|
||||
# backup the keys
|
||||
[ -f "${_DOMAIN_FOLDER}fullchain.crt" ] \
|
||||
&& cp "${_DOMAIN_FOLDER}fullchain.crt" "${_DOMAIN_FOLDER}fullchain.crt.bak"
|
||||
[ -f "${_DOMAIN_FOLDER}private.key" ] \
|
||||
&& cp --preserve=mode,ownership "${_DOMAIN_FOLDER}private.key" "${_DOMAIN_FOLDER}private.key.bak"
|
||||
|
||||
local _OPTIONS
|
||||
# always --force because we check expiring on ourself
|
||||
# _OPTIONS="--issue --force --test"
|
||||
_OPTIONS="--issue --force"
|
||||
if [ "${_MODE}" == "dns" ]; then
|
||||
_OPTIONS="${_OPTIONS} --dns ${AUTOACME_DNS_PROVIDER:?"single(): Missing global parameter AUTOACME_DNS_PROVIDER"}"
|
||||
[ -f "${_CHALLENGE_ALIAS_DOMAIN_FILE}" ] \
|
||||
&& _OPTIONS="${_OPTIONS} --challenge-alias $(cat "${_CHALLENGE_ALIAS_DOMAIN_FILE}")"
|
||||
isWildcardCertificate "${_DOMAIN}" \
|
||||
&& _OPTIONS="${_OPTIONS} --domain ${_PRETTY_DOMAIN}"
|
||||
elif [ "${_MODE}" == "http" ]; then
|
||||
_OPTIONS="${_OPTIONS} --webroot /var/www/letsencrypt"
|
||||
fi
|
||||
readonly _OPTIONS
|
||||
|
||||
${_ACME_FILE} ${_OPTIONS} \
|
||||
--domain "${_BASE_DOMAIN}" \
|
||||
--server "letsencrypt" \
|
||||
--keylength "ec-384" \
|
||||
--fullchain-file "${_DOMAIN_FOLDER}fullchain.crt" \
|
||||
--key-file "${_DOMAIN_FOLDER}private.key" \
|
||||
&& openssl pkcs12 -export -in "${_DOMAIN_FOLDER}fullchain.crt" -inkey "${_DOMAIN_FOLDER}private.key" -out "${_DOMAIN_FOLDER}bundle.pkx" -passout pass: \
|
||||
&& echo "Certificate of domain '${_PRETTY_DOMAIN}' was updated." \
|
||||
&& tryGitPush "${_PRETTY_DOMAIN}" \
|
||||
&& return 0
|
||||
|
||||
echo "Certificate of domain '${_PRETTY_DOMAIN}' remains unchanged."
|
||||
return 0
|
||||
}
|
||||
|
||||
function isInstalled() {
|
||||
local _ACME_FILE
|
||||
_ACME_FILE="${ACME_FILE:?"isInstalled(): Missing global parameter ACME_FILE"}"
|
||||
readonly _ACME_FILE
|
||||
|
||||
[ -f "${_ACME_FILE}" ] \
|
||||
&& echo "Following version of acme.sh is installed:" \
|
||||
&& echo "------------------------------------------" \
|
||||
&& ${_ACME_FILE} --version | tail -n 1 \
|
||||
&& return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
function extractTarArchive() {
|
||||
local _ACME_SETUP_FILE _ACME_TAR_FILE
|
||||
_ACME_SETUP_FILE="${ACME_SETUP_FILE:?"extractTarArchive(): Missing global parameter ACME_SETUP_FILE"}"
|
||||
_ACME_TAR_FILE="${ACME_TAR_FILE:?"extractTarArchive(): Missing global parameter ACME_TAR_FILE"}"
|
||||
readonly _ACME_SETUP_FILE _ACME_TAR_FILE
|
||||
|
||||
# extracted file already exists
|
||||
[ -f "${_ACME_SETUP_FILE}" ] \
|
||||
&& return 0
|
||||
|
||||
[ -f "${_ACME_TAR_FILE}" ] \
|
||||
&& mkdir -p "/tmp/acme.sh-setup/" \
|
||||
&& tar -xzf "${_ACME_TAR_FILE}" -C "/tmp/acme.sh-setup/" \
|
||||
&& [ -f "${_ACME_SETUP_FILE}" ] \
|
||||
&& return 0
|
||||
|
||||
echo "Missing setup file '${_ACME_SETUP_FILE}' after trying to extract '${_ACME_TAR_FILE}'"
|
||||
return 1
|
||||
}
|
||||
|
||||
function setup() {
|
||||
local _ACME_SETUP_FILE
|
||||
_ACME_SETUP_FILE="${ACME_SETUP_FILE:?"setup(): Missing global parameter ACME_SETUP_FILE"}"
|
||||
readonly _ACME_SETUP_FILE
|
||||
|
||||
isInstalled \
|
||||
&& return 0
|
||||
|
||||
! [ $(id -u) == 0 ] \
|
||||
&& echo "Setup requires execution as user 'root'." \
|
||||
&& exit 1
|
||||
|
||||
! [ "$(echo $HOME)" == "/root" ] \
|
||||
&& echo "The setup is executed with 'root' privileges but not in the 'root' user environment." \
|
||||
&& exit 1
|
||||
|
||||
! extractTarArchive \
|
||||
&& exit 1
|
||||
|
||||
echo "Starting install of acme.sh:"
|
||||
echo "----------------------------"
|
||||
pushd "${_ACME_SETUP_FILE%/*}" > /dev/null 2>&1 #Removes shortest matching pattern '/*' from the end
|
||||
./acme.sh --install --no-cron --no-profile 2>&1
|
||||
popd > /dev/null 2>&1
|
||||
isInstalled \
|
||||
&& echo \
|
||||
&& echo 'Now this script can be added into cron-tab (crontab -e), like this e.g.:' \
|
||||
&& echo \
|
||||
&& echo '# Each day at 6:00am renew certificates:' \
|
||||
&& echo '0 6 * * * /renewCerts.sh --http --own > /var/log/renewCerts.sh.log 2>&1' \
|
||||
&& return 0
|
||||
|
||||
echo "Something went wrong during setup."
|
||||
return 1
|
||||
}
|
||||
|
||||
function usage() {
|
||||
echo
|
||||
echo 'Commands:'
|
||||
echo ' --prepare DOMAIN --usingAlias ALIAS-DOMAIN : Prepares a domain to issue certificate using an alias domain in DNS mode.'
|
||||
echo ' See: https://github.com/acmesh-official/acme.sh/wiki/DNS-alias-mode'
|
||||
echo ' --dns --single DOMAIN [--force] : Issues a certificate for the given domain using DNS mode.'
|
||||
echo ' --http --single DOMAIN [--force] : Issues a certificate for the given domain using HTTP mode.'
|
||||
echo
|
||||
echo ' (--dns|--http) --own [--force] : Iterates all domains found in RESULT_CERTS.'
|
||||
echo
|
||||
echo 'Current environment:'
|
||||
echo " Full name of this script: OWN_FULLNAME='${OWN_FULLNAME}'"
|
||||
echo " Configuration:"
|
||||
echo " Version of 'acme.sh' that will be installed: ACME_VERSION='${ACME_VERSION}'"
|
||||
echo " Tar file containing the setup of 'acme.sh': ACME_TAR_FILE='${ACME_TAR_FILE}'"
|
||||
echo " Setup file of 'acme.sh' after extraction: ACME_SETUP_FILE='${ACME_SETUP_FILE}'"
|
||||
echo " Full name of the installed script 'acme.sh': ACME_FILE='${ACME_FILE}'"
|
||||
echo " Output:"
|
||||
echo " Path were the issued certificate are saved: RESULT_CERTS='${RESULT_CERTS}'"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function main() {
|
||||
|
||||
echo
|
||||
|
||||
[ -f "/autoACME.env" ] \
|
||||
&& source "/autoACME.env" \
|
||||
&& echo "[$(date)] Environment '/autoACME.env' loaded."
|
||||
|
||||
local ACME_FILE ACME_VERSION OWN_FULLNAME
|
||||
OWN_FULLNAME="$(readlink -e ${0})"
|
||||
ACME_FILE="/root/.acme.sh/acme.sh"
|
||||
ACME_VERSION="acme.sh-3.1.1"
|
||||
readonly ACME_FILE ACME_VERSION OWN_FULLNAME
|
||||
|
||||
local ACME_SETUP_FILE ACME_TAR_FILE RESULT_CERTS
|
||||
ACME_SETUP_FILE="/tmp/acme.sh-setup/${ACME_VERSION}/acme.sh"
|
||||
ACME_TAR_FILE="${OWN_FULLNAME%/*}/${ACME_VERSION}.tar.gz"
|
||||
RESULT_CERTS="${AUTOACME_RESULT_CERTS%/}" #Removes shortest matching pattern '/' from the end
|
||||
RESULT_CERTS="${RESULT_CERTS:-"/etc/nginx/ssl"}/"
|
||||
readonly ACME_SETUP_FILE ACME_TAR_FILE RESULT_CERTS
|
||||
|
||||
local REPOSITORY_URL
|
||||
isGitRepository "${RESULT_CERTS}" \
|
||||
&& REPOSITORY_URL="$(git -C ${RESULT_CERTS} config --get remote.origin.url)"
|
||||
readonly REPOSITORY_URL
|
||||
|
||||
case "${1}" in
|
||||
--dns)
|
||||
case "${2}" in
|
||||
--single)
|
||||
echo "[$(date)] Issue single certificate '${3}' via DNS:" \
|
||||
&& prepareFullDomainFolder "${3}" \
|
||||
&& single "dns" "${3}" "${4}" \
|
||||
&& return 0
|
||||
;;
|
||||
--own)
|
||||
echo "[$(date)] Renewing own certificates via DNS:"
|
||||
own "dns" "${3}" \
|
||||
&& echo "Finished successfully." \
|
||||
&& return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
--http)
|
||||
case "${2}" in
|
||||
--single)
|
||||
echo "[$(date)] Issue single certificate '${3}' via HTTP:" \
|
||||
&& prepareFullDomainFolder "${3}" \
|
||||
&& single "http" "${3}" "${4}" \
|
||||
&& echo \
|
||||
&& echo "Checking configuration of nginx and restart the webserver:" \
|
||||
&& echo "==========================================================" \
|
||||
&& nginx -t && systemctl reload nginx \
|
||||
&& return 0
|
||||
;;
|
||||
--own)
|
||||
echo "[$(date)] Renewing own certificates via HTTP:" \
|
||||
&& own "http" "${3}" \
|
||||
&& echo \
|
||||
&& echo "Checking configuration of nginx and restart the webserver:" \
|
||||
&& echo "==========================================================" \
|
||||
&& nginx -t && systemctl reload nginx \
|
||||
&& return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
--prepare)
|
||||
case "${3}" in
|
||||
--usingAlias)
|
||||
echo "[$(date)] Prepare domain '${2}' using the alias-domain '${4}' via DNS:" \
|
||||
&& prepareFullDomainFolder "${2}" \
|
||||
&& prepareAndCheckAliasDomain "${2}" "${4}" \
|
||||
&& return 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command '${1}' '${2}' '${3}' '${4}'"
|
||||
usage
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
--setup)
|
||||
setup \
|
||||
&& return 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown command '${1}' '${2}'"
|
||||
usage
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
main "$@" && exit 0 || exit 1
|
||||
@@ -1,161 +0,0 @@
|
||||
#/bin/bash
|
||||
|
||||
function createEnvironmentFile() {
|
||||
local _ENVIRONMENT_FILE _REPOSITORY_FOLDER
|
||||
_ENVIRONMENT_FILE="${ENVIRONMENT_FILE:?"createEnvironmentFile(): Missing global parameter ENVIRONMENT_FILE"}"
|
||||
_REPOSITORY_FOLDER="${AUTOACME_REPOSITORY_FOLDER:?"createEnvironmentFile(): Missing global parameter AUTOACME_REPOSITORY_FOLDER"}"
|
||||
readonly _ENVIRONMENT_FILE _REPOSITORY_FOLDER
|
||||
|
||||
# Save environment for cronjob
|
||||
export -p | grep -v -E "(HOME|OLDPWD|PWD|SHLVL)" > "${_ENVIRONMENT_FILE}" \
|
||||
&& echo "SUCCESS: there values were exported into file: '${_ENVIRONMENT_FILE}'" \
|
||||
&& echo " - AUTOACME_CONTAINER_HOSTNAME: ${AUTOACME_CONTAINER_HOSTNAME}" \
|
||||
&& echo " - AUTOACME_DNS_PROVIDER: ${AUTOACME_DNS_PROVIDER}" \
|
||||
&& echo " - AUTOACME_GIT_REPOSITORY_VIA_SSH: ${AUTOACME_GIT_REPOSITORY_VIA_SSH}" \
|
||||
&& echo " - AUTOACME_PATH_IN_GIT_REPOSITORY: ${AUTOACME_PATH_IN_GIT_REPOSITORY}"
|
||||
|
||||
[ "${AUTOACME_GIT_REPOSITORY_VIA_SSH}" == "" ] \
|
||||
&& echo "declare -x AUTOACME_RESULT_CERTS=\"${AUTOACME_REPOSITORY_FOLDER#/}\"" >> "${_ENVIRONMENT_FILE}" \
|
||||
&& echo "SUCCESS: added AUTOACME_RESULT_CERTS (without git) into file '${_ENVIRONMENT_FILE}'." \
|
||||
&& echo " - AUTOACME_RESULT_CERTS: ${AUTOACME_REPOSITORY_FOLDER#/}" \
|
||||
&& echo " (depends on if there is a git repo and the path for the certs in it)"
|
||||
|
||||
! [ "${AUTOACME_GIT_REPOSITORY_VIA_SSH}" == "" ] \
|
||||
&& echo "declare -x AUTOACME_RESULT_CERTS=\"${AUTOACME_REPOSITORY_FOLDER}${AUTOACME_PATH_IN_GIT_REPOSITORY#/}\"" >> "${_ENVIRONMENT_FILE}" \
|
||||
&& echo "SUCCESS: added AUTOACME_RESULT_CERTS (with git) into file '${_ENVIRONMENT_FILE}'." \
|
||||
&& echo " - AUTOACME_RESULT_CERTS: ${AUTOACME_REPOSITORY_FOLDER}${AUTOACME_PATH_IN_GIT_REPOSITORY#/}" \
|
||||
&& echo " (depends on if there is a git repo and the path for the certs in it)"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function ensureThereAreSSHKeys() {
|
||||
grep -F 'ssh' "/root/.ssh/id_ed25519.pub" &> /dev/null \
|
||||
&& echo "SUCCESS: ssh-keys found, printing public key:" \
|
||||
&& cat "/root/.ssh/id_ed25519.pub" \
|
||||
&& return 0
|
||||
|
||||
# -t type of the key pair
|
||||
# -f defines the filenames (we use the standard for the selected type here)
|
||||
# -q quiet, no output or interaction
|
||||
# -N "" means the private key will not be secured by a passphrase
|
||||
# -C defines a comment
|
||||
ssh-keygen \
|
||||
-t ed25519 \
|
||||
-f "/root/.ssh/id_ed25519" -q -N "" \
|
||||
-C "$(date +%Y%m%d)-root@$(hostname -s)_onHost_${AUTOACME_CONTAINER_HOSTNAME%%.*}"
|
||||
|
||||
grep -F 'ssh' "/root/.ssh/id_ed25519.pub" &> /dev/null \
|
||||
&& echo "SUCCESS: ssh-keys generated, printing public key:" \
|
||||
&& cat "/root/.ssh/id_ed25519.pub" \
|
||||
&& return 0
|
||||
|
||||
echo
|
||||
echo "FAILED: something went wrong during the generation of the ssh keys..."
|
||||
echo " These keys are mandantory to access the git repository."
|
||||
echo "You can try to restart this script."
|
||||
echo
|
||||
return 1
|
||||
}
|
||||
|
||||
function ensureGitIsInstalled() {
|
||||
git --version &> /dev/null \
|
||||
&& return 0
|
||||
|
||||
echo \
|
||||
&& echo "Installing Git in 30s (ensure the SSH-Key is trusted and has write pemissions)... " \
|
||||
&& sleep 30 \
|
||||
&& DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get install git -y &> /dev/null \
|
||||
&& echo "SUCCESS: $(git --version) is usable now." \
|
||||
&& return 0
|
||||
|
||||
echo
|
||||
echo "FAILED: something went wrong during the installation of Git..."
|
||||
echo " Git is mandantory to push the keys into the specified repository."
|
||||
echo "You can try to install git manually (apt install git)."
|
||||
echo
|
||||
return 1
|
||||
}
|
||||
|
||||
function ensureRepositoryIsAvailableAndWritable() {
|
||||
local _REPOSITORY_FOLDER
|
||||
_REPOSITORY_FOLDER="${AUTOACME_REPOSITORY_FOLDER:?"ensureRepositoryIsAvailableAndWritable(): Missing global parameter AUTOACME_REPOSITORY_FOLDER"}"
|
||||
readonly _REPOSITORY_FOLDER
|
||||
|
||||
[ -d "${_REPOSITORY_FOLDER}.git" ] \
|
||||
&& echo \
|
||||
&& git -C "${_REPOSITORY_FOLDER}" pull &> /dev/null \
|
||||
&& git -C "${_REPOSITORY_FOLDER}" push --dry-run &> /dev/null \
|
||||
&& echo "Writable repository found in folder '${_REPOSITORY_FOLDER}'." \
|
||||
&& return 0
|
||||
|
||||
! [ -d "${_REPOSITORY_FOLDER}.git" ] \
|
||||
&& echo \
|
||||
&& echo "Cloning repository '${AUTOACME_GIT_REPOSITORY_VIA_SSH}'... " \
|
||||
&& GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" git clone "${AUTOACME_GIT_REPOSITORY_VIA_SSH}" "${_REPOSITORY_FOLDER}" &> /dev/null \
|
||||
&& git -C "${_REPOSITORY_FOLDER}" config user.name "autoacme on ${AUTOACME_CONTAINER_HOSTNAME%%.*}" \
|
||||
&& git -C "${_REPOSITORY_FOLDER}" config user.email "autoacme@${AUTOACME_CONTAINER_HOSTNAME%%.*}" \
|
||||
&& git -C "${_REPOSITORY_FOLDER}" push --dry-run &> /dev/null \
|
||||
&& echo "SUCCESS: repository cloned into folder '${_REPOSITORY_FOLDER}' and it is writable." \
|
||||
&& return 0
|
||||
|
||||
echo
|
||||
echo "FAILED: something went wrong during cloning the repository to '${_REPOSITORY_FOLDER}' from:"
|
||||
echo " - ${AUTOACME_GIT_REPOSITORY_VIA_SSH}"
|
||||
echo
|
||||
echo "1.) You can try to clone it manually into: git clone ${AUTOACME_GIT_REPOSITORY_VIA_SSH} '${_REPOSITORY_FOLDER}'"
|
||||
echo "2.) Check if the repositoty is writable: git -C '${_REPOSITORY_FOLDER}' push --dry-run"
|
||||
return 1
|
||||
}
|
||||
|
||||
function prepareThisRuntimeForUsingGitOrIgnore() {
|
||||
createEnvironmentFile \
|
||||
|| return 1
|
||||
|
||||
[ "${AUTOACME_GIT_REPOSITORY_VIA_SSH}" == "" ] \
|
||||
&& echo "There is no git repository specified." \
|
||||
&& echo "To distribute all keys and certificates via a git repository set environment variable:" \
|
||||
&& echo " - AUTOACME_GIT_REPOSITORY_VIA_SSH" \
|
||||
&& echo \
|
||||
&& echo "FIRST AND ONLY WARNING: DO NOT USE ANY PUBLIC GIT SERVICE FOR THAT!" \
|
||||
&& echo \
|
||||
&& return 0
|
||||
|
||||
echo \
|
||||
&& ensureThereAreSSHKeys \
|
||||
&& ensureGitIsInstalled \
|
||||
&& ensureRepositoryIsAvailableAndWritable \
|
||||
&& return 0
|
||||
|
||||
echo "No job will run inside this container because there is an issue."
|
||||
echo "The container keeps running for 10min, please check your setup..."
|
||||
return 1
|
||||
}
|
||||
|
||||
AUTOACME_REPOSITORY_FOLDER="/root/acmeResults/"
|
||||
ENVIRONMENT_FILE="/autoACME.env"
|
||||
|
||||
echo
|
||||
echo '################################################################################'
|
||||
echo "# Container started at $(date +%F_%T) on host ${AUTOACME_CONTAINER_HOSTNAME}"
|
||||
echo '################################################################################'
|
||||
echo
|
||||
|
||||
# Log start and truncate file: /autoACME.log
|
||||
echo > /autoACME.log
|
||||
|
||||
# Generate SSH keys and setup Git if a repository is specified, on failure keep the container running
|
||||
prepareThisRuntimeForUsingGitOrIgnore \
|
||||
|| timeout --preserve-status 10m tail -f /autoACME.log
|
||||
|
||||
# Ensure acme.sh ist installed
|
||||
/renewCerts.sh --setup >> /autoACME.log \
|
||||
&& echo >> /autoACME.log
|
||||
|
||||
echo "Register following entry to crontab:" >> /autoACME.log
|
||||
echo "------------------------------------" >> /autoACME.log
|
||||
_CRON_ENTRY="$((RANDOM % 59)) $((RANDOM % 5)) * * * /renewCerts.sh --dns --own >> /autoACME.log 2>&1"
|
||||
echo "${_CRON_ENTRY}" | tee -a /autoACME.log | crontab -
|
||||
|
||||
cron && tail -n 100 -f /autoACME.log
|
||||
Reference in New Issue
Block a user