mirror of
https://github.com/m8tin/cis.git
synced 2025-12-06 07:48:26 +01:00
Compare commits
47 Commits
master
...
d0eb35441f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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.
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#/bin/bash
|
|
||||||
|
|
||||||
docker network inspect $(docker network ls | grep -F 'bridge' | cut -d' ' -f1) \
|
|
||||||
| jq -r '.[] | .Name + " " + .IPAM.Config[0].Subnet' -
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#/bin/bash
|
|
||||||
|
|
||||||
_COMPOSITION_FILE="${1:-./docker-compose.yml}"
|
|
||||||
|
|
||||||
[ -d "${_COMPOSITION_FILE}" ] \
|
|
||||||
&& echo "A valid composition file ('docker-compose.yml') is needed. Given parameter was: ${_COMPOSITION_FILE}" >&2 \
|
|
||||||
&& exit 1
|
|
||||||
|
|
||||||
_DOCKER_COMPOSE_CMD=""
|
|
||||||
|
|
||||||
[ "${_DOCKER_COMPOSE_CMD}" = "" ] \
|
|
||||||
&& docker compose version 2> /dev/null | grep -q version \
|
|
||||||
&& _DOCKER_COMPOSE_CMD="docker compose"
|
|
||||||
|
|
||||||
[ "${_DOCKER_COMPOSE_CMD}" = "" ] \
|
|
||||||
&& docker-compose version 2> /dev/null | grep -q version \
|
|
||||||
&& _DOCKER_COMPOSE_CMD="docker-compose"
|
|
||||||
|
|
||||||
[ "${_DOCKER_COMPOSE_CMD}" = "" ] \
|
|
||||||
&& echo "Command 'docker compose' not found" >&2 \
|
|
||||||
&& exit 1
|
|
||||||
|
|
||||||
${_DOCKER_COMPOSE_CMD} -f "${_COMPOSITION_FILE}" images | tail -n +2 | cut -d' ' -f1
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Select just lines containing 'managedHost'.
|
|
||||||
# 1.) Remove everything after a '#' (including the #).
|
|
||||||
# 2.) Remove every indenting.
|
|
||||||
# 3.) Remove blanks (spaces or tabs) at the end of lines.
|
|
||||||
# 4.) Replace blanks (spaces or tabs) with one ';' between the values.
|
|
||||||
# 5.) Delete empty lines.
|
|
||||||
# Then cut the second field
|
|
||||||
# Then cut the first field to get the short hostname
|
|
||||||
grep 'managedHost' /etc/hosts \
|
|
||||||
| sed -e 's/#.*//' \
|
|
||||||
-e 's/^[[:blank:]]*//' \
|
|
||||||
-e 's/[[:blank:]]*$//' \
|
|
||||||
-e 's/\s\+/;/g' \
|
|
||||||
-e '/^$/d' \
|
|
||||||
| cut -d';' -f2 \
|
|
||||||
| cut -d'.' -f1
|
|
||||||
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
cat /sys/class/net/e*/address \
|
|
||||||
| head -n 1
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
#grep -E '(:|^(127|169\.254|10|172\.(1(6|7|8|9)|2[0-9]|30|31)|192\.168|(22(4|5|6|7|8|9)|23(0|1|2|3|4|5|6|7|8|9))).*)' findet:
|
|
||||||
# loopback: 127.0.0.0/8
|
|
||||||
# linklocal: 169.254.0.0/16
|
|
||||||
# private: 10.0.0.0/8,
|
|
||||||
# 172.16.0.0/12, (172.16… bis 172.31…)
|
|
||||||
# 192.168.0.0/16
|
|
||||||
# multicast: 224.0.0.0/4 (224… bis 239…)
|
|
||||||
|
|
||||||
|
|
||||||
function all() {
|
|
||||||
# Select just lines containing 'inet'.
|
|
||||||
# 1.) Remove every indenting.
|
|
||||||
# 2.) Remove 'inet '.
|
|
||||||
# 3.) Remove everything after a '/' (including the /).
|
|
||||||
ip -4 addr \
|
|
||||||
| grep 'inet' \
|
|
||||||
| sed -e 's/^[[:blank:]]*//' \
|
|
||||||
-e 's/inet //' \
|
|
||||||
-e 's/\/.*//'
|
|
||||||
}
|
|
||||||
|
|
||||||
function routed() {
|
|
||||||
local _DEVICE
|
|
||||||
_DEVICE="$(ip -4 route show default | xargs -n 1 | grep -A1 -i dev | tail -n 1)"
|
|
||||||
readonly _DEVICE
|
|
||||||
|
|
||||||
ip -4 addr show dev "${_DEVICE:?"Missing DEVICE"}" scope global \
|
|
||||||
| grep 'inet' | xargs -n 1 \
|
|
||||||
| grep -A1 'inet' \
|
|
||||||
| tail -n 1 \
|
|
||||||
| cut -d/ -f1
|
|
||||||
}
|
|
||||||
|
|
||||||
function public() {
|
|
||||||
hostname -I | xargs -n 1 \
|
|
||||||
| grep -vE '(:|^(127|169\.254|10|172\.(1(6|7|8|9)|2[0-9]|30|31)|192\.168|(22(4|5|6|7|8|9)|23(0|1|2|3|4|5|6|7|8|9))).*)'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Maybe use "resolvectl status" to get DNS Server and specify 'nslookup'
|
|
||||||
function published() {
|
|
||||||
local _BOOT_HOSTNAME
|
|
||||||
_BOOT_HOSTNAME="$(hostname -b)"
|
|
||||||
readonly _BOOT_HOSTNAME
|
|
||||||
|
|
||||||
nslookup -type=A "${_BOOT_HOSTNAME:?"Missing BOOT_HOSTNAME"}" | xargs -n 1 \
|
|
||||||
| grep -A2 -i "${_BOOT_HOSTNAME}" \
|
|
||||||
| grep -A1 -i 'address' \
|
|
||||||
| tail -n1
|
|
||||||
}
|
|
||||||
|
|
||||||
function verified() {
|
|
||||||
local _PUBLISHED_IP
|
|
||||||
_PUBLISHED_IP="$(published)"
|
|
||||||
readonly _PUBLISHED_IP
|
|
||||||
|
|
||||||
[ -z "${_PUBLISHED_IP}" ] \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
all | grep "${_PUBLISHED_IP}"
|
|
||||||
}
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
echo "Use one of the following options:"
|
|
||||||
echo " --all : prints all IPv4 addresses"
|
|
||||||
echo " --routed : prints the IPv4 address used to send traffic to the default gateway"
|
|
||||||
echo " --public : prints all IPv4 addresses direct accessable from the internet"
|
|
||||||
echo " --published : prints the IPv4 address provided by DNS using this host's name"
|
|
||||||
echo " --verified : prints the IPv4 included in 'all' und respended by 'published'"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function main(){
|
|
||||||
|
|
||||||
case "${1}" in
|
|
||||||
--all)
|
|
||||||
all
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--routed)
|
|
||||||
routed
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--public)
|
|
||||||
public
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--published)
|
|
||||||
published
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--verified)
|
|
||||||
verified
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
usage
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
return 1
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@" && exit 0 || exit 1
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
#grep -E '(^::1|(^fc.*|^fd.*)|^fe80::.*|^ff.*)' findet:
|
|
||||||
# loopback: ::1/128
|
|
||||||
# uniquelocal: fc00::/7 (fc00… bis fdff…)
|
|
||||||
# linklocal: fe80::/64
|
|
||||||
# multicast: ff00::/8 (ff…)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function all() {
|
|
||||||
# Select just lines containing 'inet6'.
|
|
||||||
# 1.) Remove every indenting.
|
|
||||||
# 2.) Remove 'inet6 '.
|
|
||||||
# 3.) Remove everything after a '/' (including the /).
|
|
||||||
ip -6 addr \
|
|
||||||
| grep 'inet6' \
|
|
||||||
| sed -e 's/^[[:blank:]]*//' \
|
|
||||||
-e 's/inet6 //' \
|
|
||||||
-e 's/\/.*//'
|
|
||||||
}
|
|
||||||
|
|
||||||
function routed() {
|
|
||||||
local _DEVICE
|
|
||||||
_DEVICE="$(ip -6 route show default | xargs -n 1 | grep -A1 -i dev | tail -n 1)"
|
|
||||||
readonly _DEVICE
|
|
||||||
|
|
||||||
ip -6 addr show dev "${_DEVICE:?"Missing DEVICE"}" scope global \
|
|
||||||
| grep 'inet6' \
|
|
||||||
| xargs -n 1 \
|
|
||||||
| grep -A1 'inet6' \
|
|
||||||
| grep ':' \
|
|
||||||
| cut -d/ -f1
|
|
||||||
}
|
|
||||||
|
|
||||||
function public() {
|
|
||||||
hostname -I | xargs -n 1 \
|
|
||||||
| grep ':' \
|
|
||||||
| grep -vE '(^::1|(^fc.*|^fd.*)|^fe80::.*|^ff.*)'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Maybe use "resolvectl status" to get DNS Server and specify 'nslookup'
|
|
||||||
function published() {
|
|
||||||
local _BOOT_HOSTNAME
|
|
||||||
_BOOT_HOSTNAME="$(hostname -b)"
|
|
||||||
readonly _BOOT_HOSTNAME
|
|
||||||
|
|
||||||
nslookup -type=AAAA "${_BOOT_HOSTNAME:?"Missing BOOT_HOSTNAME"}" | xargs -n 1 \
|
|
||||||
| grep -A2 -i "${_BOOT_HOSTNAME}" \
|
|
||||||
| grep -A1 -i address \
|
|
||||||
| tail -n1
|
|
||||||
}
|
|
||||||
|
|
||||||
function verified() {
|
|
||||||
local _PUBLISHED_IP
|
|
||||||
_PUBLISHED_IP="$(published)"
|
|
||||||
readonly _PUBLISHED_IP
|
|
||||||
|
|
||||||
[ -z "${_PUBLISHED_IP}" ] \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
all | grep "${_PUBLISHED_IP}"
|
|
||||||
}
|
|
||||||
|
|
||||||
function usage() {
|
|
||||||
echo "Use one of the following options:"
|
|
||||||
echo " --all : prints all IPv6 addresses"
|
|
||||||
echo " --routed : prints the IPv6 address used to send traffic to the default gateway"
|
|
||||||
echo " --public : prints all IPv6 addresses direct accessable from the internet"
|
|
||||||
echo " --published : prints the IPv6 address provided by DNS using this host's name"
|
|
||||||
echo " --verified : prints the IPv6 included in 'all' und respended by 'published'"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function main(){
|
|
||||||
|
|
||||||
case "${1}" in
|
|
||||||
--all)
|
|
||||||
all
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--routed)
|
|
||||||
routed
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--public)
|
|
||||||
public
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--published)
|
|
||||||
published
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
--verified)
|
|
||||||
verified
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
usage
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
return 1
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@" && exit 0 || exit 1
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
cat /sys/class/net/e*/address
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Select just lines containing 'inet'.
|
|
||||||
# 1.) Remove every indenting.
|
|
||||||
# 2.) Remove 'inet '.
|
|
||||||
# 3.) Remove everything after a '/' (including the /).
|
|
||||||
# Search each IP of the IPv4-list in file '/etc/hosts'
|
|
||||||
# Select just lines containing 'managedHost'.
|
|
||||||
# 1.) Remove everything after a '#' (including the #).
|
|
||||||
# 2.) Remove every indenting.
|
|
||||||
# 3.) Remove blanks (spaces or tabs) at the end of lines.
|
|
||||||
# 4.) Replace blanks (spaces or tabs) with one ';' between the values.
|
|
||||||
# 5.) Delete empty lines.
|
|
||||||
# Then cut the second field
|
|
||||||
# Then cut the first field to get the short hostname
|
|
||||||
ip -4 addr \
|
|
||||||
| grep 'inet' \
|
|
||||||
| sed -e 's/^[[:blank:]]*//' \
|
|
||||||
-e 's/inet //' \
|
|
||||||
-e 's/\/.*//' \
|
|
||||||
| xargs -i grep {} /etc/hosts \
|
|
||||||
| grep 'managedHost' \
|
|
||||||
| sed -e 's/#.*//' \
|
|
||||||
-e 's/^[[:blank:]]*//' \
|
|
||||||
-e 's/[[:blank:]]*$//' \
|
|
||||||
-e 's/\s\+/;/g' \
|
|
||||||
-e '/^$/d' \
|
|
||||||
| cut -d';' -f2 \
|
|
||||||
| cut -d'.' -f1
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
nginx -t &> /dev/null \
|
|
||||||
&& systemctl restart nginx.service \
|
|
||||||
&& exit 0
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function main() {
|
|
||||||
local _SCRIPTPATH _DH_PATH _SELF_SIGNED_PATH
|
|
||||||
_SCRIPTPATH="$(cd -- "$(dirname "$0")" > /dev/null 2>&1; pwd -P)"
|
|
||||||
_DH_PATH="/etc/ssl/private"
|
|
||||||
_SELF_SIGNED_PATH="/etc/ssl/private"
|
|
||||||
readonly _SCRIPTPATH _DH_PATH _SELF_SIGNED_PATH
|
|
||||||
|
|
||||||
! dpkg -s nginx > /dev/null 2>&1 \
|
|
||||||
&& apt-get --yes install nginx-full \
|
|
||||||
&& echo "Nginx erfolgreich installiert." \
|
|
||||||
|| echo "Nginx ist bereits installiert."
|
|
||||||
|
|
||||||
! dpkg -s openssl > /dev/null 2>&1 \
|
|
||||||
&& apt-get --yes install openssl \
|
|
||||||
&& echo "OpenSSL erfolgreich installiert." \
|
|
||||||
|| echo "OpenSSL ist bereits installiert."
|
|
||||||
|
|
||||||
! [ -f "${_DH_PATH}/dhparam4096.pem" ] \
|
|
||||||
&& mkdir -p "${_DH_PATH}" \
|
|
||||||
&& chmod go-rwx "${_DH_PATH}" \
|
|
||||||
&& openssl dhparam -out "${_DH_PATH}/dhparam4096.pem" 4096 \
|
|
||||||
&& echo "Diffie-Hellman-Parameters erfolgreich erstellt." \
|
|
||||||
|| echo "Diffie-Hellman-Parameters bereits vorhanden."
|
|
||||||
|
|
||||||
! [ -f "${_SELF_SIGNED_PATH}/selfsigned-private.key" ] \
|
|
||||||
&& mkdir -p "${_SELF_SIGNED_PATH}" \
|
|
||||||
&& chmod go-rwx "${_SELF_SIGNED_PATH}" \
|
|
||||||
&& openssl req -x509 -days 36524 -nodes -newkey rsa:4096 \
|
|
||||||
-keyout "${_SELF_SIGNED_PATH}/selfsigned-private.key" \
|
|
||||||
-out "${_SELF_SIGNED_PATH}/selfsigned-fullchain.crt" \
|
|
||||||
&& echo "Selbstsignierte Standardschlüssel erfolgreich erstellt." \
|
|
||||||
|| echo "Selbstsignierte Standardschlüssel bereits vorhanden."
|
|
||||||
|
|
||||||
#TODO Links erstellen
|
|
||||||
# [ -d "/etc/nginx/" ] \
|
|
||||||
# && cp "${_SCRIPTPATH}/etc_nginx_conf.d/"* "/etc/nginx/conf.d/" \
|
|
||||||
# && mkdir -p /etc/nginx/ssl-trusted \
|
|
||||||
# && cp "${_SCRIPTPATH}/etc_nginx_ssl-trusted/"* "/etc/nginx/ssl-trusted/" \
|
|
||||||
# && mkdir -p /var/www/letsencrypt/.well-known/acme-challenge \
|
|
||||||
# && echo "Basis-Konfiguration erfolgreich erstellt." \
|
|
||||||
# || echo "Basis-Konfiguration bereits vorhanden."
|
|
||||||
|
|
||||||
echo \
|
|
||||||
&& echo "Nginx neu starten:" \
|
|
||||||
&& nginx -t \
|
|
||||||
&& systemctl restart nginx.service \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@" && exit 0 || exit 1
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
sudo usermod --append --groups sudo "${1:?"Missing first parameter USER"}"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
sudo usermod --remove --groups sudo "${1:?"Missing first parameter USER"}"
|
|
||||||
@@ -1,33 +1,126 @@
|
|||||||
|
|
||||||
Monitoring - How it works
|
How to setup a monitoring dashboard
|
||||||
=========================
|
===================================
|
||||||
|
|
||||||
Basics
|
Inspired by: https://pimylifeup.com/ubuntu-chromium-kiosk/
|
||||||
------
|
|
||||||
|
|
||||||
You have to set up the monitoring host first. That host will monitor your other machines.
|
Steps
|
||||||
Execute `/cis/script/monitor/setupMonitoringHost.sh` to start the process.
|
-----
|
||||||
|
|
||||||
As usual you can configure this feature via definitions.
|
|
||||||
```
|
|
||||||
# Path of this feature's scripts : '/cis/script /monitor'
|
### 1.) Install Ubuntu Server (no desktop) on your computer than set hostname and timezone.
|
||||||
# Path of the corresponding definitions: '/cis/definitions/YOUR.DOMAIN/monitor'
|
|
||||||
ls -lha '/cis/script/monitor'
|
```sh
|
||||||
ls -lha '/cis/definitions/YOUR.DOMAIN/monitor'
|
hostnamectl set-hostname check.local
|
||||||
|
timedatectl set-timezone Europe/Berlin
|
||||||
```
|
```
|
||||||
|
|
||||||
You can modify the appearance and place your own `check.css` or `logo.png` into the definitions folder:
|
|
||||||
|
|
||||||
- /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,
|
### 2.) Install minimal GUI and Tools.
|
||||||
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.
|
```sh
|
||||||
|
apt install ubuntu-desktop-minimal
|
||||||
|
apt install language-pack-gnome-de
|
||||||
|
apt install xdotool
|
||||||
|
apt install dbus-x11
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Dashboard
|
### 3.) Create a kiosk user with home-directory.
|
||||||
---------
|
|
||||||
|
|
||||||
You can set up an dashboard following this manual [SETUP_DASHBOARD.md](SETUP_DASHBOARD.md)
|
```sh
|
||||||
|
useradd -m kiosk
|
||||||
|
```
|
||||||
|
|
||||||
|
and disable Welocme-Screen
|
||||||
|
```sh
|
||||||
|
echo "yes" > /home/kiosk/.config/gnome-initial-setup-done
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 4.) Edit following file `nano /etc/gdm3/custom.conf` to turn of wayland and turn on autologin for user 'kiosk'.
|
||||||
|
|
||||||
|
```
|
||||||
|
[daemon]
|
||||||
|
# Uncomment the line below to force the login screen to use Xorg
|
||||||
|
#WaylandEnable=false
|
||||||
|
|
||||||
|
WaylandEnable=false
|
||||||
|
|
||||||
|
# Enabling automatic login
|
||||||
|
# AutomaticLoginEnable = true
|
||||||
|
# AutomaticLogin = user1
|
||||||
|
|
||||||
|
AutomaticLoginEnable = true
|
||||||
|
AutomaticLogin = kiosk
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 5.) Configure GUI of user kiosk to prevent monitor from sleeping
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#gsettings list-recursively
|
||||||
|
|
||||||
|
# Does not work
|
||||||
|
#sudo -u kiosk gsettings set org.gnome.desktop.session idle-delay 0
|
||||||
|
|
||||||
|
# Set idle-delay from "uint32 300" to "uint32 0", needs 'apt install dbus-x11'
|
||||||
|
# You can check the value in "GUI-Session of kiosk -> Settings -> Power"
|
||||||
|
sudo -u kiosk dbus-launch dconf write /org/gnome/desktop/session/idle-delay "uint32 0"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 6.) Create custom service to start firefox loading the page.
|
||||||
|
|
||||||
|
Therefore create a file `/etc/systemd/system/kiosk.service` with this content:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Unit]
|
||||||
|
Description=Firefox Kiosk
|
||||||
|
Wants=graphical.target
|
||||||
|
After=graphical.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Environment=DISPLAY=:0
|
||||||
|
# Set firefox language, needs 'apt install language-pack-gnome-de'
|
||||||
|
Environment=LANG=de_DE.UTF-8
|
||||||
|
Type=simple
|
||||||
|
# Always a fresh firefox ('-' allow error if common does not exist)
|
||||||
|
ExecStartPre=-/usr/bin/rm -r /home/kiosk/snap/firefox/common
|
||||||
|
# Move Mouse (should also work on small screens), needs 'apt install dbus-x11'
|
||||||
|
ExecStartPre=/usr/bin/xdotool mousemove 4096 2160
|
||||||
|
# See: https://wiki.mozilla.org/Firefox/CommandLineOptions (just -kiosk URL => Start-Assistant, so use -url too)
|
||||||
|
ExecStart=/usr/bin/firefox -fullscreen -kiosk -url http://monitor.example.net/check.html
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30
|
||||||
|
User=kiosk
|
||||||
|
Group=kiosk
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=graphical.target
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 7.) Enable the service and reboot
|
||||||
|
|
||||||
|
```sh
|
||||||
|
systemctl enable kiosk
|
||||||
|
reboot
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Troubleshouting
|
||||||
|
---------------
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl disable pd-mapper.service
|
||||||
|
apt purge cloud-init -y && apt autoremove --purge -y
|
||||||
|
```
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
|
|
||||||
How to setup a monitoring dashboard
|
|
||||||
===================================
|
|
||||||
|
|
||||||
Inspired by: https://pimylifeup.com/ubuntu-chromium-kiosk/
|
|
||||||
|
|
||||||
Steps
|
|
||||||
-----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 1.) Install Ubuntu Server (no desktop) on your computer than set hostname and timezone.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
hostnamectl set-hostname check.local
|
|
||||||
timedatectl set-timezone Europe/Berlin
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 2.) Install minimal GUI and Tools.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
apt install ubuntu-desktop-minimal
|
|
||||||
apt install language-pack-gnome-de
|
|
||||||
apt install xdotool
|
|
||||||
apt install dbus-x11
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 3.) Create a kiosk user with home-directory.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
useradd -m kiosk
|
|
||||||
```
|
|
||||||
|
|
||||||
and disable Welocme-Screen
|
|
||||||
```sh
|
|
||||||
echo "yes" > /home/kiosk/.config/gnome-initial-setup-done
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 4.) Edit following file `nano /etc/gdm3/custom.conf` to turn of wayland and turn on autologin for user 'kiosk'.
|
|
||||||
|
|
||||||
```
|
|
||||||
[daemon]
|
|
||||||
# Uncomment the line below to force the login screen to use Xorg
|
|
||||||
#WaylandEnable=false
|
|
||||||
|
|
||||||
WaylandEnable=false
|
|
||||||
|
|
||||||
# Enabling automatic login
|
|
||||||
# AutomaticLoginEnable = true
|
|
||||||
# AutomaticLogin = user1
|
|
||||||
|
|
||||||
AutomaticLoginEnable = true
|
|
||||||
AutomaticLogin = kiosk
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 5.) Configure GUI of user kiosk to prevent monitor from sleeping
|
|
||||||
|
|
||||||
```sh
|
|
||||||
#gsettings list-recursively
|
|
||||||
|
|
||||||
# Does not work
|
|
||||||
#sudo -u kiosk gsettings set org.gnome.desktop.session idle-delay 0
|
|
||||||
|
|
||||||
# Set idle-delay from "uint32 300" to "uint32 0", needs 'apt install dbus-x11'
|
|
||||||
# You can check the value in "GUI-Session of kiosk -> Settings -> Power"
|
|
||||||
sudo -u kiosk dbus-launch dconf write /org/gnome/desktop/session/idle-delay "uint32 0"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 6.) Create custom service to start firefox loading the page.
|
|
||||||
|
|
||||||
Therefore create a file `/etc/systemd/system/kiosk.service` with this content:
|
|
||||||
|
|
||||||
```
|
|
||||||
[Unit]
|
|
||||||
Description=Firefox Kiosk
|
|
||||||
Wants=graphical.target
|
|
||||||
After=graphical.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Environment=DISPLAY=:0
|
|
||||||
# Set firefox language, needs 'apt install language-pack-gnome-de'
|
|
||||||
Environment=LANG=de_DE.UTF-8
|
|
||||||
Type=simple
|
|
||||||
# Always a fresh firefox ('-' allow error if common does not exist)
|
|
||||||
ExecStartPre=-/usr/bin/rm -r /home/kiosk/snap/firefox/common
|
|
||||||
# Move Mouse (should also work on small screens), needs 'apt install dbus-x11'
|
|
||||||
ExecStartPre=/usr/bin/xdotool mousemove 4096 2160
|
|
||||||
# See: https://wiki.mozilla.org/Firefox/CommandLineOptions (just -kiosk URL => Start-Assistant, so use -url too)
|
|
||||||
ExecStart=/usr/bin/firefox -fullscreen -kiosk -url http://monitor.example.net/check.html
|
|
||||||
Restart=always
|
|
||||||
RestartSec=30
|
|
||||||
User=kiosk
|
|
||||||
Group=kiosk
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=graphical.target
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 7.) Enable the service and reboot
|
|
||||||
|
|
||||||
```sh
|
|
||||||
systemctl enable kiosk
|
|
||||||
reboot
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Troubleshouting
|
|
||||||
---------------
|
|
||||||
|
|
||||||
```
|
|
||||||
systemctl disable pd-mapper.service
|
|
||||||
apt purge cloud-init -y && apt autoremove --purge -y
|
|
||||||
```
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
_CHECK="$(readlink -f "${0}" 2> /dev/null)"
|
|
||||||
|
|
||||||
# Folders always ends with an tailing '/'
|
|
||||||
_CIS_ROOT="${_CHECK%%/definitions/*}/" #Removes longest matching pattern '/definitions/*' from the end
|
|
||||||
_GENERIC_CHECKS="${_CIS_ROOT:?"Missing CIS_ROOT"}script/monitor/generic/"
|
|
||||||
|
|
||||||
${_GENERIC_CHECKS:?"Missing GENERIC_CHECKS"}OVERRIDDEN_DOMAIN_CHECK.sh "your-host.your-domain.net"
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
_REMOTE_HOST="${1:?"FQDN of server missing: e.g. host.example.net[:port]"}"
|
|
||||||
_REMOTE_HOSTNAME_FQDN="${_REMOTE_HOST%%:*}" #Removes longest matching pattern ':*' from the end
|
|
||||||
_REMOTE_HOSTNAME_SHORT="${_REMOTE_HOSTNAME_FQDN%%.*}" #Removes longest matching pattern '.*' from the end
|
|
||||||
_REMOTE_PORT="${_REMOTE_HOST}:"
|
|
||||||
_REMOTE_PORT="${_REMOTE_PORT#*:}" #Removes shortest matching pattern '*:' from the begin
|
|
||||||
_REMOTE_PORT="${_REMOTE_PORT%%:*}" #Removes longest matching pattern ':*' from the end
|
|
||||||
_REMOTE_PORT="${_REMOTE_PORT:-"22"}"
|
|
||||||
_REMOTE_USER="monitoring"
|
|
||||||
_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' \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
|
||||||
ssh -o ControlMaster=auto \
|
|
||||||
-o ControlPath=${_SOCKET} \
|
|
||||||
-o ControlPersist=65 \
|
|
||||||
-p ${_REMOTE_PORT} \
|
|
||||||
-f ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} exit &> /dev/null \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
echo "FAIL#SSH connection (setup ok?)"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDomain(){
|
|
||||||
checkOrStartSSHMaster \
|
|
||||||
|| return 1
|
|
||||||
|
|
||||||
local _RESULT="$(ssh -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} 'bash /cis/core/printOwnDomain.sh' 2>&1 1>/dev/null)"
|
|
||||||
|
|
||||||
[ -z "${_RESULT}" ] \
|
|
||||||
&& echo "OK" \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
echo "WARNING#Check hosts '/cis/core/printOwnDomain'"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
testDomain && exit 0
|
|
||||||
@@ -13,7 +13,8 @@ _SOCKET='~/.ssh/%r@%h:%p'
|
|||||||
|
|
||||||
|
|
||||||
function checkOrStartSSHMaster() {
|
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 1 "ssh -O check -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN}" &> /dev/null \
|
||||||
|
&& echo "master checked" \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
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
|
#!/bin/bash
|
||||||
|
|
||||||
|
_URL="${1:?"URL of site missing"}"
|
||||||
|
|
||||||
#curl:
|
#curl:
|
||||||
# --connect-timeout SECONDS Maximum time allowed for connection
|
# --connect-timeout SECONDS Maximum time allowed for connection
|
||||||
# -k Allow connections to SSL sites without certs (H)
|
# -k Allow connections to SSL sites without certs (H)
|
||||||
@@ -12,51 +14,4 @@
|
|||||||
#grep:
|
#grep:
|
||||||
# -q Quite, no output just status codes
|
# -q Quite, no output just status codes
|
||||||
# -F Interpret search term as plain text
|
# -F Interpret search term as plain text
|
||||||
function checkUrl() {
|
((curl --connect-timeout 10 --max-time 10 -k -s --head --no-progress-meter "${_URL}" | grep -qF '200 OK') && echo OK) || echo FAIL
|
||||||
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
|
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
_REMOTE_HOST="${1:?"FQDN of server missing: e.g. host.example.net[:port]"}"
|
|
||||||
_ZFS_POOL="${2:?"Name of zfs pool missing: e.g. zpool1"}"
|
|
||||||
_REMOTE_HOSTNAME_FQDN="${_REMOTE_HOST%%:*}" #Removes longest matching pattern ':*' from the end
|
|
||||||
_REMOTE_HOSTNAME_SHORT="${_REMOTE_HOSTNAME_FQDN%%.*}" #Removes longest matching pattern '.*' from the end
|
|
||||||
_REMOTE_PORT="${_REMOTE_HOST}:"
|
|
||||||
_REMOTE_PORT="${_REMOTE_PORT#*:}" #Removes shortest matching pattern '*:' from the begin
|
|
||||||
_REMOTE_PORT="${_REMOTE_PORT%%:*}" #Removes longest matching pattern ':*' from the end
|
|
||||||
_REMOTE_PORT="${_REMOTE_PORT:-"22"}"
|
|
||||||
_REMOTE_USER="monitoring"
|
|
||||||
_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' \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
|
||||||
ssh -o ControlMaster=auto \
|
|
||||||
-o ControlPath=${_SOCKET} \
|
|
||||||
-o ControlPersist=65 \
|
|
||||||
-p ${_REMOTE_PORT} \
|
|
||||||
-f ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} exit &> /dev/null \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
echo "FAIL#SSH connection (setup ok?)"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function testPool(){
|
|
||||||
checkOrStartSSHMaster \
|
|
||||||
|| return 1
|
|
||||||
|
|
||||||
local _RESPONSE="$(ssh -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} 'zpool status ${_ZFS_POOL} | grep -F scrub')"
|
|
||||||
local _RESULT=$(echo "${_RESPONSE}" | grep -F 'scrub repaired 0B' | grep -F '0 errors')
|
|
||||||
_RESULT="${_RESULT#*on}" #Removes shortest matching pattern '*on' from the begin
|
|
||||||
|
|
||||||
[ -z "${_RESULT}" ] \
|
|
||||||
&& echo "FAIL#CHECK POOL: ${_ZFS_POOL}" \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
echo "OK#Scrubbed on ${_RESULT}."
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
testPool && exit 0
|
|
||||||
|
|
||||||
exit 1
|
|
||||||
@@ -30,7 +30,8 @@ _DEBUG_PATH="/tmp/monitor/"
|
|||||||
|
|
||||||
|
|
||||||
function checkOrStartSSHMaster() {
|
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 1 "ssh -O check -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN}" &> /dev/null \
|
||||||
|
&& echo "master checked" \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ _SOCKET='~/.ssh/%r@%h:%p'
|
|||||||
|
|
||||||
|
|
||||||
function checkOrStartSSHMaster() {
|
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 1 "ssh -O check -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN}" &> /dev/null \
|
||||||
|
&& echo "master checked" \
|
||||||
&& return 0
|
&& return 0
|
||||||
|
|
||||||
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
ssh -O stop -S ${_SOCKET} -p ${_REMOTE_PORT} ${_REMOTE_USER}@${_REMOTE_HOSTNAME_FQDN} &> /dev/null
|
||||||
|
|||||||
@@ -15,20 +15,6 @@ _DEFINITIONS="${_CIS_ROOT:?"Missing CIS_ROOT"}definitions/${_DOMAIN:?"Missing DO
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
function checkPreconditions() {
|
|
||||||
[ -d "${_DEFINITIONS:?"Missing DEFINITIONS"}monitor/checks" ] \
|
|
||||||
&& return 0
|
|
||||||
|
|
||||||
echo "No folder for your defined checks found: ${_DEFINITIONS:?"Missing DEFINITIONS"}monitor/checks"
|
|
||||||
echo "Please create it and add all your custom monitoring checks there, following this convention: 'NAME_OF_THE_CHECK.on'"
|
|
||||||
echo "A check has to be switched 'on' to be executed, so you can rename a check to 'NAME_OF_THE_CHECK.off' and it will be ignored."
|
|
||||||
echo
|
|
||||||
echo "You can copy the file '/cis/script/monitor/checks/EXAMPLE_CHECK.off' to your check definitions folder and modify it."
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function printSelectedDefinition() {
|
function printSelectedDefinition() {
|
||||||
local _FILE_DEFINED_DOMAIN _FILE_DEFINED_DEFAULT
|
local _FILE_DEFINED_DOMAIN _FILE_DEFINED_DEFAULT
|
||||||
_FILE_DEFINED_DOMAIN="${_DEFINITIONS:?"Missing DEFINITIONS"}monitor/${1:?"Missing CURRENT_FULLFILE"}"
|
_FILE_DEFINED_DOMAIN="${_DEFINITIONS:?"Missing DEFINITIONS"}monitor/${1:?"Missing CURRENT_FULLFILE"}"
|
||||||
@@ -64,7 +50,6 @@ function setupPublicFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
echo "Setup the monitoring host that monitors the others ... " \
|
echo "Setup the monitoring host that monitors the others ... " \
|
||||||
&& checkPreconditions \
|
|
||||||
&& setupPublicFile "check.html" \
|
&& setupPublicFile "check.html" \
|
||||||
&& setupPublicFile "check.css" \
|
&& setupPublicFile "check.css" \
|
||||||
&& setupPublicFile "logo.png" \
|
&& setupPublicFile "logo.png" \
|
||||||
|
|||||||
@@ -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