diff --git a/.gitignore b/.gitignore index b5598bc7..dda43df9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ tmp database/infra/data +# --- [Caddy]: mtls +caddy/mtls/*.* +!caddy/mtls/.gitkeep + # --- [API]: Bin bin/* !bin/.gitkeep @@ -12,10 +16,10 @@ bin/* # --- [API]: Storage storage/logs/*.* storage/media/*.* +storage/logs/caddy/*.* storage/media/posts/*.* storage/media/users/*.* !storage/logs/.gitkeep -!storage/logs/caddy !storage/logs/caddy/.gitkeep !storage/media/.gitkeep !storage/media/posts/.gitkeep diff --git a/Makefile b/Makefile index bb12b8d5..b807a23e 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,7 @@ include ./metal/makefile/app.mk include ./metal/makefile/logs.mk include ./metal/makefile/build.mk include ./metal/makefile/infra.mk +include ./metal/makefile/caddy.mk # -------------------------------------------------------------------------------------------------------------------- # # -------------------------------------------------------------------------------------------------------------------- # @@ -96,4 +97,8 @@ help: @printf " $(BOLD)$(GREEN)supv:api:logs$(NC) : Show the the API service supervisor logs.\n" @printf " $(BOLD)$(GREEN)supv:api:logs-err$(NC): Show the the API service supervisor error logs.\n" + @printf "$(BOLD)$(BLUE)Caddy Commands:$(NC)\n" + @printf " $(BOLD)$(GREEN)caddy-gen-cert$(NC) : Generate the caddy's mtls certificates.\n" + @printf " $(BOLD)$(GREEN)caddy-validate$(NC) : Validates caddy's files syntax.\n" + @printf "$(NC)\n" diff --git a/caddy/Caddyfile.prod b/caddy/Caddyfile.prod index a9614925..741ff8c3 100644 --- a/caddy/Caddyfile.prod +++ b/caddy/Caddyfile.prod @@ -25,7 +25,13 @@ oullin.io { format json } - # API handler. + # --- API: block the protected path on the public listener. + @protected_public path /api/generate-signature* + handle @protected_public { + respond 403 + } + + # --- API handler. # - Reverse-proxy all requests to the Go API, forwarding Host + auth headers. # - to: Tell Caddy which upstream to send to. # - header_up: Preserve the original Host header. @@ -72,7 +78,7 @@ oullin.io { } } - # Default handler. + # --- Default handler. # - Route all other traffic to the Vue frontend app. # - `web_caddy_prod` is the Vue app's container name. # - source: https://github.com/oullin/web @@ -80,3 +86,27 @@ oullin.io { reverse_proxy web_caddy_prod:80 } } + +# INTERNAL mTLS entrypoint for the single protected path +:8443 { + tls internal { + client_auth { + mode require_and_verify + trust_pool file { + pem_file /etc/caddy/mtls/ca.pem + } + } + } + + encode gzip zstd + + # Only this path is reachable here + handle_path /api/generate-signature* { + reverse_proxy api:8080 + } + + # Everything else is denied on this listener. + handle { + respond 403 + } +} diff --git a/caddy/mtls/.gitkeep b/caddy/mtls/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docker-compose.yml b/docker-compose.yml index 36d0b62b..d01a7670 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,6 +49,7 @@ services: - caddy_config:/config - ./caddy/Caddyfile.prod:/etc/caddy/Caddyfile - ${CADDY_LOGS_PATH}:/var/log/caddy + - ./caddy/mtls/ca.pem:/etc/caddy/mtls/ca.pem:ro networks: - caddy_net diff --git a/metal/makefile/app.mk b/metal/makefile/app.mk index 9068bdeb..48a34d9b 100644 --- a/metal/makefile/app.mk +++ b/metal/makefile/app.mk @@ -1,8 +1,5 @@ .PHONY: fresh destroy audit watch format run-cli validate-caddy test-all run-cli-local -APP_CADDY_CONFIG_PROD_FILE ?= caddy/Caddyfile.prod -APP_CADDY_CONFIG_LOCAL_FILE ?= caddy/Caddyfile.local - format: gofmt -w -s . @@ -38,9 +35,6 @@ install-air: @echo "Installing air ..." @go install github.com/air-verse/air@latest -# ./database/infra/secrets/pg_username -# ./database/infra/secrets/pg_password -# ./database/infra/secrets/pg_dbname run-cli: @if [ -z "$(DB_SECRET_USERNAME)" ] || [ -z "$(DB_SECRET_PASSWORD)" ] || [ -z "$(DB_SECRET_DBNAME)" ]; then \ printf "\n$(RED)⚠️ Usage: make run-cli \n$(NC)"; \ @@ -61,13 +55,6 @@ run-cli: run-cli-local: make run-cli DB_SECRET_USERNAME=./database/infra/secrets/pg_username DB_SECRET_PASSWORD=./database/infra/secrets/pg_password DB_SECRET_DBNAME=./database/infra/secrets/pg_dbname + test-all: go test ./... - -# --- Mac: -# Needs to be locally installed: https://formulae.brew.sh/formula/caddy -validate-caddy: - caddy fmt --overwrite $(APP_CADDY_CONFIG_PROD_FILE) - caddy validate --config $(APP_CADDY_CONFIG_PROD_FILE) - caddy fmt --overwrite $(APP_CADDY_CONFIG_LOCAL_FILE) - caddy validate --config $(APP_CADDY_CONFIG_LOCAL_FILE) diff --git a/metal/makefile/caddy.mk b/metal/makefile/caddy.mk new file mode 100644 index 00000000..26c80801 --- /dev/null +++ b/metal/makefile/caddy.mk @@ -0,0 +1,21 @@ +.PHONY: caddy-gen-cert caddy-validate + +CADDY_MTLS_DIR = $(ROOT_PATH)/caddy/mtls +APP_CADDY_CONFIG_PROD_FILE ?= caddy/Caddyfile.prod +APP_CADDY_CONFIG_LOCAL_FILE ?= caddy/Caddyfile.local + +caddy-gen-cert: + openssl genrsa -out $(CADDY_MTLS_DIR)/ca.key 4096 + openssl req -x509 -new -nodes -key $(CADDY_MTLS_DIR)/ca.key -sha256 -days 3650 -subj "/CN=oullin-mtls-ca" -out $(CADDY_MTLS_DIR)/ca.pem + openssl rand -hex 16 | tr '[:lower:]' '[:upper:]' > $(CADDY_MTLS_DIR)/ca.srl + chmod 600 $(CADDY_MTLS_DIR)/ca.key + chmod 644 $(CADDY_MTLS_DIR)/ca.pem + chmod 644 $(CADDY_MTLS_DIR)/ca.srl + +# --- Mac: +# Needs to be locally installed: https://formulae.brew.sh/formula/caddy +caddy-validate: + caddy fmt --overwrite $(APP_CADDY_CONFIG_PROD_FILE) + caddy validate --config $(APP_CADDY_CONFIG_PROD_FILE) + caddy fmt --overwrite $(APP_CADDY_CONFIG_LOCAL_FILE) + caddy validate --config $(APP_CADDY_CONFIG_LOCAL_FILE)