diff --git a/docker_ignis/ignis.Dockerfile b/docker_ignis/ignis.Dockerfile index e68b076..1bb0d16 100644 --- a/docker_ignis/ignis.Dockerfile +++ b/docker_ignis/ignis.Dockerfile @@ -5,7 +5,7 @@ ARG BASE_IMG="node" FROM ${BASE_NAMESPACE:+$BASE_NAMESPACE/}${BASE_IMG} LABEL maintainer="postmaster@labnow.ai" -LABEL com.thiefling.ignis.obsidian-version="1.12.7" +# LABEL com.thiefling.ignis.obsidian-version="1.12.7" ENV NODE_ENV=production ENV PORT=8080 @@ -15,29 +15,24 @@ ENV OBSIDIAN_ASSETS_PATH=/app/obsidian-app ENV PUID=1000 ENV PGID=1000 -# Install runtime dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates curl gosu \ - && rm -rf /var/lib/apt/lists/* - WORKDIR /app # Copy utility scripts COPY work /opt/ignis/ -# Clone and build ignis from source -RUN set -eux \ - && chmod +x /opt/ignis/*.sh \ - && git clone --depth 1 --branch main https://github.com/Nystik-gh/ignis.git . \ - && mv /opt/ignis/start-ignis.sh /app/ - -# Install build-time dependencies explicitly: NODE_ENV=production would otherwise omit esbuild. RUN set -eux \ - && npm install --include=dev --prefer-offline --no-audit --fetch-retries=5 \ - && npm run build \ - && chmod +x /app/apps/ignis-server/scripts/entrypoint.sh \ - && ln -sf /app/start-ignis.sh /usr/local/bin/ignis-server \ - && npm cache clean --force + && apt-get update && apt-get install -y --no-install-recommends ca-certificates curl gosu \ + ## Clone and build ignis from source + && chmod +x /opt/ignis/*.sh \ + && git clone --depth 1 --branch main https://github.com/Nystik-gh/ignis.git . \ + && mv /opt/ignis/start-ignis.sh /app/ \ + ## Install build-time dependencies explicitly: NODE_ENV=production would otherwise omit esbuild. + && npm install --include=dev --prefer-offline --no-audit --fetch-retries=5 \ + && npm run build \ + && chmod +x /app/apps/ignis-server/scripts/entrypoint.sh \ + && ln -sf /app/start-ignis.sh /usr/local/bin/ignis-server \ + && npm cache clean --force \ + && source /opt/utils/script-utils.sh && install__clean # Data volumes VOLUME ["/vaults", "/app/obsidian-app", "/app/data"] diff --git a/docker_openresty/README.md b/docker_openresty/README.md index 7a76717..78bc503 100644 --- a/docker_openresty/README.md +++ b/docker_openresty/README.md @@ -1,76 +1,94 @@ # OpenResty with Lua & acme.sh -`openresty` is a full-fledged web platform that integrates the standard Nginx core, LuaJIT, and acme.sh SSL certificate management tools. +`openresty` integrates the Nginx core, LuaJIT, and acme.sh SSL certificate management into a single web platform. --- -## 1. Port Configuration +## 1. Ports -- **`80` (HTTP)**: Serves standard HTTP proxy requests, acme-challenge routing, and redirect logic. -- **`443` (HTTPS)**: Serves SSL-terminated connections (configured inside custom proxy templates). +- **`80` (HTTP)** — HTTP proxying, ACME HTTP-01 challenges, and redirects. +- **`443` (HTTPS)** — SSL-terminated connections. --- -## 2. Data Persistence (Volumes) +## 2. Template Pipeline -To store custom configuration templates and SSL certificates, mount the following directories: +Nginx configuration is generated at container start from profile-selected templates. -- **`/var/log/nginx`**: Directory for access and error log outputs. -- **`/var/cache/nginx`**: Proxy cache directories. -- **`/etc/nginx/templates/`**: Directory containing `.template` files for environment variables substitutions. -- **`/etc/nginx/ssl`**: Stores certificates and private keys generated by acme.sh. -- **`/root/.acme.sh`**: Houses acme.sh configs, renewal certificates, and API secrets. +1. `PROFILE_NGINX` (optional) selects a list file at `/etc/nginx/profiles/.list`. +2. Each non-comment line names a flat template filename from `/etc/nginx/templates.repo/`: + - `*.conf.template` → HTTP output (`/etc/nginx/conf.d/`) + - `*.conf.stream-template` → stream output (`/etc/nginx/stream-conf.d/`) +3. `20-prepare-template-files.sh` copies the listed templates into the internal `/etc/nginx/templates/` staging directory (tmpfs, cleared each start). +4. `21-envsubst-on-templates.sh` renders them via `envsubst` into the output directories. + +Template variables (e.g. `SERVER_DOMAIN_NAME`, `SERVER_HOME_REDIRECT`) are supplied via `env_file`. Blank lines and `#` comments are ignored. When `PROFILE_NGINX` is unset, or the profile/source directories are empty, preparation is skipped and nginx starts with its default configuration. + +--- + +## 3. Resolver + +Set `NGINX_ENTRYPOINT_LOCAL_RESOLVERS=1` to write `resolver ... valid=10s ipv6=off;` into `/etc/nginx/conf.d/include/resolvers.conf`, populated from the container's DNS servers (fallback: Docker's embedded `127.0.0.11`). + +--- + +## 4. Volumes + +- **`/var/log/nginx`** — access/error logs. +- **`/var/cache/nginx`** — persistent proxy cache and client-body temp files; keep on disk, do not mount as tmpfs. +- **`/etc/nginx/ssl`** — acme.sh certificates and private keys. +- **`/root/.acme.sh`** — acme.sh config, renewals, and API credentials. +- **`/etc/nginx/profiles/`** — profile list files (`*.list`); may be empty. +- **`/etc/nginx/templates.repo/`** — template sources; may be empty. + +`/etc/nginx/templates/` is an internal tmpfs staging directory, not a user-facing mount. --- -## 3. How to apply for certificates using ACME.sh +## 5. ACME Certificates + +Log into the running container to inspect existing certificates: -Log into the running container to view existing certificates: ```bash -docker exec -it svc-proxy-openresty bash +docker exec -it svc-openresty bash cd /etc/nginx/ssl && ls -alh ``` -Choose your validation method: - ### Method A: HTTP-01 Validation -Requires public endpoint accessibility and nginx challenge configuration files. + +Requires public endpoint accessibility and nginx challenge configuration. + ```bash -# Set your DOMAINS environment variable DOMAINS='a1.example.com a2.example.com a3.example.com' - -# Or if you already have certs in this folder, run the command below to get a list of DOMAINS +# or, if certificates already exist: # DOMAINS=$(printf "%s\n" *.crt *.key 2>/dev/null | sed 's/\.[^.]*$//' | sort -u) -# Apply for certs using HTTP-01: /opt/utils/script-acme-sh.sh 'your@email.com' "${DOMAINS}" ``` ### Method B: DNS-01 Validation (Recommended for Wildcards) -Does not require exposed HTTP ports. Requires [`acme.sh` supported DNS service provider](https://github.com/acmesh-official/acme.sh/wiki/dnsapi) API token (e.g. Cloudflare): - -e.g.: the `CF_Token` and `dns_cf` below is for [Cloudflare](https://github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cf). +Requires an [`acme.sh`-supported DNS provider](https://github.com/acmesh-official/acme.sh/wiki/dnsapi) API token; no exposed HTTP ports needed. The `CF_Token` and `dns_cf` below are for [Cloudflare](https://github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cf). ```bash export CF_Token='your-cloudflare-api-token' -# define variable to apply cert for multiple domains in a same cert file (the one without wild-card goes first): +# non-wildcard domain goes first: DOMAINS='example.com *.example.com' -# Apply for certs using DNS-01: /opt/utils/script-acme-sh.sh 'your@email.com' "${DOMAINS}" "dns_cf" ``` --- -## 4. Custom Configurations +## 6. Custom Configurations -- Refer to [source code](https://github.com/NginxProxyManager/nginx-proxy-manager/tree/develop/docker/rootfs/etc/nginx/conf.d) and [docs](https://nginxproxymanager.com/advanced-config/#custom-nginx-configurations) of [Nginx Proxy Manager](https://nginxproxymanager.com/). +See [Nginx Proxy Manager](https://nginxproxymanager.com/) [docs](https://nginxproxymanager.com/advanced-config/#custom-nginx-configurations). +Optional snippets included by `nginx.conf` (each uses the `[.]` glob, so a missing file is ignored): You can add custom config snippets to extend OpenResty routing: -- `/data/nginx/custom/root_top.conf`: Included at the top of nginx.conf +- `/etc/nginx/custom/root_top.conf`: Included at the top of nginx.conf - `conf/root.conf`: Included at the very end of nginx.conf - `conf/http_top.conf`: Included at the top of the main http block - `conf/http.conf`: Included at the end of the main http block @@ -82,9 +100,10 @@ You can add custom config snippets to extend OpenResty routing: - `conf/server_stream_tcp.conf`: Included at the end of every TCP stream server block - `conf/server_stream_udp.conf`: Included at the end of every UDP stream server block - `/data/nginx/custom/server_dead.conf`: Included at the end of every 404 server block -- -## Log fields mappings +--- + +## Log Field Mappings | JSON key | standard | proxy | stream | | -------- | ------------------------- | ------------------------- | -------------------------- | diff --git a/docker_openresty/compose/docker-compose.openresty.yml b/docker_openresty/compose/docker-compose.openresty.yml new file mode 100644 index 0000000..93a22a5 --- /dev/null +++ b/docker_openresty/compose/docker-compose.openresty.yml @@ -0,0 +1,55 @@ +# Namespace the project with PROFILE_ENV so multiple instances can coexist, +# consistent with the rest of this repo. Example: +# PROFILE_ENV=X docker compose -f docker-compose.openresty.yml up -d +name: ${PROFILE_ENV:-X}-svc-openresty + +networks: + net-ailab-proxy: + external: true + + +services: + svc-openresty: + container_name: ${PROFILE_ENV:-X}-svc-openresty + hostname: svc-openresty + image: quay.io/labnow/openresty:latest + pull_policy: always + restart: unless-stopped + networks: ["net-ailab-proxy"] + ports: ["80:80", "443:443"] + + # Template variables come from env_file. PROFILE_NGINX is optional: when it is unset or empty, + # 20-prepare-template-files.sh skips template preparation and nginx starts without any rendered templates. + # SERVER_DOMAIN_NAME / SERVER_HOME_REDIRECT are only used by the selected profile's templates. + env_file: ["../credentials/${PROFILE_ENV:-@}-openresty.env"] + + environment: + TZ: ${TZ:-Asia/Shanghai} + PROFILE_LOCALIZE: aliyun-pub + # Enable 15-local-resolvers.envsh to export NGINX_LOCAL_RESOLVERS for 16-set-resolvers.sh to write the resolver config. + NGINX_ENTRYPOINT_LOCAL_RESOLVERS: "1" + + volumes: + # - /var/cache/nginx:/var/cache/nginx:rw + - /data/storage/nginx/ssl:/etc/nginx/ssl:rw + - /data/storage/nginx/web:/var/nginx/web:ro + - /data/storage/log/nginx:/var/log/nginx:rw + + # Internal staging directory of the template pipeline. + # Mounted as tmpfs so it is always empty at container start and never leaks stale templates across restarts. + - type: tmpfs + target: /etc/nginx/templates + + # Optional custom config (e.g. http_top.conf, http.conf, stream.conf). + # An empty directory is ignored and nginx falls back to its defaults. + - ./nginx-custom.d:/etc/nginx/custom:ro + + # Template pipeline (nginx still starts when these directories are empty): + # ./profiles/ -> /etc/nginx/profiles/ profile lists (*.list) + # ./nginx-conf.d-template/ -> /etc/nginx/templates.repo/ template sources (*.conf.template / *.conf.stream-template) + # The in-container defaults already match these mount points, so PROFILE_DIR_NGINX / DIR_TEMPLATE_SOURCE do not need to be set. + # Each non-comment line names a flat template filename from `/etc/nginx/templates.repo/`: + # - `*.conf.template` → HTTP output (`/etc/nginx/conf.d/`) + # - `*.conf.stream-template` → stream output (`/etc/nginx/stream-conf.d/`) + - ./profiles/:/etc/nginx/profiles/:ro + - ./nginx-templates.repo.d:/etc/nginx/templates.repo:ro diff --git a/docker_openresty/openresty.Dockerfile b/docker_openresty/openresty.Dockerfile index 719f2ca..f2ca2a1 100644 --- a/docker_openresty/openresty.Dockerfile +++ b/docker_openresty/openresty.Dockerfile @@ -14,11 +14,11 @@ COPY work /opt/utils/ RUN set -eux \ && chmod +x /opt/utils/*.sh \ - # ----------------------------- Install acme.sh - && source /opt/utils/script-setup-acme.sh && setup_acme \ - # ----------------------------- Install lua and lua-rocks + ## ----------------------------- Install acme.sh + && source /opt/utils/script-setup-acme.sh && setup_acme \ + ## ----------------------------- Install lua and lua-rocks && source /opt/utils/script-setup-core.sh && setup_lua_base && setup_lua_rocks \ - # ----------------------------- Install openresty + ## ----------------------------- Install openresty && useradd nginx -G www-data \ && mkdir -pv /var/cache/nginx /var/log/nginx \ && chown -R nginx:www-data /var/cache/nginx /var/log/nginx \ @@ -29,7 +29,7 @@ RUN set -eux \ && nginx -t \ && install__clean -VOLUME ["/var/log/nginx", "/var/cache/nginx", "/etc/nginx/templates/", "/etc/nginx/ssl", "/root/.acme.sh"] +VOLUME ["/var/log/nginx", "/var/cache/nginx", "/etc/nginx/ssl", "/root/.acme.sh"] ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["nginx"] diff --git a/docker_openresty/work/entrypoint/docker-entrypoint.d/16-set-resolvers.sh b/docker_openresty/work/entrypoint/docker-entrypoint.d/16-set-resolvers.sh new file mode 100644 index 0000000..51ac55e --- /dev/null +++ b/docker_openresty/work/entrypoint/docker-entrypoint.d/16-set-resolvers.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +ME=$(basename "$0") +RESOLVERS_FILE=/etc/nginx/conf.d/include/resolvers.conf + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +# 15-local-resolvers.envsh export NGINX_LOCAL_RESOLVERS (like "127.0.0.11 8.8.8.8") +# when NGINX_ENTRYPOINT_LOCAL_RESOLVERS is not set or empty, fallback to Docker's embedded DNS. +RESOLVERS="${NGINX_LOCAL_RESOLVERS:-127.0.0.11}" + +mkdir -pv "$(dirname "$RESOLVERS_FILE")" + +touch "$RESOLVERS_FILE" 2>/dev/null || { + entrypoint_log "$ME: info: cannot write $RESOLVERS_FILE (read-only file system?)" + exit 0 +} + +{ + echo "# Generated by $ME on $(date)" + echo "resolver ${RESOLVERS} valid=10s ipv6=off;" +} > "$RESOLVERS_FILE" + +entrypoint_log "$ME: wrote resolver ${RESOLVERS} to $RESOLVERS_FILE" diff --git a/docker_openresty/work/entrypoint/docker-entrypoint.d/20-prepare-template-files.sh b/docker_openresty/work/entrypoint/docker-entrypoint.d/20-prepare-template-files.sh new file mode 100644 index 0000000..4875aaf --- /dev/null +++ b/docker_openresty/work/entrypoint/docker-entrypoint.d/20-prepare-template-files.sh @@ -0,0 +1,88 @@ +#!/bin/sh +# vim:sw=2:ts=2:sts=2:et + +set -eu + +LC_ALL=C +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +ME=$(basename "$0") + +DIR_PROFILE="${PROFILE_DIR_NGINX:-/etc/nginx/profiles}" +DIR_SOURCE="${DIR_TEMPLATE_SOURCE:-/etc/nginx/templates.repo}" +DIR_TARGET="${NGINX_ENVSUBST_TEMPLATE_DIR:-/etc/nginx/templates}" +PROFILE="${PROFILE_NGINX:-}" + +entrypoint_log() { + if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then + echo "$@" + fi +} + +# when profile is not set, skip template preparation +if [ -z "$PROFILE" ]; then + entrypoint_log "$ME: PROFILE_NGINX not set, skipping template preparation" + exit 0 +fi + +case "$PROFILE" in + *[!a-zA-Z0-9._-]*) + entrypoint_log "$ME: invalid PROFILE_NGINX '$PROFILE', skipping" + exit 0 + ;; +esac + +FILE_PROFILE="${DIR_PROFILE}/${PROFILE}.list" + +if [ ! -f "$FILE_PROFILE" ]; then + entrypoint_log "$ME: profile file not found: $FILE_PROFILE, skipping" + exit 0 +fi + +if [ ! -d "$DIR_SOURCE" ]; then + entrypoint_log "$ME: template source dir not found: $DIR_SOURCE, skipping" + exit 0 +fi + +mkdir -p "$DIR_TARGET" +find "$DIR_TARGET" -mindepth 1 -maxdepth 1 -type f -delete + +entrypoint_log "$ME: preparing templates for profile '$PROFILE'" + +count=0 +while IFS= read -r line || [ -n "$line" ]; do + line=$(printf '%s' "$line" | tr -d '\r') + line="${line%%#*}" # remove comments + line=$(printf '%s' "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + + if [ -z "$line" ]; then + continue + fi + + case "$line" in + *.conf.template|*.conf.stream-template) ;; + *) + entrypoint_log "$ME: skipping invalid template name: $line" + continue + ;; + esac + + case "$line" in + */*|*..*) + entrypoint_log "$ME: skipping invalid template path: $line" + continue + ;; + esac + + src="${DIR_SOURCE}/${line}" + if [ ! -f "$src" ]; then + entrypoint_log "$ME: skipping missing template: $src" + continue + fi + + cp "$src" "${DIR_TARGET}/${line}" + count=$((count + 1)) +done < "$FILE_PROFILE" + +entrypoint_log "$ME: prepared $count template(s)" +exit 0 diff --git a/docker_openresty/work/entrypoint/docker-entrypoint.d/20-envsubst-on-templates.sh b/docker_openresty/work/entrypoint/docker-entrypoint.d/21-envsubst-on-templates.sh similarity index 100% rename from docker_openresty/work/entrypoint/docker-entrypoint.d/20-envsubst-on-templates.sh rename to docker_openresty/work/entrypoint/docker-entrypoint.d/21-envsubst-on-templates.sh diff --git a/docker_openresty/work/nginx/conf.d/include/resolvers.conf b/docker_openresty/work/nginx/conf.d/include/resolvers.conf new file mode 100644 index 0000000..7a41a72 --- /dev/null +++ b/docker_openresty/work/nginx/conf.d/include/resolvers.conf @@ -0,0 +1 @@ +# resolver configuration is generated by /docker-entrypoint.d/16-set-resolvers.sh at container start diff --git a/docker_openresty/work/nginx/nginx.conf b/docker_openresty/work/nginx/nginx.conf index 0bf13c8..d1a5632 100644 --- a/docker_openresty/work/nginx/nginx.conf +++ b/docker_openresty/work/nginx/nginx.conf @@ -35,6 +35,7 @@ http { } include /etc/nginx/conf.d/include/log-standard.conf; + include /etc/nginx/conf.d/include/resolvers.conf; include /etc/nginx/mime.types; default_type application/octet-stream;