diff --git a/deploy/Caddyfile b/deploy/Caddyfile new file mode 100644 index 0000000..228b3ac --- /dev/null +++ b/deploy/Caddyfile @@ -0,0 +1,4 @@ +# Caddy obtains and renews the Let's Encrypt certificate and redirects HTTP to HTTPS automatically. +git.yebilly.me { + reverse_proxy gitea:3000 +} diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..dc349ba --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,131 @@ +# Gitea Deployment (Assignment 4 / Instructor Manual Chapter 2) + +Pinned Gitea (`gitea/gitea:1.27.3`, SQLite) behind Caddy (`caddy:2.11.4-alpine`, +automatic Let's Encrypt HTTPS) on the DigitalOcean droplet **The-Ocean** +(Ubuntu 24.04, 1 vCPU, 512 MB). No secrets live in this folder. + +Hostname: `git.yebilly.me` (set in `docker-compose.yml` and `Caddyfile`). +Save the output of every step marked +**evidence** (screenshots or copied terminal text) for the IT manual. + +## 0. Prerequisites (done in web consoles) + +1. **Domain:** claim a GitHub Student Developer Pack domain. Record the offer, + eligibility, first-year cost, renewal cost, and expiry date. **evidence** +2. **DNS:** add an `A` record `git` -> droplet IPv4 at the registrar. **evidence** + (DNS screen), then from a laptop: + `nslookup git.yebilly.me 1.1.1.1` **evidence** +3. **DigitalOcean Cloud Firewall** (`Networking -> Firewalls`), name + `gitea-fw`, applied to The-Ocean: + - Inbound `TCP 443` All IPv4 + All IPv6 + - Inbound `TCP 80` All IPv4 + All IPv6 (redirect + certificate validation) + - Inbound `TCP 22` **team public IPs only** (add these first and confirm SSH + still works before removing any wider SSH rule) + - Outbound: leave default (all) so apt, Docker Hub, DNS, NTP, and Let's + Encrypt remain reachable. + **evidence** (rules screen + date) + +## 1. Prepare the droplet + +```bash +docker stop color-buttons-app && docker rm color-buttons-app # frees port 80 +# 512 MB RAM: add 1 GB swap +fallocate -l 1G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile +echo '/swapfile none swap sw 0 0' >> /etc/fstab +docker --version && docker compose version # evidence +``` + +From the laptop, copy this folder: + +```bash +scp -r gitea root@:~/gitea +``` + +## 2. Start the stack + +```bash +cd ~/gitea +docker compose up -d +docker compose ps # evidence: only caddy shows 0.0.0.0:80/443 +docker compose logs caddy | grep -i certificate # evidence: certificate obtained +``` + +Open `https://git.yebilly.me`, keep **SQLite3** on the install page, create +the admin account. Store its password in a password manager, never in Git or +the manual. + +## 3. HTTPS evidence + +```bash +curl -sI http://git.yebilly.me # expect 308 + Location: https://... +curl -vI https://git.yebilly.me 2>&1 | grep -Ei "issuer|expire|subject|HTTP/" +``` + +Also screenshot the browser certificate viewer (issuer, expiry). **evidence** + +## 4. Renewal test (forced reissue) + +Caddy renews automatically about 30 days before expiry and has no dry-run +command, so force a fresh issuance: + +```bash +date -u +docker compose exec caddy rm -rf /data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/git.yebilly.me +docker compose restart caddy +docker compose logs --since 2m caddy | grep -i certificate # evidence +curl -vI https://git.yebilly.me 2>&1 | grep -Ei "issuer|expire" # new dates +``` + +Do this once; repeated reissues can hit Let's Encrypt rate limits. + +## 5. Persistence test + +In Gitea, create a repository and an issue; note the repo name and issue +number. Then: + +```bash +docker ps --filter name=gitea --format '{{.ID}} {{.Image}} {{.CreatedAt}}' # before +docker compose up -d --force-recreate --no-deps gitea +docker ps --filter name=gitea --format '{{.ID}} {{.Image}} {{.CreatedAt}}' # after: new ID +docker compose exec gitea ls -l /data/gitea/gitea.db # SQLite file +docker volume inspect gitea_gitea-data --format '{{.Mountpoint}}' +``` + +Reload the repo and issue in the browser. **evidence** (before/after +screenshots). The named volume outlives the container, so data persists. This +is not a backup or restore test. + +## 6. External exposure test + +Run from a laptop, **not** the droplet. Record source network, time, expected +and actual result. + +```powershell +Test-NetConnection git.yebilly.me -Port 443 # expect True +Test-NetConnection git.yebilly.me -Port 80 # expect True (redirects) +Test-NetConnection git.yebilly.me -Port 22 # True from approved IP only +Test-NetConnection git.yebilly.me -Port 3000 # expect False +``` + +Repeat the port 22 check from a non-approved network (phone hotspot): expect +`False`. Supporting on-droplet view (not proof of public exposure): + +```bash +ss -tlnp +docker compose ps +``` + +## 7. Team workflow (Gitea UI) + +Create the organization and product repository with a README, add at least two +members with roles, open at least five issues (product and operations), and for +two issues: branch -> pull request with `Closes #N` -> peer review -> merge -> +issue closed. **evidence** (links/screenshots) + +## Operate + +```bash +docker compose restart gitea # restart +docker compose logs -f gitea caddy # troubleshoot +docker compose pull && docker compose up -d # after bumping pinned tags +``` diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..a8eddc4 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,39 @@ +# Gitea behind Caddy (automatic Let's Encrypt HTTPS) on the The-Ocean droplet. +# Only Caddy publishes ports; Gitea is reachable solely on the private "web" network. +services: + gitea: + image: gitea/gitea:1.27.3 + restart: unless-stopped + environment: + - USER_UID=1000 + - USER_GID=1000 + - GITEA__database__DB_TYPE=sqlite3 # file: /data/gitea/gitea.db + - GITEA__server__DOMAIN=git.yebilly.me + - GITEA__server__ROOT_URL=https://git.yebilly.me/ + - GITEA__server__HTTP_PORT=3000 + - GITEA__server__DISABLE_SSH=true # Git over HTTPS only + volumes: + - gitea-data:/data + networks: [web] + # ponytail: no ports: on purpose -- Caddy reaches gitea:3000 privately. + + caddy: + image: caddy:2.11.4-alpine + restart: unless-stopped + ports: + - "80:80" # HTTP->HTTPS redirect + ACME HTTP-01 validation + - "443:443" # HTTPS + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy-data:/data # certificates + ACME account + - caddy-config:/config + networks: [web] + depends_on: [gitea] + +volumes: + gitea-data: + caddy-data: + caddy-config: + +networks: + web: