diff --git a/Dockerfile.multiarch b/Dockerfile.multiarch index d9b9888..89e455b 100644 --- a/Dockerfile.multiarch +++ b/Dockerfile.multiarch @@ -12,53 +12,42 @@ ARG TARGETARCH ARG TARGETVARIANT ARG GOMPLATE_VERSION -ARG SUPERCRONIC_VERSION -ARG URL_PARSER_VERSION -ARG WAIT_FOR_VERSION ARG CONTAINER_LIBRARY # renovate: datasource=github-releases depName=hairyhenderson/gomplate ENV GOMPLATE_VERSION="${GOMPLATE_VERSION:-v3.11.5}" -# renovate: datasource=github-releases depName=aptible/supercronic -ENV SUPERCRONIC_VERSION="${SUPERCRONIC_VERSION:-v0.2.26}" -# renovate: datasource=github-releases depName=thegeeklab/url-parser -ENV URL_PARSER_VERSION="${URL_PARSER_VERSION:-v1.0.5}" -# renovate: datasource=github-releases depName=thegeeklab/wait-for -ENV WAIT_FOR_VERSION="${WAIT_FOR_VERSION:-v0.4.2}" # renovate: datasource=git-tags depName=https://gitea.rknet.org/docker/container-library ENV CONTAINER_LIBRARY="${CONTAINER_LIBRARY:-v0.1.3}" -RUN addgroup -g 101 -S nginx && \ - adduser -S -D -H -u 101 -h /var/www -s /sbin/nologin -G nginx -g nginx nginx && \ +RUN addgroup -g 1001 -S nginx && \ + adduser -S -D -H -u 1001 -h /var/www -s /sbin/nologin -G nginx -g nginx nginx && \ apk --update add --virtual .build-deps curl && \ - apk --update --no-cache add nginx ca-certificates && \ - rm -rf /var/www/localhost && \ + apk --update --no-cache add inotify-tools ca-certificates && \ + apk --update --no-cache add openresty --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing/ && \ rm -rf /etc/nginx/conf.d && \ curl -SsfL "https://gitea.rknet.org/docker/container-library/releases/download/${CONTAINER_LIBRARY}/container-library.tar.gz" | tar xz -C / && \ curl -SsfL -o /usr/local/bin/gomplate "https://github.com/hairyhenderson/gomplate/releases/download/${GOMPLATE_VERSION}/gomplate_${TARGETOS}-${TARGETARCH}${TARGETVARIANT}" && \ - curl -SsfL -o /usr/local/bin/supercronic "https://github.com/aptible/supercronic/releases/download/${SUPERCRONIC_VERSION}/supercronic-${TARGETOS}-${TARGETARCH}" && \ - curl -SsfL -o /usr/local/bin/url-parser "https://github.com/thegeeklab/url-parser/releases/download/${URL_PARSER_VERSION}/url-parser-${TARGETOS}-${TARGETARCH}${TARGETVARIANT//v/-}" && \ - curl -SsfL -o /usr/local/bin/wait-for "https://github.com/thegeeklab/wait-for/releases/download/${WAIT_FOR_VERSION}/wait-for" && \ chmod 755 /usr/local/bin/gomplate && \ - chmod 755 /usr/local/bin/supercronic && \ - chmod 755 /usr/local/bin/url-parser && \ - chmod 755 /usr/local/bin/wait-for && \ - touch /run/nginx.pid && \ - chown nginx /run/nginx.pid && \ - chown -R nginx /var/log/nginx && \ - mkdir -p /var/cache/nginx && \ - chown -R nginx /var/cache/nginx && \ - chmod -R 750 /var/cache/nginx && \ - chown -R nginx:nginx /var/www && \ - chmod -R 750 /var/www && \ apk del .build-deps && \ rm -rf /var/cache/apk/* && \ rm -rf /tmp/* ADD overlay/ / +RUN mkdir -p /var/www /etc/proxy-config /etc/nginx/conf.d /var/tmp/nginx /var/cache/nginx && \ + touch /run/nginx.pid && \ + touch /etc/nginx/conf.d/vhost.conf && \ + chown nginx /run/nginx.pid && \ + chown -R nginx /var/log/nginx /var/tmp/nginx /var/cache/nginx && \ + chown -R nginx:nginx /var/www && \ + chown -R root:nginx /etc/nginx /etc/nginx/conf.d && \ + chmod -R 640 /etc/nginx /etc/nginx/conf.d && \ + chmod 750 /var/www /var/cache/nginx /etc/proxy-config /etc/nginx /etc/nginx/conf.d + EXPOSE 8080 STOPSIGNAL SIGTERM -CMD ["nginx", "-g", "daemon off;"] +ENTRYPOINT ["/usr/local/bin/entrypoint", "server"] +WORKDIR /var/www +CMD [] diff --git a/README.md b/README.md index c7b2468..5ffe10e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# nginx +# nginx-s3 -Custom image for nginx HTTP server +Custom image for nginx (openresty) to proxy S3 buckets [![Build Status](https://img.shields.io/drone/build/docker/nginx?logo=drone&server=https%3A%2F%2Fdrone.rknet.org)](https://drone.rknet.org/docker/nginx) [![Docker Hub](https://img.shields.io/badge/dockerhub-latest-blue.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/thegeeklab/nginx) @@ -8,7 +8,7 @@ Custom image for nginx HTTP server [![Source: Gitea](https://img.shields.io/badge/source-gitea-blue.svg?logo=gitea&logoColor=white)](https://gitea.rknet.org/docker/nginx) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitea.rknet.org/docker/nginx/src/branch/main/LICENSE) -Custom rootless Docker base image for nginx based on Alpine. The pre-configured non-root user is a system user named `nginx` with the UID `101`. There is also a primary group with the same values. +Custom image for nginx (openresty) to proxy S3 buckets based on Alpine. The pre-configured non-root user is a system user named `nginx` with the UID `1001`. There is also a primary group with the same values. ## License diff --git a/overlay/etc/nginx/nginx.conf b/overlay/etc/nginx/nginx.conf index 2fe3efb..a102471 100644 --- a/overlay/etc/nginx/nginx.conf +++ b/overlay/etc/nginx/nginx.conf @@ -20,5 +20,10 @@ http { fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; - include /etc/nginx/vhost.conf; + map $request_uri $request_path { + default $request_uri; + ~/$ ${request_uri}index.html; + } + + include /etc/nginx/conf.d/vhost.conf; } diff --git a/overlay/etc/nginx/vhost.conf b/overlay/etc/nginx/vhost.conf deleted file mode 100644 index 6cdf91e..0000000 --- a/overlay/etc/nginx/vhost.conf +++ /dev/null @@ -1,16 +0,0 @@ -server { - listen 8080; - server_name localhost; - - location / { - root /var/lib/nginx/html; - index index.html index.htm; - } - - # redirect server error pages to the static page /50x.html - # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /var/lib/nginx/html; - } -} diff --git a/overlay/etc/templates/vhost.conf.tmpl b/overlay/etc/templates/vhost.conf.tmpl new file mode 100644 index 0000000..6164738 --- /dev/null +++ b/overlay/etc/templates/vhost.conf.tmpl @@ -0,0 +1,59 @@ +{{- $vhost := ds "vhost" }} +{{- $defauls := data.YAML "{hostnames: [localhost], proxy_ssl_protocols: TLSv1.2 TLSv1.3, proxy_hide_header: [X-Amz-*]}" -}} + +{{- range $vhost }} +{{- $this := coll.Merge . $defauls }} +{{- $upstream_host := index ($this.upstream | strings.Split ":") 0 }} +{{- $access_key_id := index $this "access_key_id" }} +{{- $secret_access_key := index $this "secret_access_key" -}} + +upstream backend_s3_{{ $this.bucket }} { + server {{ $this.upstream }}; +} + +server { + listen 8080; + server_name {{ conv.Join $this.hostnames " " }}; + + location / { + {{ if and $access_key_id $secret_access_key -}} + set_by_lua $now "return ngx.http_time(ngx.time())"; + set $string_to_sign "GET\n\n\n${now}\n/{{ $this.bucket }}/${repo}$request_path"; + set_hmac_sha1 $aws_signature "{{ $this.secret_access_key }}" "$string_to_sign"; + set_encode_base64 $aws_signature "$aws_signature"; + + proxy_set_header Date "$now"; + proxy_set_header Authorization "AWS {{ $this.access_key_id }}:$aws_signature"; + + {{ end -}} + + proxy_pass https://backend_s3_{{ $this.bucket }}/{{ $this.bucket }}/${repo}$uri; + proxy_ssl_name {{ $upstream_host }}; + proxy_ssl_server_name on; + proxy_ssl_verify on; + proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; + proxy_ssl_protocols {{ $this.proxy_ssl_protocols }}; + + proxy_http_version 1.1; + proxy_buffering off; + proxy_connect_timeout 300; + proxy_intercept_errors on; + port_in_redirect off; + + proxy_set_header Host "{{ $upstream_host }}"; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + {{ range $this.proxy_hide_header }} + more_clear_headers {{ . }}; + {{- end }} + + proxy_ignore_headers Set-Cookie; + + rewrite ^([^.]*[^/])$ $1/ permanent; + rewrite ^(.*)/$ $1/index.html break; + rewrite ^(.*/[^./]+)$ $1/index.html break; + } +} +{{- end -}} diff --git a/overlay/usr/local/bin/entrypoint b/overlay/usr/local/bin/entrypoint new file mode 100755 index 0000000..fbd703b --- /dev/null +++ b/overlay/usr/local/bin/entrypoint @@ -0,0 +1,57 @@ +#!/usr/bin/env sh + +# shellcheck disable=SC1091 +. /usr/local/lib/log.sh + +start_server() { + log_info "Start nginx server" + if ! nginx -q -g 'daemon off;' -t; then + log_error 'Nginx config validation failed, exit' + exit 1 + fi + + exec nginx -g "daemon off;" & + PID=$! + + { + while true; do + inotifywait -rq --timefmt "%F %T" --format "%T [INFO] [$(basename "$0")] %e %f" -e modify,move,create,delete /etc/nginx/ + log_info 'Detected nginx config update, run validation' + + if ! nginx -q -g 'daemon off;' -t; then + log_warn 'Nginx config validation failed, skip reload' + continue + fi + + log_info 'Reload nginx to apply config update' + kill -HUP $PID + done + } & + + wait $PID +} + +run_config() { + log_info "Start nginx config service" + /usr/local/bin/gomplate -d vhost=/etc/proxy-config/vhost.yml -o /etc/nginx/conf.d/vhost.conf -f /etc/templates/vhost.conf.tmpl --chmod "0640" + + while inotifywait -q --timefmt "%F %T" --format "%T [INFO] [$(basename "$0")] %e %f" -e modify,move,create,delete /etc/proxy-config/vhost.yml; do + log_info "Regenerate nginx config" + /usr/local/bin/gomplate -d vhost=/etc/proxy-config/vhost.yml -o /etc/nginx/conf.d/vhost.conf -f /etc/templates/vhost.conf.tmpl --chmod "0640" + done +} + +while [ $# -gt 0 ]; do + case "$1" in + server) + start_server + ;; + config) + run_config + ;; + *) + log_info "Unknown entrypoint option $1" >&2 + exit 1 + ;; + esac +done