Merge branch 'main' of https://git.jakach.ch/jakach/jakach-login
This commit is contained in:
+425
-18
@@ -2,15 +2,243 @@ name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
security_scan:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GIT_REPO: jakach/jakach-login
|
||||
GIT_BRANCH: main
|
||||
GIT_USER: ${{ vars.GIT_USER }}
|
||||
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
|
||||
SECURITY_SCAN_ENABLED: ${{ vars.SECURITY_SCAN_ENABLED }}
|
||||
TRIVY_SEVERITY: HIGH,CRITICAL
|
||||
|
||||
steps:
|
||||
- name: Scan Docker images for vulnerabilities
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
|
||||
case "${SECURITY_SCAN_ENABLED:-true}" in
|
||||
false|False|FALSE|0|no|No|NO|off|Off|OFF)
|
||||
echo "Security scan disabled by SECURITY_SCAN_ENABLED=${SECURITY_SCAN_ENABLED}"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
: "${GIT_USER:?GIT_USER is required}"
|
||||
: "${GIT_TOKEN:?GIT_TOKEN is required}"
|
||||
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
apk add --no-cache ca-certificates curl git
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update
|
||||
apt-get install -y ca-certificates curl git
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
dnf install -y ca-certificates curl git
|
||||
elif command -v yum >/dev/null 2>&1; then
|
||||
yum install -y ca-certificates curl git
|
||||
else
|
||||
echo "Unsupported package manager"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v trivy >/dev/null 2>&1; then
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh \
|
||||
| sh -s -- -b "$HOME/.local/bin"
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
fi
|
||||
|
||||
GIT_HOST="${GIT_HOST:-git.jakach.ch}"
|
||||
REPO_URL="https://${GIT_USER}:${GIT_TOKEN}@${GIT_HOST}/${GIT_REPO}"
|
||||
|
||||
git clone \
|
||||
--branch "$GIT_BRANCH" \
|
||||
"$REPO_URL" \
|
||||
source
|
||||
|
||||
cd source
|
||||
|
||||
COMPOSE_FILE=""
|
||||
|
||||
for file in docker-compose.yml compose.yml compose.yaml; do
|
||||
if [ -f "$file" ]; then
|
||||
COMPOSE_FILE="$file"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$COMPOSE_FILE" ]; then
|
||||
echo "No docker compose file found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
docker compose -f "$COMPOSE_FILE" config --images > images.txt
|
||||
else
|
||||
awk '
|
||||
/^[[:space:]]*image:[[:space:]]*/ {
|
||||
sub(/^[[:space:]]*image:[[:space:]]*/, "")
|
||||
gsub(/["\047]/, "")
|
||||
print
|
||||
}
|
||||
' "$COMPOSE_FILE" > images.txt
|
||||
fi
|
||||
|
||||
sort -u images.txt -o images.txt
|
||||
|
||||
if [ ! -s images.txt ]; then
|
||||
echo "No Docker images found to scan"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TRIVY_IGNORE_ARGS=""
|
||||
|
||||
if [ -f cve_blacklist.txt ]; then
|
||||
awk '
|
||||
/^[[:space:]]*($|#)/ {
|
||||
next
|
||||
}
|
||||
{
|
||||
print $1
|
||||
}
|
||||
' cve_blacklist.txt > .trivyignore
|
||||
|
||||
if [ -s .trivyignore ]; then
|
||||
TRIVY_IGNORE_ARGS="--ignorefile .trivyignore"
|
||||
echo "Using CVE blacklist from cve_blacklist.txt"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Scanning Docker images for ${TRIVY_SEVERITY} vulnerabilities:"
|
||||
cat images.txt
|
||||
|
||||
failed=0
|
||||
|
||||
while IFS= read -r image; do
|
||||
[ -n "$image" ] || continue
|
||||
|
||||
echo ""
|
||||
echo "Scanning ${image}"
|
||||
|
||||
if ! trivy \
|
||||
image \
|
||||
--exit-code 1 \
|
||||
--severity "${TRIVY_SEVERITY}" \
|
||||
--ignore-unfixed \
|
||||
${TRIVY_IGNORE_ARGS} \
|
||||
--no-progress \
|
||||
"${image}"; then
|
||||
failed=1
|
||||
fi
|
||||
done < images.txt
|
||||
|
||||
if [ "$failed" -ne 0 ]; then
|
||||
echo "WARNING: High or critical vulnerabilities were found in one or more Docker images. Deployment stopped."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "No high or critical vulnerabilities found"
|
||||
|
||||
code_scan:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GIT_REPO: jakach/jakach-login
|
||||
GIT_BRANCH: main
|
||||
GIT_USER: ${{ vars.GIT_USER }}
|
||||
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
|
||||
CODE_SCAN_ENABLED: ${{ vars.CODE_SCAN_ENABLED }}
|
||||
|
||||
steps:
|
||||
- name: Scan source code
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
|
||||
case "${CODE_SCAN_ENABLED:-true}" in
|
||||
false|False|FALSE|0|no|No|NO|off|Off|OFF)
|
||||
echo "Code scan disabled by CODE_SCAN_ENABLED=${CODE_SCAN_ENABLED}"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
: "${GIT_USER:?GIT_USER is required}"
|
||||
: "${GIT_TOKEN:?GIT_TOKEN is required}"
|
||||
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
apk add --no-cache ca-certificates curl git python3 py3-pip
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update
|
||||
apt-get install -y ca-certificates curl git python3 python3-pip python3-venv
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
dnf install -y ca-certificates curl git python3 python3-pip
|
||||
elif command -v yum >/dev/null 2>&1; then
|
||||
yum install -y ca-certificates curl git python3 python3-pip
|
||||
else
|
||||
echo "Unsupported package manager"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v semgrep >/dev/null 2>&1; then
|
||||
python3 -m venv "$HOME/.semgrep-venv"
|
||||
. "$HOME/.semgrep-venv/bin/activate"
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install semgrep
|
||||
fi
|
||||
|
||||
if ! command -v trivy >/dev/null 2>&1; then
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh \
|
||||
| sh -s -- -b "$HOME/.local/bin"
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
fi
|
||||
|
||||
GIT_HOST="${GIT_HOST:-git.jakach.ch}"
|
||||
REPO_URL="https://${GIT_USER}:${GIT_TOKEN}@${GIT_HOST}/${GIT_REPO}"
|
||||
|
||||
git clone \
|
||||
--branch "$GIT_BRANCH" \
|
||||
"$REPO_URL" \
|
||||
source
|
||||
|
||||
cd source
|
||||
|
||||
semgrep scan \
|
||||
--config p/default \
|
||||
--error \
|
||||
--metrics=off
|
||||
|
||||
trivy fs \
|
||||
--scanners vuln,misconfig,secret \
|
||||
--exit-code 1 \
|
||||
--severity HIGH,CRITICAL \
|
||||
--ignore-unfixed \
|
||||
--no-progress \
|
||||
.
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- security_scan
|
||||
- code_scan
|
||||
|
||||
env:
|
||||
GIT_REPO: jakach/jakach-login
|
||||
GIT_BRANCH: main
|
||||
|
||||
APP_NAME: template
|
||||
APP_DOMAIN: auth.jakach.ch
|
||||
APP_PORT: 447
|
||||
|
||||
steps:
|
||||
- name: Install SSH client
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
set -e
|
||||
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
apk add --no-cache openssh-client git bash
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
@@ -21,55 +249,234 @@ jobs:
|
||||
elif command -v yum >/dev/null 2>&1; then
|
||||
yum install -y openssh-clients git bash
|
||||
else
|
||||
echo "No supported package manager found"
|
||||
echo "Unsupported package manager"
|
||||
exit 1
|
||||
fi
|
||||
- name: Run deploy
|
||||
|
||||
- name: Deploy application
|
||||
env:
|
||||
SSH_KEY: ${{ secrets.SSH_KEY }}
|
||||
SSH_USER: ${{ vars.SSH_USER }}
|
||||
SSH_IP: ${{ vars.SSH_IP }}
|
||||
|
||||
GIT_USER: ${{ vars.GIT_USER }}
|
||||
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
|
||||
APP_DIR: /home/deploy/my-app
|
||||
GIT_REPO: Jakach/my-app.git
|
||||
GIT_BRANCH: main
|
||||
|
||||
run: |
|
||||
cat > deploy.sh <<'EOF'
|
||||
cat > deploy.sh <<'OUTER_EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
: "${SSH_KEY:?SSH_KEY is required}"
|
||||
: "${SSH_USER:?SSH_USER is required}"
|
||||
: "${SSH_IP:?SSH_IP is required}"
|
||||
: "${GIT_USER:?GIT_USER is required}"
|
||||
: "${GIT_TOKEN:?GIT_TOKEN is required}"
|
||||
|
||||
APP_DIR="/srv/systems/jakach-login"
|
||||
GIT_HOST="${GIT_HOST:-git.jakach.ch}"
|
||||
GIT_REPO="jakach/jakach-login.git"
|
||||
GIT_BRANCH="${GIT_BRANCH:-main}"
|
||||
|
||||
REPO_NAME="$(basename "$GIT_REPO")"
|
||||
APP_DIR="/srv/systems/${REPO_NAME}"
|
||||
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
|
||||
printf '%s\n' "$SSH_KEY" | tr -d '\r' > ~/.ssh/deploy_key
|
||||
chmod 600 ~/.ssh/deploy_key
|
||||
|
||||
ssh-keyscan -H "$SSH_IP" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||
|
||||
ssh -i ~/.ssh/deploy_key \
|
||||
ssh \
|
||||
-i ~/.ssh/deploy_key \
|
||||
-o StrictHostKeyChecking=yes \
|
||||
-o IdentitiesOnly=yes \
|
||||
"$SSH_USER@$SSH_IP" \
|
||||
"export APP_DIR='$APP_DIR' GIT_HOST='$GIT_HOST' GIT_REPO='$GIT_REPO' GIT_BRANCH='$GIT_BRANCH' GIT_USER='$GIT_USER' GIT_TOKEN='$GIT_TOKEN'; bash -s" <<'REMOTE'
|
||||
"
|
||||
export \
|
||||
APP_NAME='${APP_NAME}' \
|
||||
APP_DOMAIN='${APP_DOMAIN}' \
|
||||
APP_PORT='${APP_PORT}' \
|
||||
APP_DIR='${APP_DIR}' \
|
||||
GIT_HOST='${GIT_HOST}' \
|
||||
GIT_REPO='${GIT_REPO}' \
|
||||
GIT_BRANCH='${GIT_BRANCH}' \
|
||||
GIT_USER='${GIT_USER}' \
|
||||
GIT_TOKEN='${GIT_TOKEN}';
|
||||
bash -s
|
||||
" <<'REMOTE_EOF'
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
REPO_URL="https://${GIT_USER}:${GIT_TOKEN}@${GIT_HOST}/${GIT_REPO}"
|
||||
|
||||
PROXY_DIR="/srv/systems/proxy"
|
||||
NGINX_CONF="${PROXY_DIR}/nginx_conf/nginx.conf"
|
||||
GET_CERT_SCRIPT="${PROXY_DIR}/get_cert.sh"
|
||||
RENEW_SCRIPT="${PROXY_DIR}/renew_cert.sh"
|
||||
FIRST_DEPLOY=0
|
||||
PROXY_RESTART_REQUIRED=0
|
||||
|
||||
# --------------------------------------------------
|
||||
# Clone repository if missing
|
||||
# --------------------------------------------------
|
||||
|
||||
if [ ! -d "$APP_DIR/.git" ]; then
|
||||
echo "Repository missing, cloning..."
|
||||
FIRST_DEPLOY=1
|
||||
|
||||
mkdir -p "$(dirname "$APP_DIR")"
|
||||
|
||||
git clone \
|
||||
--branch "$GIT_BRANCH" \
|
||||
"$REPO_URL" \
|
||||
"$APP_DIR"
|
||||
fi
|
||||
|
||||
# --------------------------------------------------
|
||||
# Update repository
|
||||
# --------------------------------------------------
|
||||
|
||||
cd "$APP_DIR"
|
||||
git remote set-url origin "https://${GIT_USER}:${GIT_TOKEN}@${GIT_HOST}/${GIT_REPO}"
|
||||
|
||||
git remote set-url origin "$REPO_URL"
|
||||
|
||||
git fetch origin "$GIT_BRANCH"
|
||||
|
||||
git checkout "$GIT_BRANCH"
|
||||
|
||||
git pull origin "$GIT_BRANCH"
|
||||
docker compose down
|
||||
docker compose up -d --build
|
||||
REMOTE
|
||||
EOF
|
||||
|
||||
# --------------------------------------------------
|
||||
# Create nginx reverse proxy entry
|
||||
# --------------------------------------------------
|
||||
|
||||
if [ "$FIRST_DEPLOY" -eq 1 ]; then
|
||||
|
||||
if ! grep -q "proxy_pass http://192.168.1.109:${APP_PORT}/;" "$NGINX_CONF"; then
|
||||
echo "Creating nginx entry..."
|
||||
PROXY_RESTART_REQUIRED=1
|
||||
|
||||
cat >> "$NGINX_CONF" <<NGINXEOF
|
||||
|
||||
# ---------------------
|
||||
# ${APP_NAME} Service
|
||||
# ---------------------
|
||||
server {
|
||||
server_tokens off;
|
||||
listen 443 ssl;
|
||||
server_name ${APP_DOMAIN};
|
||||
|
||||
ssl_certificate /etc/nginx/certs/${APP_NAME}.fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/${APP_NAME}.privkey.pem;
|
||||
|
||||
if (\$allowed_country = no) {
|
||||
return 403;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://192.168.1.109:${APP_PORT}/;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
NGINXEOF
|
||||
|
||||
else
|
||||
echo "Nginx entry already exists"
|
||||
fi
|
||||
|
||||
# --------------------------------------------------
|
||||
# Add domain to get_cert.sh
|
||||
# --------------------------------------------------
|
||||
|
||||
if ! grep -q "\"${APP_DOMAIN}\"" "$GET_CERT_SCRIPT"; then
|
||||
echo "Adding domain to get_cert.sh"
|
||||
|
||||
sed -i "/DOMAINS=(/a\ \"${APP_DOMAIN}\"" "$GET_CERT_SCRIPT"
|
||||
PROXY_RESTART_REQUIRED=1
|
||||
fi
|
||||
|
||||
# --------------------------------------------------
|
||||
# Create certificate if missing
|
||||
# --------------------------------------------------
|
||||
|
||||
if [ ! -d "/etc/letsencrypt/live/${APP_DOMAIN}" ]; then
|
||||
echo "Creating certificate..."
|
||||
|
||||
sudo certbot certonly \
|
||||
--standalone \
|
||||
--non-interactive \
|
||||
--agree-tos \
|
||||
-m admin@jakach.ch \
|
||||
-d "${APP_DOMAIN}"
|
||||
PROXY_RESTART_REQUIRED=1
|
||||
else
|
||||
echo "Certificate already exists"
|
||||
fi
|
||||
|
||||
# --------------------------------------------------
|
||||
# Update renew_cert.sh
|
||||
# --------------------------------------------------
|
||||
|
||||
if ! grep -q "${APP_DOMAIN}/privkey.pem" "$RENEW_SCRIPT"; then
|
||||
echo "Updating renew_cert.sh"
|
||||
|
||||
cat >> "$RENEW_SCRIPT" <<CERTEOF
|
||||
|
||||
cp /etc/letsencrypt/live/${APP_DOMAIN}/privkey.pem certs/${APP_NAME}.privkey.pem
|
||||
cp /etc/letsencrypt/live/${APP_DOMAIN}/fullchain.pem certs/${APP_NAME}.fullchain.pem
|
||||
|
||||
CERTEOF
|
||||
PROXY_RESTART_REQUIRED=1
|
||||
fi
|
||||
|
||||
chmod +x "$RENEW_SCRIPT"
|
||||
|
||||
# --------------------------------------------------
|
||||
# Make renew non-interactive
|
||||
# --------------------------------------------------
|
||||
|
||||
sed -i 's/certbot renew$/certbot renew -n/' "$RENEW_SCRIPT" || true
|
||||
|
||||
# --------------------------------------------------
|
||||
# Renew certs + restart proxy
|
||||
# --------------------------------------------------
|
||||
|
||||
if [ "$PROXY_RESTART_REQUIRED" -eq 1 ]; then
|
||||
cd "$PROXY_DIR"
|
||||
|
||||
sudo bash /srv/systems/proxy/renew_cert.sh
|
||||
|
||||
docker compose down || true
|
||||
docker compose up -d --build
|
||||
else
|
||||
echo "Proxy already configured, skipping certificate renewal and proxy restart"
|
||||
fi
|
||||
|
||||
else
|
||||
echo "Existing deployment, skipping proxy setup, certificate renewal and proxy restart"
|
||||
fi
|
||||
|
||||
# --------------------------------------------------
|
||||
# Deploy app
|
||||
# --------------------------------------------------
|
||||
|
||||
cd "$APP_DIR"
|
||||
|
||||
if [ -f docker-compose.yml ] || [ -f compose.yml ] || [ -f compose.yaml ]; then
|
||||
echo "Deploying docker stack..."
|
||||
|
||||
docker compose down || true
|
||||
docker compose up -d --build
|
||||
else
|
||||
echo "No docker compose file found"
|
||||
fi
|
||||
|
||||
echo "Deployment complete"
|
||||
|
||||
REMOTE_EOF
|
||||
OUTER_EOF
|
||||
|
||||
chmod +x deploy.sh
|
||||
./deploy.sh
|
||||
@@ -14,13 +14,8 @@ Using Jakach Login is straightforward:
|
||||
|
||||
1. **Clone the repository:**
|
||||
```bash
|
||||
git clone https://github.com/jakani24/jakach-login
|
||||
git clone https://git.jakach.ch/jakani24/jakach-login
|
||||
```
|
||||
2. **Create the `certs/` folder and set up SSL certificates:**
|
||||
```bash
|
||||
mkdir certs/
|
||||
```
|
||||
- Generate certificates (e.g., using [Let's Encrypt](https://letsencrypt.org/getting-started/#with-shell-access)).
|
||||
|
||||
3. **Create a Docker volume for database storage:**
|
||||
```bash
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
jakach-login-db:
|
||||
image: yobasystems/alpine-mariadb:latest
|
||||
image: mariadb:10.6.25
|
||||
container_name: jakach-login-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
||||
Reference in New Issue
Block a user