From 8022bd58343b9e425ab2ebe0a27c0f9daa053cca Mon Sep 17 00:00:00 2001 From: Vincent Mrose Date: Sat, 1 Aug 2026 14:17:37 +0200 Subject: [PATCH] Initial commit Drone CI steps, Dockerfile for ENV arguments and README.md --- .dockerignore | 7 ++ .drone.yml | 37 ++++++++ Dockerfile | 13 +++ README.md | 150 ++++++++++++++++++++++++++++++++ build-and-tag.ps1 | 11 +++ build-and-tag.sh | 10 +++ docker-compose.yml | 33 +++++++ templates/default.conf.template | 16 ++++ 8 files changed, 277 insertions(+) create mode 100644 .dockerignore create mode 100644 .drone.yml create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 build-and-tag.ps1 create mode 100644 build-and-tag.sh create mode 100644 docker-compose.yml create mode 100644 templates/default.conf.template diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..51a5990 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.git +.gitignore +.drone.yml +docker-compose.yml +README.md +build-and-tag.sh +build-and-tag.ps1 diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..39f8b73 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,37 @@ +kind: pipeline +type: docker +name: build-host-redirect-image + +steps: + - name: build-and-tag-host-image + image: docker:cli + volumes: + - name: dockersock + path: /var/run/docker.sock + commands: + - echo "Building redirect image on host Docker daemon..." + - docker build -t redirect:latest . + - | + if [ -n "$DRONE_COMMIT_SHA" ]; then + SHORT_SHA=$(echo $DRONE_COMMIT_SHA | cut -c1-7) + docker tag redirect:latest redirect:$SHORT_SHA + echo "Tagged redirect:$SHORT_SHA" + fi + - | + if [ -n "$DRONE_TAG" ]; then + docker tag redirect:latest redirect:$DRONE_TAG + echo "Tagged redirect:$DRONE_TAG" + fi + - echo "Build complete. Images registered on host Docker engine:" + - docker images | grep redirect || true + +volumes: + - name: dockersock + host: + path: /var/run/docker.sock + +trigger: + event: + - push + - tag + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6f7490e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM nginxinc/nginx-unprivileged:alpine-slim + +# Set default environment variables +ENV REDIRECT_TARGET="https://example.com" \ + REDIRECT_CODE="301" \ + LISTEN_PORT="8080" \ + NGINX_ENVSUBST_FILTER="REDIRECT_TARGET REDIRECT_CODE LISTEN_PORT" + +# Copy template for automatic envsubst rendering on startup +COPY templates/default.conf.template /etc/nginx/templates/default.conf.template + +# Default unprivileged port +EXPOSE 8080 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a127fa1 --- /dev/null +++ b/README.md @@ -0,0 +1,150 @@ +# Unprivileged Nginx Redirect Docker Image + +A lightweight, secure, and reusable Docker image based on `nginxinc/nginx-unprivileged:alpine-slim` designed to redirect HTTP requests to any configurable target domain/path with customizable HTTP status codes. + +It is designed to be built via **Drone CI** directly onto the host server's Docker daemon, allowing multiple containers to run on the host system using the single tagged image. + +--- + +## Features + +- **Ultra-Lightweight & Secure**: Based on `nginxinc/nginx-unprivileged:alpine-slim` (~12 MB), running as non-root user (UID 101). +- **Environment Variable Configuration**: Leverages native Nginx startup template rendering (`envsubst`). +- **Path & Query String Preservation**: Support for preserving request paths (e.g. `$request_uri`). +- **Built-in Health Checks**:`/healthz` endpoint returns `200 OK` for orchestrator probe support without triggering a redirect. +- **Drone CI Host Integration**: Pipeline builds directly onto the host machine's Docker engine via host Docker socket binding (`/var/run/docker.sock`). + +--- + +## Environment Variables + +| Variable | Default Value | Description | +| :--- | :--- | :--- | +| `REDIRECT_TARGET` | `https://example.com` | Target URL or domain for redirection. Add `$request_uri` to preserve path & query parameters. | +| `REDIRECT_CODE` | `301` | HTTP status code for redirection (e.g. `301`, `302`, `307`, `308`). | +| `LISTEN_PORT` | `8080` | Internal HTTP listening port (unprivileged standard). | + +--- + +## Drone CI Integration + +The included `.drone.yml` pipeline mounts the host Docker socket (`/var/run/docker.sock`) into a `docker:cli` container step. This builds and tags the image directly on the host host's Docker daemon. + +```yaml +kind: pipeline +type: docker +name: build-host-redirect-image + +steps: + - name: build-and-tag-host-image + image: docker:cli + volumes: + - name: dockersock + path: /var/run/docker.sock + commands: + - docker build -t redirect:latest . + - | + if [ -n "$DRONE_COMMIT_SHA" ]; then + SHORT_SHA=$(echo $DRONE_COMMIT_SHA | cut -c1-7) + docker tag redirect:latest redirect:$SHORT_SHA + fi +volumes: + - name: dockersock + host: + path: /var/run/docker.sock +``` + +> **Note**: Ensure your Drone runner environment allows mounting the host socket volume. + +### Tagging a Release with Git + +When you create and push a Git tag to your repository: + +```bash +# Create a tag locally +git tag v1.0.0 + +# Push the tag to your git remote +git push origin v1.0.0 +``` + +1. Git sends a `tag` webhook event to Drone CI. +2. Drone automatically populates `$DRONE_TAG` with the tag value (e.g. `v1.0.0`) and triggers the pipeline. +3. The pipeline builds and tags the host image as `redirect:v1.0.0` (as well as `redirect:latest` and `redirect:`). + +--- + +## Quick Start & Usage + +### 1. Manual Build (Local Testing) + +```bash +# Bash +./build-and-tag.sh redirect latest + +# PowerShell +.\build-and-tag.ps1 -ImageName redirect -Tag latest +``` + +### 2. Run Single Containers + +**Basic Domain Redirect (301 Permanent Redirect)** +```bash +docker run -d \ + --name redirect-domain \ + -p 8080:8080 \ + -e REDIRECT_TARGET="https://destination.com" \ + -e REDIRECT_CODE="301" \ + redirect:latest +``` + +**Path & Query Parameter Preserving Redirect (302 Found)** +```bash +docker run -d \ + --name redirect-path \ + -p 8081:8080 \ + -e REDIRECT_TARGET="https://destination.com\$request_uri" \ + -e REDIRECT_CODE="302" \ + redirect:latest +``` + +### 3. Multi-Container Setup with Docker Compose + +To deploy multiple redirect services side-by-side using the host-tagged image: + +```yaml +services: + site-a-redirect: + image: redirect:latest + container_name: site-a-redirect + ports: + - "8081:8080" + environment: + - REDIRECT_TARGET=https://site-a.com + - REDIRECT_CODE=301 + + site-b-redirect: + image: redirect:latest + container_name: site-b-redirect + ports: + - "8082:8080" + environment: + - REDIRECT_TARGET=https://site-b.com$request_uri + - REDIRECT_CODE=302 +``` + +Run compose stack: +```bash +docker compose up -d +``` + +--- + +## Health Check Endpoint + +All requests to `/healthz` bypass redirection and return an HTTP `200 OK` response with text body `OK`. + +```bash +curl http://localhost:8080/healthz +# Response: HTTP 200 OK -> "OK" +``` diff --git a/build-and-tag.ps1 b/build-and-tag.ps1 new file mode 100644 index 0000000..a3cb1a4 --- /dev/null +++ b/build-and-tag.ps1 @@ -0,0 +1,11 @@ +param ( + [string]$ImageName = "redirect", + [string]$Tag = "latest" +) + +$ErrorActionPreference = "Stop" + +Write-Host "Building Docker image: ${ImageName}:${Tag}" +docker build -t "${ImageName}:${Tag}" . + +Write-Host "Build complete. Image ${ImageName}:${Tag} is registered on host Docker engine." diff --git a/build-and-tag.sh b/build-and-tag.sh new file mode 100644 index 0000000..69d3e6b --- /dev/null +++ b/build-and-tag.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e + +IMAGE_NAME="${1:-redirect}" +TAG="${2:-latest}" + +echo "Building Docker image: ${IMAGE_NAME}:${TAG}" +docker build -t "${IMAGE_NAME}:${TAG}" . + +echo "Build complete. Image ${IMAGE_NAME}:${TAG} is registered on host Docker engine." diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..cf78f62 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +services: + # Example 1: Simple domain redirect (301 Permanent Redirect) + redirect-example: + image: redirect:latest + container_name: redirect-example + ports: + - "8081:8080" + environment: + - REDIRECT_TARGET=https://example.com + - REDIRECT_CODE=301 + restart: unless-stopped + + # Example 2: Redirect preserving full request path and query string (302 Temporary Redirect) + redirect-with-path: + image: redirect:latest + container_name: redirect-with-path + ports: + - "8082:8080" + environment: + - REDIRECT_TARGET=https://newsite.com$request_uri + - REDIRECT_CODE=302 + restart: unless-stopped + + # Example 3: Redirect to a specific subpath (307 Temporary Redirect) + redirect-subpath: + image: redirect:latest + container_name: redirect-subpath + ports: + - "8083:8080" + environment: + - REDIRECT_TARGET=https://example.com/landing-page + - REDIRECT_CODE=307 + restart: unless-stopped diff --git a/templates/default.conf.template b/templates/default.conf.template new file mode 100644 index 0000000..899ef7d --- /dev/null +++ b/templates/default.conf.template @@ -0,0 +1,16 @@ +server { + listen ${LISTEN_PORT}; + server_name _; + + # Health check endpoint for container orchestrator probes + location = /healthz { + access_log off; + default_type text/plain; + return 200 "OK\n"; + } + + # Redirect all other incoming traffic + location / { + return ${REDIRECT_CODE} ${REDIRECT_TARGET}; + } +}