feat: rework entire repo structure

This commit is contained in:
2026-02-28 21:31:18 +01:00
parent 98ae07ae88
commit fcbfd3a838
12 changed files with 214 additions and 254 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.env

33
Dockerfile Normal file
View File

@@ -0,0 +1,33 @@
# Utilise la même base que ton serveur Scaleway
FROM debian:12-slim
# Évite les erreurs 'TERM environment variable not set' lors du 'clear'
ENV TERM=xterm
# Empêche les interfaces interactives pendant l'installation des paquets
ENV DEBIAN_FRONTEND=noninteractive
# Installation des dépendances
# Note : on utilise docker.io pour avoir le binaire /usr/bin/docker
RUN apt-get update && apt-get install -y \
curl \
cron \
sudo \
procps \
docker.io \
docker-compose \
&& rm -rf /var/lib/apt/lists/*
# Création de l'arborescence de travail
WORKDIR /app
# On copie le contenu du projet
# Assure-toi d'être à la racine de 'serverconfig' quand tu buildes
COPY . .
# On rend les scripts exécutables
# Utilisation de find pour être sûr de ne rater aucun script dans les sous-dossiers
RUN find . -name "*.sh" -exec chmod +x {} +
# On lance le script d'installation
# Utilisation de la forme exec pour une meilleure gestion des signaux
CMD ["./install.sh"]

View File

@@ -1,32 +1,34 @@
#!/bin/bash #!/bin/bash
source /usr/local/bin/libs/common.sh # DISK MONITORING & INSTALLATION SCRIPT
source /etc/serverconfig/.env # This script monitors disk usage and sends notifications.
# It can also self-install into the system crontab.
PROJECT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
readonly PROJECT_DIR 2>/dev/null
source $PROJECT_DIR/utils.sh
source $ENV_FILE
INSTALLED=$1 INSTALLED=$1
if [[ "--install" == $INSTALLED ]]; then if [[ "--install" == $INSTALLED ]]; then
info_print "\n\ log_info "disk-monitor Installation"
==================================================\n\
disk-monitor Installation\n\
--------------------------------------------------"
if ! command -v crontab >/dev/null 2>&1; then if ! command -v crontab >/dev/null 2>&1; then
info_print "Error: crontab not found." $ERROR_FLAG log_error "Error: crontab not found."
exit 1 exit 1
fi fi
CRON_JOB="0 3 * * 1 $SCRIPT_FILE/scripts/disk-monitor.sh" CRON_JOB="0 3 * * 1 $PROJECT_DIR/disk-monitor.sh"
crontab -l | grep -F "$CRON_JOB" > /dev/null 2>&1 crontab -l | grep -F "$CRON_JOB" > /dev/null 2>&1
if ! crontab -l | grep -Fq "$CRON_JOB"; then if ! crontab -l | grep -Fq "$CRON_JOB"; then
(crontab -l 2>/dev/null; echo "$CRON_JOB") | crontab - (crontab -l 2>/dev/null; echo "$CRON_JOB") | crontab -
info_print "Cron job added." $SUCCESS_FLAG log_success "Cron job added."
fi fi
exit 0; exit 0;
fi fi
source /usr/local/bin/libs/notifications.sh
usage=80 usage=80
send_notification "$( send_notification "$(
df -h / | grep / | awk -v max="$usage" '{ df -h / | grep / | awk -v max="$usage" '{
usage = $5; usage = $5;

20
docker-compose.sh Normal file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
PROJECT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
readonly PROJECT_DIR 2>/dev/null
source $PROJECT_DIR/utils.sh
source $ENV_FILE
if [[ "--install" == $1 ]]; then
log_info "docker-compose Installation"
COMPOSE_FILE="$(realpath "$PROJECT_DIR/docker-compose.yml")"
if [[ -f "$COMPOSE_FILE" ]]; then
docker compose -f "$COMPOSE_FILE" up -d --force-recreate --remove-orphans && \
log_success "$COMPOSE_FILE is running.";
else log_error "no docker-compose.yml found at $PROJECT_DIR";
fi
exit 0;
fi

View File

@@ -69,9 +69,9 @@ services:
DOCKER_HOST: tcp://socket-rw:2375 # Uses RW proxy to pause containers during backup DOCKER_HOST: tcp://socket-rw:2375 # Uses RW proxy to pause containers during backup
volumes: volumes:
- wireguard-data:/backup/wireguard:ro - wireguard-data:/backup/wireguard:ro
- ./synapse:/backup/synapse:ro - ./data/synapse:/backup/synapse:ro
- ssl-data:/backup/ssl:ro - ssl-data:/backup/ssl:ro
- ./backup:/archive - ./data/backup:/archive
networks: networks:
- socket-rw-bridge - socket-rw-bridge
- web-network - web-network
@@ -89,8 +89,8 @@ services:
- ENABLE_IPV6=true - ENABLE_IPV6=true
volumes: volumes:
- ssl-data:/etc/nginx/certs:ro - ssl-data:/etc/nginx/certs:ro
- ./nginx/default_html:/usr/share/nginx/html - ./data/nginx/default_html:/usr/share/nginx/html
- ./nginx/vhost.d:/etc/nginx/vhost.d - ./data/nginx/vhost.d:/etc/nginx/vhost.d
labels: labels:
- "docker-volume-backup.stop-during-backup=true" # Ensure consistency during backup - "docker-volume-backup.stop-during-backup=true" # Ensure consistency during backup
depends_on: depends_on:
@@ -110,9 +110,9 @@ services:
- DOCKER_HOST=tcp://socket-rw:2375 # Needs RW to restart Nginx after renewal - DOCKER_HOST=tcp://socket-rw:2375 # Needs RW to restart Nginx after renewal
volumes: volumes:
- ssl-data:/etc/nginx/certs - ssl-data:/etc/nginx/certs
- ./nginx/vhost.d:/etc/nginx/vhost.d - ./data/nginx/vhost.d:/etc/nginx/vhost.d
- ./nginx/default_html:/usr/share/nginx/html - ./data/nginx/default_html:/usr/share/nginx/html
- ./nginx/acme_config:/etc/acme.sh - ./data/nginx/acme_config:/etc/acme.sh
labels: labels:
- "docker-volume-backup.stop-during-backup=true" - "docker-volume-backup.stop-during-backup=true"
depends_on: depends_on:
@@ -132,7 +132,7 @@ services:
# - LETSENCRYPT_HOST=${HOSTNAME}, www.${HOSTNAME} # - LETSENCRYPT_HOST=${HOSTNAME}, www.${HOSTNAME}
# - LETSENCRYPT_EMAIL=${EMAIL} # - LETSENCRYPT_EMAIL=${EMAIL}
# volumes: # volumes:
# - ./data:/usr/share/nginx/html/data # - /data/public:/usr/share/nginx/html/data
# networks: # networks:
# - web-network # - web-network
@@ -186,7 +186,7 @@ services:
container_name: synapse container_name: synapse
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- ./synapse:/data - ./data/synapse:/data
environment: environment:
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml - SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
- VIRTUAL_HOST=msg.${HOSTNAME} - VIRTUAL_HOST=msg.${HOSTNAME}
@@ -210,7 +210,7 @@ services:
- GITEA__server__ROOT_URL=https://git.${HOSTNAME}/ - GITEA__server__ROOT_URL=https://git.${HOSTNAME}/
- DISABLE_REGISTRATION=true # Private instance security - DISABLE_REGISTRATION=true # Private instance security
volumes: volumes:
- ./gitea:/data - ./data/gitea:/data
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
ports: ports:
- "222:22" # SSH port mapping for Git operations - "222:22" # SSH port mapping for Git operations
@@ -227,7 +227,7 @@ services:
- LETSENCRYPT_HOST=mirror.${HOSTNAME} - LETSENCRYPT_HOST=mirror.${HOSTNAME}
- LETSENCRYPT_EMAIL=${EMAIL} - LETSENCRYPT_EMAIL=${EMAIL}
- VIRTUAL_PORT=4321 - VIRTUAL_PORT=4321
- BETTER_AUTH_SECRET=${MIRROR_AUTH_SECRET} - BETTER_AUTH_SECRET=${GITHUB_AUTH_SECRET}
volumes: volumes:
- gitea-mirror-data:/app/data - gitea-mirror-data:/app/data
networks: networks:

View File

@@ -1,32 +1,17 @@
#!/bin/bash #!/bin/bash
set -euo pipefail PROJECT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
readonly PROJECT_DIR 2>/dev/null
source "$PROJECT_DIR/utils.sh"
readonly PROJECT_NAME="serverconfig" # set -euo pipefail
readonly PROJECT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
readonly REQ=("curl" "docker") ENV_LIST=(
"EMAIL" "HOSTNAME"
readonly ENV_FILE="${PROJECT_DIR}/.env" "TELEGRAM_TOKEN" "TELEGRAM_CHAT_ID"
"AWS" "ENDPOINT" "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY"
readonly GREEN='\033[0;32m' "GITHUB_AUTH_SECRET"
readonly RED='\033[0;31m' )
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m'
DATETIME_FORMAT="%d-%m-%Y %H:%M:%S"
function log() {
local type="${1}"
local color="${2}"
local message="${3}"
echo -e "${color}[$(date +"$DATETIME_FORMAT")] [${type}]${NC} ${message}"
}
function log_info() { log "INFO" "$BLUE" "$1"; }
function log_success() { log "OK " "$GREEN" "$1"; }
function log_error() { log "ERR " "$RED" "$1" >&2; }
function check_root() { function check_root() {
if [[ $EUID -ne 0 ]]; then if [[ $EUID -ne 0 ]]; then
@@ -49,8 +34,13 @@ function check_dependencies() {
function install_scripts() { function install_scripts() {
log_info "Installing scripts..." log_info "Installing scripts..."
for script in "$SCRIPT_FILE"/*.sh; do for script in "$PROJECT_DIR"/*.sh; do
[ -e "$script" ] || continue [ -e "$script" ] || continue
if [[ "$script" == "$(realpath "$0")" ]]; then
continue
fi
log_info "Configuring $(basename "$script")..." log_info "Configuring $(basename "$script")..."
if ! bash "$script" --install; then if ! bash "$script" --install; then
log_error "Hook failed for $script" log_error "Hook failed for $script"
@@ -68,9 +58,17 @@ function main() {
log_info "Creating directories and files..." log_info "Creating directories and files..."
touch "$ENV_FILE" touch "$ENV_FILE"
for env in "${ENV_LIST[@]}"; do
read -sp "Enter value for $env: " value
echo
env_variable "$env" "$value"
done
install_scripts install_scripts
log_success "Installation Complete" log_success "Installation Complete"
} }
main "$@" if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

View File

@@ -1,105 +0,0 @@
#!/bin/bash
FILENAME="serverconfig"
ETC_DIR="/etc/$FILENAME"
ENV_FILE="$ETC_DIR/.env"
LOG="/var/log/$FILENAME.log"
SCRIPT_FILE="/usr/local/bin"
INFO="\e[34mINFO\e[0m"
SUCCESS="\e[32mSUCCESS\e[0m"
WARN="\e[33mWARN\e[0m"
ERROR="\e[31mERROR\e[0m"
DEBUG="\e[35mDEBUG\e[0m"
ACTION="\e[36mACTION\e[0m"
INFO_FLAG=1
WARN_FLAG=2
ERROR_FLAG=3
DEBUG_FLAG=4
ACTION_FLAG=5
SUCCESS_FLAG=6
DATETIME_FORMAT="%d-%m-%Y %H:%M:%S"
info_print() {
local message="$1"
local level="${2:-1}"
local write_log="${3:-true}"
case $level in
1|--) local level=$INFO;;
2) local level=$WARN;;
3) local level=$ERROR;;
4) local level=$DEBUG;;
5) local level=$ACTION;;
6) local level=$SUCCESS;;
*);;
esac
local output="[$(date +"$DATETIME_FORMAT")] - $level: $message"
if [ "$write_log" = true ]; then echo -e "$output" | tee -a "$LOG"
else echo -e "$output"
fi
}
create_env_variable() {
local key="$1"
local value="$2"
local file="${3:-$ENV_FILE}"
local isUpdated="${4:-true}"
if [[ "$file" == '--' ]]; then
file=$ENV_FILE
fi
if [[ -z "$value" && "$isUpdated" == true ]]; then
if grep -q "^$key=*" "$file" 2>/dev/null; then
info_print "$key not updated." $WARN_FLAG
return
else
info_print "$key not set (empty input)." $WARN_FLAG
return
fi
fi
if [[ "$isUpdated" == true ]]; then
if grep -Eq "^${key}=" "$file" 2>/dev/null; then
read -p "$key already set, overwrite? (y/N): " yn
case "$yn" in
[Yy]*)
sed -i "s/^$key=.*/$key=$value/" "$file"
info_print "$key updated." $SUCCESS_FLAG
;;
*)
info_print "$key not changed." $WARN_FLAG
;;
esac
else
echo "$key=$value" >> "$file"
info_print "$key set." $SUCCESS_FLAG
fi
else
if grep -Eq "^${key}=" "$file" 2>/dev/null; then
sed -i "s/^$key=.*/$key=$value/" "$file"
else
echo "$key=$value" >> "$file"
fi
fi
}
create_raw_line_variable() {
local line="$1"
local file="$2"
if grep -Fxq "$line" "$file" 2>/dev/null; then
info_print "'$line' already defined as raw line." $WARN_FLAG
else
echo "$line" >> "$file"
info_print "'$line' added as raw line." $SUCCESS_FLAG
fi
}

View File

@@ -1,28 +0,0 @@
source /usr/local/bin/libs/common.sh
source /etc/serverconfig/.env
INSTALLED=$1
if [[ "--install" == $INSTALLED ]]; then
info_print "\n\
==================================================\n\
notifications Installation\n\
--------------------------------------------------"
ENV_LIST=("TELEGRAM_TOKEN" "TELEGRAM_CHAT_ID")
for env in "${ENV_LIST[@]}"; do
read -p "Enter value for $env: " value
create_env_variable "$env" "$value"
done
exit 0
fi
send_notification() {
local message="$1"
curl -X POST "https://api.telegram.org/bot$TELEGRAM_TOKEN/sendMessage" \
-d "chat_id=$TELEGRAM_CHAT_ID" \
-d "text=$message" \
-d "parse_mode=HTML"
}

View File

@@ -1,28 +0,0 @@
#!/bin/bash
INSTALLED=$1
if [[ "--install" == $INSTALLED ]]; then
source /usr/local/bin/libs/common.sh
info_print "\n\
==================================================\n\
docker-compose Installation\n\
--------------------------------------------------"
ENV_LIST=("EMAIL" "HOSTNAME")
for env in "${ENV_LIST[@]}"; do
read -p "Enter value for $env: " value
create_env_variable "$env" "$value"
done
source /etc/serverconfig/.env
if [[ -f "$ETC_DIR/docker-compose.yml" ]]; then
docker compose -f "$ETC_DIR/docker-compose.yml" up -d --force-recreate --remove-orphans && \
info_print "$ETC_DIR/docker-compose.yml is running." 6;
else info_print "no docker-compose.yml found at $ETC_DIR" 3;
fi
exit 0;
fi

View File

@@ -1,43 +0,0 @@
#!/bin/bash
source /usr/local/bin/libs/common.sh
source /etc/serverconfig/.env
INSTALLED=$1
if [[ "--install" == $INSTALLED ]]; then
info_print "\n\
==================================================\n\
sshd-login Installation\n\
--------------------------------------------------"
login='session optional pam_exec.so /usr/local/bin/scripts/sshd-login.sh'
file='/etc/pam.d/common-session'
if [[ ! -f "$file" ]]; then
info_print "$file doesn't found." $ERROR_FLAG
exit 1
fi
if ! grep -Fxq "$login" "$file"; then
echo "$login" >> "$file"
info_print "login command added to $file." $SUCCESS_FLAG
else
info_print "login command already added to $file." $WARN_FLAG
fi
exit 0;
fi
source /usr/local/bin/libs/notifications.sh
case "$PAM_TYPE" in
open_session)
PAYLOAD=$(printf "<b>🚨 Login Event 🚨</b>\nUser <code>%s</code> logged in from <code>%s</code> at <i>%s</i>." "$PAM_USER" "$PAM_RHOST" "$(date)")
;;
close_session)
PAYLOAD=$(printf "<b>🚨 Logout Event 🚨</b>\nUser <code>%s</code> logged out from <code>%s</code> at <i>%s</i>." "$PAM_USER" "$PAM_RHOST" "$(date)")
;;
esac
if [ -n "$PAYLOAD" ] ; then
send_notification "$PAYLOAD"
fi

40
sshd-login.sh Normal file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
PROJECT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
readonly PROJECT_DIR 2>/dev/null
source $PROJECT_DIR/utils.sh
INSTALLED=$1
if [[ "--install" == $INSTALLED ]]; then
log_info "sshd-login Installation"
login="session optional pam_exec.so $PROJECT_DIR/sshd-login.sh"
file='/etc/pam.d/common-session'
if [[ ! -f "$file" ]]; then
log_error "$file doesn't found."
exit 1
fi
if ! grep -Fxq "$login" "$file"; then
echo "$login" >>"$file"
log_success "login command added to $file."
else
log_warn "login command already added to $file." $WARN_FLAG
fi
exit 0
fi
case "$PAM_TYPE" in
open_session)
PAYLOAD=$(printf "<b>🚨 Login Event 🚨</b>\nUser <code>%s</code> logged in from <code>%s</code> at <i>%s</i>." "$PAM_USER" "$PAM_RHOST" "$(date)")
;;
close_session)
PAYLOAD=$(printf "<b>🚨 Logout Event 🚨</b>\nUser <code>%s</code> logged out from <code>%s</code> at <i>%s</i>." "$PAM_USER" "$PAM_RHOST" "$(date)")
;;
esac
if [ -n "$PAYLOAD" ]; then
send_notification "$PAYLOAD"
fi

70
utils.sh Normal file
View File

@@ -0,0 +1,70 @@
#!/bin/bash
# UTILITY FUNCTIONS & CONFIGURATION
# This script acts as a library (utils.sh) for other monitoring scripts.
# It handles logging, environment variables, and Telegram notifications.
PROJECT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
readonly PROJECT_DIR 2>/dev/null
readonly PROJECT_NAME="serverconfig"
readonly REQ=("curl" "docker")
readonly ENV_FILE="${PROJECT_DIR}/.env"
readonly GREEN='\033[0;32m'
readonly RED='\033[0;31m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m'
DATETIME_FORMAT="%d-%m-%Y %H:%M:%S"
function log() {
local type="${1}"
local color="${2}"
local message="${3}"
echo -e "${color}[$(date +"$DATETIME_FORMAT")] [${type}]${NC} ${message}"
}
function log_info() { log "INFO" "$BLUE" "$1"; }
function log_success() { log "OK " "$GREEN" "$1"; }
function log_error() { log "ERR " "$RED" "$1" >&2; }
function log_warn() { log "WARN " "$YELLOW" "$1" >&2; }
# USING TELEGRAM (may change later)
function send_notification() {
local message="$1"
curl -X POST "https://api.telegram.org/bot$TELEGRAM_TOKEN/sendMessage" \
-d "chat_id=$TELEGRAM_CHAT_ID" \
-d "text=$message" \
-d "parse_mode=HTML"
}
function env_variable() {
source $ENV_FILE
local key="$1"
local value="$2"
if [[ -z "$value" ]]; then
log_warn "$key not set."
fi
if grep -Eq "^${key}=" "$ENV_FILE" 2>/dev/null; then
read -p "$key already set, overwrite? (y/N): " yn
case "$yn" in
[yY]*)
sed -i "s/^$key=.*/$key=$value/" "$ENV_FILE"
log_success "$key updated."
;;
*)
log_info "$key not changed."
;;
esac
else
echo "$key=$value" >> "$ENV_FILE"
log_success "$key created."
fi
}