Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40c58e8d9b | ||
|
|
00ba36191f | ||
|
|
2a18854894 | ||
|
|
c157c599ea | ||
|
|
810069d1fe | ||
|
|
e2a530045d | ||
|
|
9e69de9fd8 | ||
|
|
808187bef2 | ||
|
|
ae74124319 | ||
|
|
eefcee2ce3 | ||
|
|
d9cf96e594 | ||
|
|
b809a14e2b | ||
|
|
a0878cf30e | ||
|
|
f28aca8484 | ||
|
|
84495ae064 | ||
|
|
e8d04b8373 | ||
|
|
a4b9e5a040 | ||
|
|
9fc7c371c5 | ||
|
|
1ef8f861c1 | ||
|
|
3c5678f8a8 | ||
|
|
319c8fa922 | ||
|
|
b84843ca3d | ||
|
|
4b0b681302 | ||
|
|
2c6f20fbc3 | ||
|
|
b88828f92e | ||
|
|
09780d9598 | ||
|
|
c24ed01e0a | ||
|
|
7e1728a037 | ||
|
|
30ef38b143 | ||
|
|
f927bc191c | ||
|
|
25b6e0575a | ||
|
|
e133b01b00 |
@@ -0,0 +1,18 @@
|
||||
# Send only files required by the Hugo build and SWS runtime stages.
|
||||
*
|
||||
|
||||
!Dockerfile
|
||||
!go.mod
|
||||
!go.sum
|
||||
!sws.toml
|
||||
|
||||
!assets/
|
||||
!assets/**
|
||||
!config/
|
||||
!config/**
|
||||
!content/
|
||||
!content/**
|
||||
!layouts/
|
||||
!layouts/**
|
||||
!static/
|
||||
!static/**
|
||||
@@ -0,0 +1,135 @@
|
||||
name: Build Hugo
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
REGISTRY: tea.ahmza.com
|
||||
IMAGE_NAME: ${{ gitea.repository }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to Gitea Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ gitea.repository_owner }}
|
||||
password: ${{ secrets.GITEATOKEN }}
|
||||
|
||||
# QEMU deliberately omitted.
|
||||
#
|
||||
# Hugo runs on BUILDPLATFORM in the Dockerfile, so the static site is
|
||||
# built natively on the runner. The final amd64/arm64 stages only copy
|
||||
# the resulting static files into the target-platform SWS image.
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm64, linux/amd64
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:latest
|
||||
${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:${{ gitea.event.head_commit.id }}
|
||||
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:buildcache
|
||||
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:buildcache,mode=max
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- name: Pre-flight - verify VPN prerequisites
|
||||
# NetBird brings up a WireGuard interface, which needs a TUN device and
|
||||
# NET_ADMIN inside the job container. Without them, `netbird up` hangs
|
||||
# ~50s and dies with a cryptic "DeadlineExceeded". Check up front so the
|
||||
# failure is obvious and points at the fix (runner config.yaml).
|
||||
run: |
|
||||
echo "::group::NetBird prerequisites"
|
||||
if [ -e /dev/net/tun ]; then
|
||||
echo "OK: /dev/net/tun present"
|
||||
else
|
||||
echo "::error title=Missing TUN device::/dev/net/tun not found. Add --device=/dev/net/tun to the runner's config.yaml container.options."
|
||||
exit 1
|
||||
fi
|
||||
if command -v capsh >/dev/null 2>&1; then
|
||||
capsh --print | grep -q 'cap_net_admin' \
|
||||
&& echo "OK: NET_ADMIN present" \
|
||||
|| echo "::warning title=NET_ADMIN missing::netbird up will likely time out (DeadlineExceeded) configuring wt0. Add --cap-add=NET_ADMIN to the runner's config.yaml container.options."
|
||||
else
|
||||
echo "note: capsh unavailable, skipping NET_ADMIN check"
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Netbird Connect
|
||||
id: netbird
|
||||
timeout-minutes: 3
|
||||
uses: Alemiz112/netbird-connect@v1
|
||||
with:
|
||||
setup-key: ${{ secrets.NETBIRD_SETUP_KEY }}
|
||||
hostname: 'gitea-actions-blog'
|
||||
management-url: ${{ secrets.NETBIRD_URL }}
|
||||
|
||||
- name: NetBird status
|
||||
# Surface the actual peer/route state in the log and hard-assert that
|
||||
# the management connection is up before attempting any deploys.
|
||||
run: |
|
||||
echo "::group::netbird status"
|
||||
sudo netbird status --detail || true
|
||||
echo "::endgroup::"
|
||||
sudo netbird status | grep -q 'Management: Connected' \
|
||||
&& echo "NetBird management connected." \
|
||||
|| { echo "::error title=NetBird not connected::Management not connected after netbird up."; exit 1; }
|
||||
|
||||
- name: Deploy image to talos-prod
|
||||
uses: actions-hub/kubectl@v1.36.2
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
|
||||
with:
|
||||
args: >-
|
||||
-n blog-ahmza-com
|
||||
set image deployment/blog
|
||||
blog=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:${{ gitea.event.head_commit.id }}
|
||||
|
||||
- name: Wait for rollout
|
||||
uses: actions-hub/kubectl@v1.36.2
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
|
||||
with:
|
||||
args: >-
|
||||
-n blog-ahmza-com
|
||||
rollout status deployment/blog
|
||||
--timeout=5m
|
||||
|
||||
- name: Show deployed pods
|
||||
uses: actions-hub/kubectl@v1.36.2
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
|
||||
with:
|
||||
args: >-
|
||||
-n blog-ahmza-com
|
||||
get pods -o wide
|
||||
|
||||
- name: NetBird diagnostics & disconnect
|
||||
# Runs even on failure: captures final state for debugging and tears the
|
||||
# peer down so stale 'gitea-actions-blog' peers don't pile up.
|
||||
if: always()
|
||||
run: |
|
||||
echo "::group::final netbird status"
|
||||
sudo netbird status --detail || echo "netbird unavailable"
|
||||
echo "::endgroup::"
|
||||
sudo netbird down || true
|
||||
@@ -1,86 +0,0 @@
|
||||
name: Build Hugo
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
REGISTRY: tea.ahmza.com
|
||||
IMAGE_NAME: ${{ gitea.repository }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to Gitea Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ gitea.repository_owner }}
|
||||
password: ${{ secrets.GITEATOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm64, linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
BUILDKIT_CONTEXT_KEEP_GIT_DIR=true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:latest
|
||||
${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:${{ gitea.event.head_commit.id }}
|
||||
# Registry-backed build cache. The GitHub-hosted 'gha' cache is not
|
||||
# reachable from self-hosted Gitea runners, so store the cache as a
|
||||
# dedicated tag in the same registry instead.
|
||||
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:buildcache
|
||||
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ gitea.repository_owner }}/blog-ahmza-com:buildcache,mode=max
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
steps:
|
||||
- name: Netbird Connect
|
||||
uses: Alemiz112/netbird-connect@v1
|
||||
with:
|
||||
setup-key: ${{ secrets.NETBIRD_SETUP_KEY }}
|
||||
hostname: 'gitea-actions-blog'
|
||||
management-url: ${{ secrets.NETBIRD_URL }}
|
||||
|
||||
- name: Deploy to RPIC-Cluster
|
||||
# RPIC cluster is temporarily out of service. Don't let its failure
|
||||
# block the Gimli deploy or fail the whole job. Remove this line once
|
||||
# the cluster is back.
|
||||
continue-on-error: true
|
||||
uses: appleboy/ssh-action@v1
|
||||
with:
|
||||
host: ${{ secrets.RPIC_HOST }}
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
key: ${{ secrets.RPIC_KEY }}
|
||||
port: ${{ secrets.SSH_PORT }}
|
||||
script: 'kubectl rollout restart deployment blog -n blog-ahmza-com'
|
||||
|
||||
- name: Deploy to Gimli
|
||||
uses: appleboy/ssh-action@v1
|
||||
with:
|
||||
host: ${{ secrets.GIMLI_HOST }}
|
||||
username: ${{ secrets.SSH_USER }}
|
||||
key: ${{ secrets.GIMLI_KEY }}
|
||||
port: ${{ secrets.SSH_PORT }}
|
||||
script: 'docker compose down && docker compose pull && docker compose up -d'
|
||||
@@ -5,5 +5,4 @@ styles/
|
||||
node_modules
|
||||
public/
|
||||
resources/
|
||||
go.sum
|
||||
themes/
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# Caddyfile for the blog behind Cloudflare.
|
||||
#
|
||||
# Topology: Cloudflare (terminates TLS) -> Caddy (HTTP :80) -> SWS (blog:80)
|
||||
#
|
||||
# Cloudflare handles the public HTTPS cert, so Caddy serves plain HTTP on :80
|
||||
# and does NOT need the Cloudflare DNS-01 / automatic-HTTPS setup. Set the
|
||||
# Cloudflare SSL/TLS mode to "Full" so CF->origin is encrypted at the network
|
||||
# layer (e.g. over your tunnel/Netbird), even though this listener is HTTP.
|
||||
#
|
||||
# Copy this file to /etc/caddy/Caddyfile on the caddy_cf host and reload:
|
||||
# docker exec caddy_cf caddy reload --config /etc/caddy/Caddyfile
|
||||
# or restart the container.
|
||||
{
|
||||
# Trust only Cloudflare's edge IPs so real visitor IPs (Cf-Connecting-Ip /
|
||||
# X-Forwarded-For) are honoured only when the request actually comes from
|
||||
# Cloudflare. This prevents header spoofing from any other source.
|
||||
#
|
||||
# The `cloudflare` IP source (WeidiDeng/caddy-cloudflare-ip, baked into the
|
||||
# caddy-cf image) auto-fetches the ranges from cloudflare.com/ips-v4 and
|
||||
# /ips-v6 and refreshes them periodically, so there's no static list to
|
||||
# keep in sync.
|
||||
servers {
|
||||
trusted_proxies cloudflare {
|
||||
interval 12h
|
||||
timeout 15s
|
||||
}
|
||||
# Read the real visitor IP from these headers (only honoured for
|
||||
# requests from trusted_proxies above). Cf-Connecting-Ip is
|
||||
# Cloudflare's single authoritative end-user IP; X-Forwarded-For is
|
||||
# the fallback if a request ever arrives without the CF header.
|
||||
client_ip_headers Cf-Connecting-Ip X-Forwarded-For
|
||||
}
|
||||
}
|
||||
|
||||
# Bind to the hostname on the HTTP port only. Because the address has an
|
||||
# explicit ":80" scheme/port, Caddy will NOT try to provision a TLS cert.
|
||||
http://blog.ahmza.com {
|
||||
# THE responder: proxy every request to the blog (SWS) container.
|
||||
# This is what was missing before (requests fell through to a NOP -> blank page).
|
||||
reverse_proxy blog:80
|
||||
|
||||
# Compression happens here (SWS has it disabled on purpose).
|
||||
encode zstd gzip
|
||||
|
||||
# Security headers (single source of truth; SWS has security-headers off).
|
||||
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"
|
||||
# Drop Caddy's default Server header.
|
||||
-Server
|
||||
}
|
||||
|
||||
log {
|
||||
output stdout
|
||||
format console
|
||||
}
|
||||
}
|
||||
+15
-2
@@ -1,6 +1,19 @@
|
||||
FROM ghcr.io/hugomods/hugo:0.157.0 AS build
|
||||
# syntax=docker/dockerfile:1
|
||||
# Pin the build stage to the native build platform so Hugo runs once (never
|
||||
# under QEMU emulation). Hugo output is static HTML — identical across arches —
|
||||
# so there's no reason to run it per target arch. The runtime stage below stays
|
||||
# on TARGETPLATFORM, so multi-arch (amd64 + arm64) runtime images are preserved.
|
||||
FROM --platform=$BUILDPLATFORM ghcr.io/hugomods/hugo:0.165.0 AS build
|
||||
WORKDIR /tmp/hugo
|
||||
COPY . .
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
COPY config ./config
|
||||
RUN hugo mod graph
|
||||
|
||||
COPY assets ./assets
|
||||
COPY content ./content
|
||||
COPY layouts ./layouts
|
||||
COPY static ./static
|
||||
RUN hugo --gc --minify
|
||||
|
||||
FROM ghcr.io/static-web-server/static-web-server:2
|
||||
|
||||
@@ -50,5 +50,5 @@ sacrificing performance.
|
||||
The Blowfish theme is managed as a Hugo module, so no submodules are required.
|
||||
Clone the repository with:
|
||||
```bash
|
||||
git clone ssh://gitea@tea.ahmza.com:2222/ahmza/blog-ahmza-com.git
|
||||
git clone ssh://git@tea.ahmza.com/ahmza/blog-ahmza-com.git
|
||||
```
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 782 KiB After Width: | Height: | Size: 441 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 73 KiB |
@@ -1,5 +1,5 @@
|
||||
languageCode = "en"
|
||||
languageName = "English"
|
||||
locale = "en"
|
||||
label = "English"
|
||||
weight = 1
|
||||
title = "ahmza's ramblings"
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[[imports]]
|
||||
disable = false
|
||||
path = "github.com/nunocoracao/blowfish/v2"
|
||||
path = "github.com/nunocoracao/blowfish/v3"
|
||||
|
||||
@@ -1,15 +1,44 @@
|
||||
# Caddy as a Reverse Proxy
|
||||
|
||||
This document describes how to run [Caddy](https://caddyserver.com/) as a
|
||||
reverse proxy in front of the blog. It covers both deployment targets used by
|
||||
this project: the Kubernetes (Raspberry Pi) cluster and the Docker Compose host
|
||||
(Gimli).
|
||||
reverse proxy in front of the blog.
|
||||
|
||||
It uses the custom [`caddy-cf`](https://github.com/ahmzaa/caddy-cf) image
|
||||
(`ghcr.io/ahmzaa/caddy-cf:latest`) — a Caddy build with the **Cloudflare DNS
|
||||
module** baked in. The stock Caddy image cannot solve the ACME **DNS-01**
|
||||
challenge for Cloudflare; this custom build can, which is what lets us obtain
|
||||
certificates without exposing ports `80`/`443` to the public internet.
|
||||
module** baked in.
|
||||
|
||||
## Deployment topologies
|
||||
|
||||
There are two supported ways to run Caddy in front of the blog. **Pick one.**
|
||||
|
||||
### 1. Cloudflare in front (current production) — HTTP only
|
||||
|
||||
```
|
||||
Cloudflare (terminates TLS) -> Caddy (HTTP :80) -> SWS (blog:80)
|
||||
```
|
||||
|
||||
Cloudflare proxies the domain and provides the public HTTPS certificate, so
|
||||
Caddy only needs to serve plain **HTTP on :80** and reverse-proxy to the blog.
|
||||
Caddy does **not** provision a TLS cert in this mode. This is the live setup and
|
||||
its config is the committed [`Caddyfile`](../Caddyfile) at the repo root —
|
||||
see [Cloudflare-fronted setup](#cloudflare-fronted-setup-http-only) below.
|
||||
|
||||
Set the Cloudflare **SSL/TLS mode to "Full"** so the Cloudflare→origin hop is
|
||||
still encrypted at the network layer (e.g. over Netbird), even though Caddy's
|
||||
listener is HTTP.
|
||||
|
||||
### 2. Caddy terminates TLS via Cloudflare DNS-01
|
||||
|
||||
```
|
||||
Client -> Caddy (HTTPS :443, ACME DNS-01) -> SWS (blog:80)
|
||||
```
|
||||
|
||||
Caddy obtains/renews the certificate itself using the ACME **DNS-01** challenge
|
||||
through the Cloudflare API — no inbound `80`/`443` from the public internet is
|
||||
required, which suits a Netbird-only setup with no Cloudflare proxy in front.
|
||||
The stock Caddy image cannot do this; the `caddy-cf` build bundles the
|
||||
Cloudflare DNS provider plugin. See the Docker Compose / Kubernetes examples
|
||||
later in this document.
|
||||
|
||||
## Why a reverse proxy?
|
||||
|
||||
@@ -21,7 +50,7 @@ the reverse proxy so that each layer owns exactly one job:
|
||||
|
||||
| Concern | Owned by | Notes |
|
||||
| -------------------- | -------- | ------------------------------------------------- |
|
||||
| TLS / HTTPS certs | Caddy | Automatic certs via Cloudflare **DNS-01** challenge |
|
||||
| TLS / HTTPS certs | Cloudflare *or* Caddy | Cloudflare in topology 1; Caddy (DNS-01) in topology 2 |
|
||||
| Compression (gzip/zstd) | Caddy | `encode` directive; SWS compression is off |
|
||||
| Security headers | Caddy | Single source of truth, avoids duplicate headers |
|
||||
| Serving static files | SWS | Reads from `/public`, sets `Cache-Control`/`ETag` |
|
||||
@@ -50,11 +79,72 @@ Whatever the deployment, the SWS origin exposes:
|
||||
- **Health endpoint:** `GET /health` → `200 OK`
|
||||
- **Cache headers:** already set by SWS (Caddy should pass them through, not override)
|
||||
|
||||
Caddy only needs to terminate TLS (via Cloudflare DNS-01), add compression +
|
||||
Caddy only needs to handle TLS (or let Cloudflare do it), add compression +
|
||||
security headers, and forward requests to that origin.
|
||||
|
||||
---
|
||||
|
||||
## Cloudflare-fronted setup (HTTP only)
|
||||
|
||||
**This is the current production topology.** The canonical config is the
|
||||
committed [`Caddyfile`](../Caddyfile) at the repo root; copy it to the
|
||||
`caddy_cf` host and reload. Highlights:
|
||||
|
||||
- **`http://blog.ahmza.com`** — the explicit `http://` scheme binds Caddy to
|
||||
`:80` only and stops it from trying to provision a TLS certificate (Cloudflare
|
||||
already serves HTTPS to visitors).
|
||||
- **`reverse_proxy blog:80`** — proxies to the blog (SWS) container by its
|
||||
compose service name. Caddy and the blog container must share a Docker
|
||||
network. Omitting this responder makes Caddy return an empty `NOP` response —
|
||||
i.e. a **blank page**.
|
||||
- **`encode zstd gzip`** — compression happens here (SWS has it disabled).
|
||||
- **`header { ... }`** — security headers (SWS has them disabled).
|
||||
- **`trusted_proxies cloudflare { ... }`** + **`client_ip_headers
|
||||
Cf-Connecting-Ip X-Forwarded-For`** — so the real visitor IP is used for
|
||||
logging/matchers, but only when the request genuinely comes from Cloudflare
|
||||
(prevents header spoofing from any other source). The `cloudflare` IP source
|
||||
([WeidiDeng/caddy-cloudflare-ip](https://github.com/WeidiDeng/caddy-cloudflare-ip),
|
||||
baked into the `caddy-cf` image) auto-fetches the ranges from
|
||||
<https://www.cloudflare.com/ips-v4/> and <https://www.cloudflare.com/ips-v6/>
|
||||
and refreshes them on the configured `interval`, so there is no static list to
|
||||
maintain:
|
||||
|
||||
```caddy
|
||||
trusted_proxies cloudflare {
|
||||
interval 12h
|
||||
timeout 15s
|
||||
}
|
||||
```
|
||||
|
||||
`client_ip_headers` lists which header holds the real visitor IP:
|
||||
`Cf-Connecting-Ip` is Cloudflare's single authoritative end-user IP, with
|
||||
`X-Forwarded-For` as the fallback. It is only trusted for requests coming from
|
||||
the `trusted_proxies` above.
|
||||
|
||||
> If you ever run without the `caddy-cloudflare-ip` plugin, you can instead
|
||||
> pin the static ranges with `trusted_proxies static <ip/cidr ...>` copied
|
||||
> from the Cloudflare IP pages, but then you must keep them in sync manually.
|
||||
|
||||
Deploy and reload:
|
||||
|
||||
```bash
|
||||
# Put the Caddyfile in place on the caddy_cf host (e.g. into the ./conf mount),
|
||||
# then reload without downtime:
|
||||
docker exec caddy_cf caddy reload --config /etc/caddy/Caddyfile
|
||||
|
||||
# Verify from the host (expect HTTP 200, Content-Encoding: gzip, X-Frame-Options):
|
||||
curl -sI -H "Accept-Encoding: gzip" http://localhost/ | grep -iE 'HTTP|content-encoding|x-frame'
|
||||
```
|
||||
|
||||
> **Cloudflare SSL/TLS mode:** set it to **Full** so the Cloudflare→origin hop
|
||||
> is encrypted at the network layer (e.g. over Netbird), even though Caddy's
|
||||
> listener is plain HTTP.
|
||||
|
||||
---
|
||||
|
||||
The remaining sections describe **topology 2** (Caddy terminates TLS itself via
|
||||
the Cloudflare DNS-01 challenge) for setups without a Cloudflare proxy in front.
|
||||
|
||||
## Docker Compose (Gimli)
|
||||
|
||||
Run Caddy and the blog container on the same Docker network. Caddy reaches the
|
||||
|
||||
@@ -2,4 +2,4 @@ module github.com/ahmzaa/blog-ahmza-com
|
||||
|
||||
go 1.24.3
|
||||
|
||||
require github.com/nunocoracao/blowfish/v2 v2.86.0 // indirect
|
||||
require github.com/nunocoracao/blowfish/v3 v3.4.0
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
github.com/nunocoracao/blowfish/v3 v3.4.0 h1:Fv7mS97lagllTGzHA2V1H6+N5fuQd3DuWmOQlFFV4Aw=
|
||||
github.com/nunocoracao/blowfish/v3 v3.4.0/go.mod h1:8lJwR95VgcyWDhjKvUJMdOQXogrmhYSxkWh4ogSJfCg=
|
||||
@@ -1,6 +1,8 @@
|
||||
<br>
|
||||
<div class="flex items-center gap-x-5">
|
||||
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://mastodon.social/@ahmza">Mastodon</a>
|
||||
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://github.com/ahmzaa">Github</a>
|
||||
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://www.linkedin.com/in/ahmza/">Linkedin</a>
|
||||
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://github.com/ahmzaa">Github</a>
|
||||
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://gitlab.com/ahmza">Gitlab</a>
|
||||
<a class="text-sm text-neutral-500 dark:text-neutral-400" rel="me noopener noreferrer" target="_blank" href="https://tea.ahmza.com/ahmza">Gitea</a>
|
||||
</div>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
## Commit messages
|
||||
backend:
|
||||
name: github
|
||||
repo: ahmzaa/blog-ahmza-com
|
||||
branch: main
|
||||
squash_merges: true
|
||||
base_url: https://sveltia-cms-auth.hamzaahmed12328.workers.dev
|
||||
use_graphql: true
|
||||
commit_messages:
|
||||
create: Create {{collection}} "{{slug}}"
|
||||
update: Update {{collection}} "{{slug}}"
|
||||
delete: Delete {{collection}} "{{slug}}"
|
||||
uploadMedia: Upload "{{path}}"
|
||||
deleteMedia: Delete "{{path}}"
|
||||
openAuthoring: '{{message}}'
|
||||
|
||||
## Publish mode
|
||||
publish_mode: editorial_workflow # Allowed Values: simple, editorial_workflow
|
||||
|
||||
## Media and Public folders
|
||||
#Media
|
||||
media_folder: "assets/uploaded/img"
|
||||
public_folder: "/uploaded/img"
|
||||
|
||||
## SiteTitle
|
||||
site_url: https://blog.ahmza.com
|
||||
display_url: blog.ahmza.com
|
||||
logo_url: https://blog.ahmza.com/logo.webp
|
||||
|
||||
## Collections
|
||||
collections:
|
||||
- name: posts
|
||||
identifier_field: name
|
||||
label: posts
|
||||
label_singular: post
|
||||
icon: article
|
||||
description: sites main content, blog posts
|
||||
folder: "content/posts"
|
||||
path: "{{slug}}/index"
|
||||
media_folder: ""
|
||||
public_folder: ""
|
||||
create: true
|
||||
slug: '{{slug}}'
|
||||
fields:
|
||||
- {label: "Title", name: "title", widget: "string"}
|
||||
- {label: "Featured Image", name: "featureimage", widget: "image"}
|
||||
- {label: "Featured Image Caption", name: "featureimagecaption", widget: "string"}
|
||||
- {label: "Publish Date", name: "date", widget: "datetime", default: "{{now}}", date_format: "YYYY-MM-DD"}
|
||||
- {label: "Last Modified", name: "lastmod", widget: "datetime", default: "{{now}}", date_format: "YYYY-MM-DD"}
|
||||
- {label: "Author", name: "author", widget: "string", default: "ahmza"}
|
||||
- {label: "Draft", name: "draft", widget: "boolean", default: "false"}
|
||||
- {label: "Tags", name: "tags", widget: "list"}
|
||||
- {label: "Description", name: "description", widget: "string"}
|
||||
- {label: "Body", name: "body", widget: "markdown"}
|
||||
extension: md
|
||||
sortable_fields: ['title', 'date', 'lastmod', 'tags']
|
||||
view_filters:
|
||||
- label: "ahmza's posts"
|
||||
field: author
|
||||
pattern: 'ahmza'
|
||||
- label: Drafts
|
||||
field: draft
|
||||
pattern: true
|
||||
view_groups:
|
||||
- label: Year
|
||||
field: date
|
||||
pattern: \d{4}
|
||||
- label: Drafts
|
||||
field: draft
|
||||
@@ -1,20 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>Content Manager</title>
|
||||
<!-- Include the script that enables Netlify Identity on this page. -->
|
||||
</head>
|
||||
<body>
|
||||
<!-- Sveltia CMS, pinned to a specific version with Subresource Integrity.
|
||||
Bump the version + regenerate the integrity hash when upgrading:
|
||||
curl -sS https://cdn.jsdelivr.net/npm/@sveltia/cms@<ver>/dist/sveltia-cms.js \
|
||||
| openssl dgst -sha384 -binary | openssl base64 -A -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/@sveltia/cms@0.172.4/dist/sveltia-cms.js"
|
||||
integrity="sha384-Sj4Mfbg9OjjwG2ZE/YeUYu7xbZRTXGFg/wa/nszj3KzItKuRLEmGqSpV5C9YI2Ge"
|
||||
crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user