23
.env.example
Normal file
23
.env.example
Normal file
@ -0,0 +1,23 @@
|
||||
# Host + network
|
||||
GITEA_DOMAIN=git.bhatfamily.in
|
||||
GITEA_ROOT_URL=https://git.bhatfamily.in/
|
||||
GITEA_HTTP_PORT=3000
|
||||
GITEA_SSH_PORT=2222
|
||||
TLS_EMAIL=admin@bhatfamily.in
|
||||
|
||||
# Storage (host path)
|
||||
GITEA_BASE_PATH=/media/rbhat/DATA/gitea
|
||||
|
||||
# Timezone + permissions
|
||||
TZ=Asia/Kolkata
|
||||
PUID=1000
|
||||
PGID=1000
|
||||
|
||||
# Database
|
||||
POSTGRES_USER=gitea
|
||||
POSTGRES_PASSWORD=change-me-strong-password
|
||||
POSTGRES_DB=gitea
|
||||
|
||||
# Gitea secrets (set strong values before internet exposure)
|
||||
GITEA_SECRET_KEY=change-me-gitea-secret-key
|
||||
GITEA_INTERNAL_TOKEN=change-me-gitea-internal-token
|
||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.env
|
||||
*.log
|
||||
91
README.md
Normal file
91
README.md
Normal file
@ -0,0 +1,91 @@
|
||||
# Gitea Homelab Automation (`git.bhatfamily.in`)
|
||||
Automated Docker-based setup for a self-hosted Gitea server with PostgreSQL, persistent storage at `/media/rbhat/DATA/gitea`, and lifecycle scripts for install, test, and uninstall.
|
||||
## What this repository provides
|
||||
- `docker-compose.yml` for:
|
||||
- `gitea/gitea:1.24.2`
|
||||
- `postgres:16-alpine`
|
||||
- optional TLS reverse proxy (`caddy:2.10-alpine`, profile: `tls`)
|
||||
- Idempotent lifecycle scripts:
|
||||
- `scripts/install.sh`
|
||||
- `scripts/test.sh`
|
||||
- `scripts/uninstall.sh`
|
||||
- Environment template: `.env.example`
|
||||
- Troubleshooting and network/DNS notes in `docs/`
|
||||
## Layout
|
||||
- Host storage root: `/media/rbhat/DATA/gitea`
|
||||
- Gitea data volume: `/media/rbhat/DATA/gitea/gitea-data`
|
||||
- Repository root (host): `/media/rbhat/DATA/gitea/gitea-data/git/repositories`
|
||||
- PostgreSQL data: `/media/rbhat/DATA/gitea/postgres`
|
||||
- Caddy data/config: `/media/rbhat/DATA/gitea/caddy-data`, `/media/rbhat/DATA/gitea/caddy-config`
|
||||
## Prerequisites
|
||||
- Docker + Docker Compose plugin installed
|
||||
- `curl` installed
|
||||
- `ufw` optional (if active, scripts add/remove rules for Gitea ports)
|
||||
- Sudo access to manage firewall rules
|
||||
## Quick start (baseline, no TLS profile)
|
||||
1. Copy and edit environment values:
|
||||
- `cp .env.example .env`
|
||||
- Change at least:
|
||||
- `POSTGRES_PASSWORD`
|
||||
- `GITEA_SECRET_KEY`
|
||||
- `GITEA_INTERNAL_TOKEN`
|
||||
2. Install/start stack:
|
||||
- `./scripts/install.sh`
|
||||
3. Validate setup:
|
||||
- `./scripts/test.sh`
|
||||
4. Open Gitea UI:
|
||||
- `http://localhost:3000` (or your configured HTTP port)
|
||||
## Quick start (TLS reverse proxy profile)
|
||||
1. Ensure `.env` has correct values:
|
||||
- `GITEA_DOMAIN=git.bhatfamily.in`
|
||||
- `GITEA_ROOT_URL=https://git.bhatfamily.in/`
|
||||
- `TLS_EMAIL=<your-email>` (used by Caddy for ACME account contact)
|
||||
2. Ensure DNS + router/NAT are configured first (see `docs/cloudflare-networking.md`).
|
||||
3. Install with TLS profile:
|
||||
- `./scripts/install.sh --with-tls --open-public-web`
|
||||
4. Test TLS profile (strict):
|
||||
- `./scripts/test.sh --with-tls`
|
||||
5. If DNS/cert is still propagating, run non-blocking external check:
|
||||
- `./scripts/test.sh --with-tls --allow-pending-external`
|
||||
6. Access:
|
||||
- `https://git.bhatfamily.in`
|
||||
## Uninstall
|
||||
- Stop and remove containers, keep data:
|
||||
- `./scripts/uninstall.sh`
|
||||
- Stop and remove containers including TLS profile:
|
||||
- `./scripts/uninstall.sh --with-tls`
|
||||
- Remove added 80/443 firewall rules too (if added with install flag):
|
||||
- `./scripts/uninstall.sh --with-tls --close-public-web`
|
||||
- Stop and remove containers and delete persistent data:
|
||||
- `./scripts/uninstall.sh --with-tls --purge-data`
|
||||
- Non-interactive full teardown:
|
||||
- `./scripts/uninstall.sh --with-tls --purge-data --purge-images --close-public-web --yes`
|
||||
## Port defaults
|
||||
- Host HTTP: `3000` -> container `3000`
|
||||
- Host SSH: `2222` -> container `22`
|
||||
- TLS profile ports: `80`, `443` -> Caddy
|
||||
## Firewall behavior
|
||||
When UFW is active:
|
||||
- install always adds:
|
||||
- `allow <GITEA_HTTP_PORT>/tcp` (comment: `Gitea HTTP`)
|
||||
- `allow <GITEA_SSH_PORT>/tcp` (comment: `Gitea SSH`)
|
||||
- install with `--open-public-web` also adds:
|
||||
- `allow 80/tcp` (comment: `Gitea TLS HTTP-01`)
|
||||
- `allow 443/tcp` (comment: `Gitea TLS HTTPS`)
|
||||
- uninstall always removes Gitea HTTP/SSH rules
|
||||
- uninstall with `--close-public-web` removes 80/443 rules
|
||||
## Cloudflare and home network changes
|
||||
See `docs/cloudflare-networking.md` for complete instructions.
|
||||
## Troubleshooting
|
||||
See `docs/troubleshooting.md` for diagnostics and common fixes.
|
||||
## Backup basics
|
||||
- Backup application data:
|
||||
- `/media/rbhat/DATA/gitea/gitea-data`
|
||||
- Backup PostgreSQL data:
|
||||
- `/media/rbhat/DATA/gitea/postgres`
|
||||
- If TLS profile used, backup Caddy state too:
|
||||
- `/media/rbhat/DATA/gitea/caddy-data`
|
||||
- `/media/rbhat/DATA/gitea/caddy-config`
|
||||
For consistent backups, stop containers first:
|
||||
- `docker compose --env-file .env -f docker-compose.yml down`
|
||||
Then archive directories and restart with `./scripts/install.sh` (or with `--with-tls`).
|
||||
19
caddy/Caddyfile
Normal file
19
caddy/Caddyfile
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
email {$TLS_EMAIL}
|
||||
}
|
||||
|
||||
{$GITEA_DOMAIN} {
|
||||
encode zstd gzip
|
||||
reverse_proxy gitea:3000
|
||||
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
}
|
||||
}
|
||||
|
||||
:80 {
|
||||
redir https://{$GITEA_DOMAIN}{uri} permanent
|
||||
}
|
||||
74
docker-compose.yml
Normal file
74
docker-compose.yml
Normal file
@ -0,0 +1,74 @@
|
||||
services:
|
||||
gitea:
|
||||
image: gitea/gitea:1.24.2
|
||||
container_name: gitea
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
USER_UID: ${PUID}
|
||||
USER_GID: ${PGID}
|
||||
GITEA__server__DOMAIN: ${GITEA_DOMAIN}
|
||||
GITEA__server__ROOT_URL: ${GITEA_ROOT_URL}
|
||||
GITEA__server__SSH_DOMAIN: ${GITEA_DOMAIN}
|
||||
GITEA__server__SSH_PORT: ${GITEA_SSH_PORT}
|
||||
GITEA__server__START_SSH_SERVER: "false"
|
||||
GITEA__database__DB_TYPE: postgres
|
||||
GITEA__database__HOST: postgres:5432
|
||||
GITEA__database__NAME: ${POSTGRES_DB}
|
||||
GITEA__database__USER: ${POSTGRES_USER}
|
||||
GITEA__database__PASSWD: ${POSTGRES_PASSWORD}
|
||||
GITEA__security__INSTALL_LOCK: "true"
|
||||
GITEA__security__SECRET_KEY: ${GITEA_SECRET_KEY}
|
||||
GITEA__security__INTERNAL_TOKEN: ${GITEA_INTERNAL_TOKEN}
|
||||
ports:
|
||||
- "${GITEA_HTTP_PORT}:3000"
|
||||
- "${GITEA_SSH_PORT}:22"
|
||||
volumes:
|
||||
- ${GITEA_BASE_PATH}/gitea-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--spider", "--quiet", "http://localhost:3000/api/healthz"]
|
||||
interval: 20s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: gitea-postgres
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
volumes:
|
||||
- ${GITEA_BASE_PATH}/postgres:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 20s
|
||||
|
||||
caddy:
|
||||
image: caddy:2.10-alpine
|
||||
container_name: gitea-caddy
|
||||
restart: unless-stopped
|
||||
profiles: ["tls"]
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
gitea:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- ${GITEA_BASE_PATH}/caddy-data:/data
|
||||
- ${GITEA_BASE_PATH}/caddy-config:/config
|
||||
52
docs/cloudflare-networking.md
Normal file
52
docs/cloudflare-networking.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Cloudflare DNS + Home Network Setup for `git.bhatfamily.in`
|
||||
## Goal
|
||||
Expose local Gitea securely from home network while preserving Git-over-SSH support.
|
||||
## Recommended Cloudflare DNS records
|
||||
Create DNS records in Cloudflare for zone `bhatfamily.in`:
|
||||
1. `A` record
|
||||
- Name: `git`
|
||||
- Content: your home public IPv4
|
||||
- Proxy status: **DNS only** (gray cloud)
|
||||
2. Optional `AAAA` record
|
||||
- Name: `git`
|
||||
- Content: your home public IPv6
|
||||
- Proxy status: **DNS only**
|
||||
## Why DNS-only is recommended
|
||||
This setup uses direct inbound routing for both HTTPS and custom SSH (`2222` by default). DNS-only avoids Cloudflare proxy protocol limitations around direct SSH forwarding.
|
||||
## Router/NAT rules
|
||||
Create forwards to this server's LAN IP:
|
||||
- TCP 80 -> `<server_lan_ip>:80` (ACME challenge + redirect)
|
||||
- TCP 443 -> `<server_lan_ip>:443` (HTTPS via Caddy)
|
||||
- TCP 2222 -> `<server_lan_ip>:2222` (Git SSH)
|
||||
## Firewall alignment
|
||||
If UFW is active, use install flag to open public web ports:
|
||||
- `./scripts/install.sh --with-tls --open-public-web`
|
||||
To close those later:
|
||||
- `./scripts/uninstall.sh --with-tls --close-public-web`
|
||||
## ISP constraints check
|
||||
Some ISPs block inbound ports. Validate from outside your network:
|
||||
- `curl -I https://git.bhatfamily.in`
|
||||
- `nc -vz git.bhatfamily.in 2222`
|
||||
If blocked, use alternate routing (VPN/tunnel) or ISP-compatible ports.
|
||||
## Recommended hardening
|
||||
- Keep strong secrets in `.env`
|
||||
- Restrict SSH source ranges if practical
|
||||
- Keep containers patched (`docker compose pull` and recreate)
|
||||
- Add off-host backups for gitea/postgres/caddy data directories
|
||||
|
||||
## Post-cutover verification checklist
|
||||
Run these after DNS/NAT/firewall updates to confirm end-to-end readiness:
|
||||
1. DNS resolution
|
||||
- `dig +short git.bhatfamily.in A`
|
||||
2. HTTPS response and redirect chain
|
||||
- `curl -I https://git.bhatfamily.in`
|
||||
- `curl -I http://git.bhatfamily.in`
|
||||
3. Certificate validity/issuer
|
||||
- `openssl s_client -connect git.bhatfamily.in:443 -servername git.bhatfamily.in </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates`
|
||||
4. Git SSH port reachability
|
||||
- `nc -vz git.bhatfamily.in 2222`
|
||||
5. Stack self-check
|
||||
- `./scripts/test.sh --with-tls`
|
||||
If any check fails during first-time propagation, run:
|
||||
- `./scripts/test.sh --with-tls --allow-pending-external`
|
||||
Then re-run strict checks once DNS/certificate propagation completes.
|
||||
60
docs/troubleshooting.md
Normal file
60
docs/troubleshooting.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Troubleshooting
|
||||
## 1) Containers fail to start
|
||||
- Check compose status:
|
||||
- `docker compose --env-file .env -f docker-compose.yml ps`
|
||||
- If TLS profile enabled:
|
||||
- `docker compose --env-file .env -f docker-compose.yml --profile tls ps`
|
||||
- Inspect logs:
|
||||
- `docker compose --env-file .env -f docker-compose.yml logs gitea`
|
||||
- `docker compose --env-file .env -f docker-compose.yml logs postgres`
|
||||
- `docker compose --env-file .env -f docker-compose.yml --profile tls logs caddy`
|
||||
## 2) Gitea health endpoint fails
|
||||
- Local probe:
|
||||
- `curl -v http://localhost:3000/api/healthz`
|
||||
- If port changed, use your `GITEA_HTTP_PORT`.
|
||||
- Confirm mapping:
|
||||
- `docker compose --env-file .env -f docker-compose.yml ps`
|
||||
## 3) Database connection errors
|
||||
- Confirm PostgreSQL health:
|
||||
- `docker compose --env-file .env -f docker-compose.yml ps postgres`
|
||||
- Re-check `.env` values:
|
||||
- `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`
|
||||
- Look for auth failures in postgres logs.
|
||||
## 4) Permission issues under `/media/rbhat/DATA/gitea`
|
||||
- Ensure current user can read/write this path.
|
||||
- If needed:
|
||||
- `sudo chown -R rbhat:rbhat /media/rbhat/DATA/gitea`
|
||||
- `sudo chmod -R u+rwX /media/rbhat/DATA/gitea`
|
||||
## 5) Firewall blocks access
|
||||
- Check active rules:
|
||||
- `sudo ufw status`
|
||||
- Expected allows:
|
||||
- `<GITEA_HTTP_PORT>/tcp`
|
||||
- `<GITEA_SSH_PORT>/tcp`
|
||||
- `80/tcp` and `443/tcp` if TLS profile is internet-exposed
|
||||
## 6) TLS cert not issuing
|
||||
- Ensure `git.bhatfamily.in` resolves publicly to your home WAN IP.
|
||||
- Ensure inbound TCP 80 and 443 are forwarded to this host.
|
||||
- Ensure `TLS_EMAIL` is set in `.env`.
|
||||
- Verify Caddy logs for ACME failures:
|
||||
- `docker compose --env-file .env -f docker-compose.yml --profile tls logs caddy`
|
||||
## 7) Port conflicts on 80/443
|
||||
- Check listeners:
|
||||
- `ss -tulpen | grep -E '(:80\s|:443\s)'`
|
||||
- Stop conflicting services or disable TLS profile until resolved.
|
||||
## 8) DNS resolves but service unreachable
|
||||
- Verify router forwarding targets correct LAN IP.
|
||||
- Verify host listening:
|
||||
- `ss -tulpen | grep -E '3000|2222|80|443'`
|
||||
- Test from external network (mobile hotspot) to avoid NAT loopback confusion.
|
||||
## 9) TLS tests fail during propagation
|
||||
- Strict mode (default) fails until DNS/routing/cert trust is ready:
|
||||
- `./scripts/test.sh --with-tls`
|
||||
- Temporary non-blocking mode:
|
||||
- `./scripts/test.sh --with-tls --allow-pending-external`
|
||||
## 10) Reset stack while keeping data
|
||||
- `./scripts/uninstall.sh --with-tls`
|
||||
- `./scripts/install.sh --with-tls --open-public-web`
|
||||
## 11) Full clean rebuild
|
||||
- `./scripts/uninstall.sh --with-tls --purge-data --purge-images --close-public-web --yes`
|
||||
- Re-run install
|
||||
71
scripts/install.sh
Executable file
71
scripts/install.sh
Executable file
@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
WITH_TLS=false
|
||||
OPEN_PUBLIC_WEB=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--with-tls) WITH_TLS=true ;;
|
||||
--open-public-web) OPEN_PUBLIC_WEB=true ;;
|
||||
*) die "Unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
log "Starting Gitea install"
|
||||
require_cmd docker
|
||||
require_cmd curl
|
||||
|
||||
load_env
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
: "${TLS_EMAIL:?TLS_EMAIL is required in .env when using --with-tls}"
|
||||
fi
|
||||
|
||||
mkdir -p "${GITEA_BASE_PATH}" "${GITEA_BASE_PATH}/gitea-data" "${GITEA_BASE_PATH}/postgres" "${GITEA_BASE_PATH}/backups" "${GITEA_BASE_PATH}/caddy-data" "${GITEA_BASE_PATH}/caddy-config"
|
||||
|
||||
log "Pulling images"
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
compose --profile tls pull
|
||||
else
|
||||
compose pull
|
||||
fi
|
||||
|
||||
log "Starting containers"
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
compose --profile tls up -d
|
||||
else
|
||||
compose up -d
|
||||
fi
|
||||
|
||||
wait_for_gitea_health
|
||||
apply_firewall_rules "${OPEN_PUBLIC_WEB}"
|
||||
|
||||
cat <<MSG
|
||||
Install complete.
|
||||
|
||||
Local access:
|
||||
- Web UI: http://localhost:${GITEA_HTTP_PORT}
|
||||
- SSH for Git: ssh://git@localhost:${GITEA_SSH_PORT}
|
||||
|
||||
Expected repository root on host:
|
||||
${GITEA_BASE_PATH}/gitea-data/git/repositories
|
||||
MSG
|
||||
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
cat <<TLSMSG
|
||||
TLS profile enabled (Caddy).
|
||||
- HTTPS endpoint (after DNS + router config): https://${GITEA_DOMAIN}
|
||||
- ACME contact email: ${TLS_EMAIL}
|
||||
- Optional firewall for 80/443 was $( [[ "${OPEN_PUBLIC_WEB}" == "true" ]] && printf 'enabled' || printf 'not changed' )
|
||||
TLSMSG
|
||||
fi
|
||||
|
||||
cat <<NEXT
|
||||
|
||||
Next:
|
||||
1) Run scripts/test.sh$( [[ "${WITH_TLS}" == "true" ]] && printf ' --with-tls' || true )
|
||||
2) Complete DNS/router/Cloudflare steps documented in README.md
|
||||
NEXT
|
||||
103
scripts/lib.sh
Executable file
103
scripts/lib.sh
Executable file
@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
ENV_FILE="${REPO_ROOT}/.env"
|
||||
ENV_EXAMPLE_FILE="${REPO_ROOT}/.env.example"
|
||||
COMPOSE_FILE="${REPO_ROOT}/docker-compose.yml"
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"
|
||||
}
|
||||
|
||||
create_env_if_missing() {
|
||||
if [[ ! -f "${ENV_FILE}" ]]; then
|
||||
log "No .env found; creating .env from template"
|
||||
cp "${ENV_EXAMPLE_FILE}" "${ENV_FILE}"
|
||||
fi
|
||||
}
|
||||
|
||||
load_env() {
|
||||
[[ -f "${ENV_EXAMPLE_FILE}" ]] || die "Missing template: ${ENV_EXAMPLE_FILE}"
|
||||
create_env_if_missing
|
||||
|
||||
set -a
|
||||
source "${ENV_FILE}"
|
||||
set +a
|
||||
|
||||
: "${GITEA_BASE_PATH:?GITEA_BASE_PATH is required in .env}"
|
||||
: "${GITEA_HTTP_PORT:?GITEA_HTTP_PORT is required in .env}"
|
||||
: "${GITEA_SSH_PORT:?GITEA_SSH_PORT is required in .env}"
|
||||
: "${GITEA_DOMAIN:?GITEA_DOMAIN is required in .env}"
|
||||
}
|
||||
|
||||
compose() {
|
||||
docker compose --env-file "${ENV_FILE}" -f "${COMPOSE_FILE}" "$@"
|
||||
}
|
||||
|
||||
wait_for_gitea_health() {
|
||||
local retries=45
|
||||
local delay=4
|
||||
local url="http://localhost:${GITEA_HTTP_PORT}/api/healthz"
|
||||
|
||||
log "Waiting for Gitea health endpoint: ${url}"
|
||||
for ((i=1; i<=retries; i++)); do
|
||||
if curl -fsS "${url}" >/dev/null 2>&1; then
|
||||
log "Gitea health check passed"
|
||||
return 0
|
||||
fi
|
||||
sleep "${delay}"
|
||||
done
|
||||
|
||||
die "Gitea did not become healthy in time"
|
||||
}
|
||||
|
||||
ufw_active() {
|
||||
command -v ufw >/dev/null 2>&1 || return 1
|
||||
sudo ufw status | grep -q '^Status: active'
|
||||
}
|
||||
|
||||
apply_firewall_rules() {
|
||||
local open_public_web="${1:-false}"
|
||||
|
||||
if ufw_active; then
|
||||
log "Applying UFW rules for Gitea ports"
|
||||
sudo ufw allow "${GITEA_HTTP_PORT}/tcp" comment 'Gitea HTTP'
|
||||
sudo ufw allow "${GITEA_SSH_PORT}/tcp" comment 'Gitea SSH'
|
||||
|
||||
if [[ "${open_public_web}" == "true" ]]; then
|
||||
log "Applying UFW rules for public reverse proxy ports"
|
||||
sudo ufw allow 80/tcp comment 'Gitea TLS HTTP-01'
|
||||
sudo ufw allow 443/tcp comment 'Gitea TLS HTTPS'
|
||||
fi
|
||||
else
|
||||
log "UFW not active; skipping firewall rule setup"
|
||||
fi
|
||||
}
|
||||
|
||||
remove_firewall_rules() {
|
||||
local close_public_web="${1:-false}"
|
||||
|
||||
if ufw_active; then
|
||||
log "Removing UFW rules for Gitea ports"
|
||||
sudo ufw --force delete allow "${GITEA_HTTP_PORT}/tcp" || true
|
||||
sudo ufw --force delete allow "${GITEA_SSH_PORT}/tcp" || true
|
||||
|
||||
if [[ "${close_public_web}" == "true" ]]; then
|
||||
log "Removing UFW rules for public reverse proxy ports"
|
||||
sudo ufw --force delete allow 80/tcp || true
|
||||
sudo ufw --force delete allow 443/tcp || true
|
||||
fi
|
||||
else
|
||||
log "UFW not active; skipping firewall rule removal"
|
||||
fi
|
||||
}
|
||||
72
scripts/test.sh
Executable file
72
scripts/test.sh
Executable file
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
WITH_TLS=false
|
||||
ALLOW_PENDING_EXTERNAL=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--with-tls) WITH_TLS=true ;;
|
||||
--allow-pending-external) ALLOW_PENDING_EXTERNAL=true ;;
|
||||
*) die "Unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
load_env
|
||||
require_cmd curl
|
||||
|
||||
log "Checking docker compose status"
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
compose --profile tls ps
|
||||
else
|
||||
compose ps
|
||||
fi
|
||||
|
||||
log "Checking local HTTP health"
|
||||
curl -fsS "http://localhost:${GITEA_HTTP_PORT}/api/healthz" && printf '\n'
|
||||
|
||||
log "Checking local web root"
|
||||
curl -I -sS "http://localhost:${GITEA_HTTP_PORT}" | grep -E 'HTTP/[0-9.]+ 200|HTTP/[0-9.]+ 302'
|
||||
|
||||
if command -v getent >/dev/null 2>&1; then
|
||||
log "Checking DNS resolution for ${GITEA_DOMAIN}"
|
||||
getent hosts "${GITEA_DOMAIN}" || log "DNS resolution not yet set for ${GITEA_DOMAIN}"
|
||||
fi
|
||||
|
||||
if command -v nc >/dev/null 2>&1; then
|
||||
log "Checking SSH TCP port on localhost"
|
||||
nc -z localhost "${GITEA_SSH_PORT}"
|
||||
fi
|
||||
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
log "Checking Caddy container status"
|
||||
compose --profile tls ps caddy | grep -E 'gitea-caddy|caddy'
|
||||
|
||||
if command -v nc >/dev/null 2>&1; then
|
||||
log "Checking reverse proxy listener ports"
|
||||
nc -z localhost 80
|
||||
nc -z localhost 443
|
||||
fi
|
||||
|
||||
if getent hosts "${GITEA_DOMAIN}" >/dev/null 2>&1; then
|
||||
log "Checking HTTPS response by domain"
|
||||
if [[ "${ALLOW_PENDING_EXTERNAL}" == "true" ]]; then
|
||||
if ! curl -k -I -sS "https://${GITEA_DOMAIN}" | grep -E 'HTTP/[0-9.]+ 200|HTTP/[0-9.]+ 302|HTTP/[0-9.]+ 308'; then
|
||||
log "HTTPS domain check did not pass yet (pending DNS/routing/certificate propagation)"
|
||||
fi
|
||||
else
|
||||
curl -I -sS "https://${GITEA_DOMAIN}" | grep -E 'HTTP/[0-9.]+ 200|HTTP/[0-9.]+ 302|HTTP/[0-9.]+ 308'
|
||||
fi
|
||||
else
|
||||
if [[ "${ALLOW_PENDING_EXTERNAL}" == "true" ]]; then
|
||||
log "Skipping strict HTTPS domain check until DNS is configured"
|
||||
else
|
||||
die "DNS not configured for ${GITEA_DOMAIN}; rerun with --allow-pending-external if still propagating"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
log "All tests passed"
|
||||
50
scripts/uninstall.sh
Executable file
50
scripts/uninstall.sh
Executable file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
PURGE_DATA=false
|
||||
PURGE_IMAGES=false
|
||||
ASSUME_YES=false
|
||||
WITH_TLS=false
|
||||
CLOSE_PUBLIC_WEB=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--purge-data) PURGE_DATA=true ;;
|
||||
--purge-images) PURGE_IMAGES=true ;;
|
||||
--yes) ASSUME_YES=true ;;
|
||||
--with-tls) WITH_TLS=true ;;
|
||||
--close-public-web) CLOSE_PUBLIC_WEB=true ;;
|
||||
*) die "Unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
load_env
|
||||
|
||||
if [[ "${PURGE_DATA}" == "true" && "${ASSUME_YES}" != "true" ]]; then
|
||||
read -r -p "This will delete ${GITEA_BASE_PATH}/gitea-data and ${GITEA_BASE_PATH}/postgres. Continue? [y/N]: " answer
|
||||
[[ "${answer}" == "y" || "${answer}" == "Y" ]] || die "Aborted by user"
|
||||
fi
|
||||
|
||||
log "Stopping and removing containers"
|
||||
if [[ "${WITH_TLS}" == "true" ]]; then
|
||||
compose --profile tls down --remove-orphans
|
||||
else
|
||||
compose down --remove-orphans
|
||||
fi
|
||||
|
||||
if [[ "${PURGE_IMAGES}" == "true" ]]; then
|
||||
log "Removing docker images"
|
||||
docker image rm gitea/gitea:1.24.2 postgres:16-alpine caddy:2.10-alpine || true
|
||||
fi
|
||||
|
||||
if [[ "${PURGE_DATA}" == "true" ]]; then
|
||||
log "Purging persistent data under ${GITEA_BASE_PATH}"
|
||||
rm -rf "${GITEA_BASE_PATH}/gitea-data" "${GITEA_BASE_PATH}/postgres" "${GITEA_BASE_PATH}/caddy-data" "${GITEA_BASE_PATH}/caddy-config"
|
||||
fi
|
||||
|
||||
remove_firewall_rules "${CLOSE_PUBLIC_WEB}"
|
||||
|
||||
log "Uninstall complete"
|
||||
Reference in New Issue
Block a user