├── CODEOWNERS ├── .gitattributes ├── modsec └── main.conf ├── docker-compose.yml ├── hooks └── build ├── nginx.patch ├── LICENSE ├── h3.nginx.conf ├── .github └── workflows │ ├── snyk-container.yml │ └── ci.yml ├── nginx.conf ├── CODE_OF_CONDUCT.md ├── README.md └── Dockerfile /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @patrikjuvonen 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | nginx.conf linguist-language=Nginx 2 | *.nginx.conf linguist-language=Nginx 3 | -------------------------------------------------------------------------------- /modsec/main.conf: -------------------------------------------------------------------------------- 1 | Include /etc/nginx/modsec/modsecurity.conf 2 | Include /usr/local/share/coreruleset/crs-setup.conf 3 | Include /usr/local/share/coreruleset/rules/*.conf 4 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | app: 5 | build: 6 | context: ./ 7 | dockerfile: Dockerfile 8 | image: patrikjuvonen/docker-nginx-http3 9 | ports: 10 | - 80:80 11 | - 443:443 12 | - 443:443/udp 13 | -------------------------------------------------------------------------------- /hooks/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # $IMAGE_NAME var is injected into the build so the tag is correct. 4 | 5 | echo "Build hook running..." 6 | 7 | BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") 8 | VCS_REF=$(git rev-parse --short HEAD) 9 | 10 | echo "BUILD_DATE: ${BUILD_DATE}" 11 | echo "VCS_REF: ${VCS_REF}" 12 | 13 | printenv 14 | 15 | docker build --build-arg BUILD_DATE=${BUILD_DATE} \ 16 | --build-arg VCS_REF=${VCS_REF} \ 17 | -t $IMAGE_NAME . 18 | -------------------------------------------------------------------------------- /nginx.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/http/v3/ngx_http_v3.c b/src/http/v3/ngx_http_v3.c 2 | index 77f04f1..b438e9e 100644 3 | --- a/src/http/v3/ngx_http_v3.c 4 | +++ b/src/http/v3/ngx_http_v3.c 5 | @@ -300,7 +300,7 @@ ngx_http_v3_process_headers(ngx_connection_t *c, quiche_h3_event *ev, 6 | return; 7 | } 8 | 9 | - stream->in_closed = !quiche_h3_event_headers_has_body(ev); 10 | + stream->in_closed = !quiche_h3_event_headers_has_more_frames(ev); 11 | 12 | ngx_http_v3_run_request(stream->request); 13 | } 14 | @@ -462,9 +462,6 @@ ngx_http_v3_handler(ngx_connection_t *c) 15 | case QUICHE_H3_EVENT_PRIORITY_UPDATE: 16 | break; 17 | 18 | - case QUICHE_H3_EVENT_DATAGRAM: 19 | - break; 20 | - 21 | case QUICHE_H3_EVENT_GOAWAY: 22 | break; 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 RanadeepPolavarapu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /h3.nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | return 301 https://$host$request_uri; 6 | } 7 | 8 | # HTTPS server 9 | # 10 | server { 11 | # Enable QUIC and HTTP/3. 12 | listen 443 quic reuseport; 13 | # Ensure that HTTP/2 is enabled for the server 14 | listen 443 ssl http2; 15 | server_name localhost; 16 | 17 | http2_push_preload on; 18 | 19 | gzip on; 20 | gzip_http_version 1.1; 21 | gzip_vary on; 22 | gzip_comp_level 6; 23 | gzip_proxied any; 24 | gzip_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component; 25 | gzip_buffers 16 8k; 26 | gzip_min_length 1024; 27 | gzip_disable msie6; 28 | 29 | brotli on; 30 | brotli_types text/plain text/css application/json application/javascript application/x-javascript text/javascript; 31 | brotli_comp_level 6; 32 | 33 | # Enable TLS versions (TLSv1.3 is required for QUIC). 34 | ssl_protocols TLSv1.2 TLSv1.3; 35 | 36 | ssl_certificate /etc/ssl/localhost.pem; 37 | ssl_certificate_key /etc/ssl/private/localhost.key; 38 | ssl_trusted_certificate /etc/ssl/localhost.pem; 39 | 40 | ssl_session_cache shared:SSL:1m; 41 | ssl_session_timeout 5m; 42 | 43 | # Enable TLSv1.3's 0-RTT. Use $ssl_early_data when reverse proxying to 44 | # prevent replay attacks. 45 | # 46 | # @see: http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_early_data 47 | ssl_early_data on; 48 | ssl_ciphers HIGH:!aNULL:!MD5; 49 | ssl_prefer_server_ciphers on; 50 | 51 | # Add Alt-Svc header to negotiate HTTP/3. 52 | add_header alt-svc 'h2=":443"; ma=86400, h3-29=":443"; ma=86400, h3=":443"; ma=86400' always; 53 | # Debug 0-RTT. 54 | add_header X-Early-Data $tls1_3_early_data; 55 | 56 | add_header x-frame-options "deny"; 57 | add_header Strict-Transport-Security "max-age=31536000" always; 58 | 59 | root /usr/share/nginx/html; 60 | index index.html index.htm; 61 | } 62 | 63 | map $ssl_early_data $tls1_3_early_data { 64 | "~." $ssl_early_data; 65 | default ""; 66 | } 67 | -------------------------------------------------------------------------------- /.github/workflows/snyk-container.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | # A sample workflow which checks out the code, builds a container 7 | # image using Docker and scans that image for vulnerabilities using 8 | # Snyk. The results are then uploaded to GitHub Security Code Scanning 9 | # 10 | # For more examples, including how to limit scans to only high-severity 11 | # issues, monitor images for newly disclosed vulnerabilities in Snyk and 12 | # fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ 13 | 14 | name: Snyk Container 15 | 16 | on: 17 | push: 18 | branches: [ "master" ] 19 | pull_request: 20 | # The branches below must be a subset of the branches above 21 | branches: [ "master" ] 22 | schedule: 23 | - cron: '26 9 * * 3' 24 | workflow_dispatch: 25 | 26 | permissions: 27 | contents: read 28 | 29 | jobs: 30 | snyk: 31 | permissions: 32 | contents: read # for actions/checkout to fetch code 33 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v4 37 | - name: Build a Docker image 38 | run: docker buildx build -t patrikjuvonen/docker-nginx-http3:snyk-ci --build-arg BUILD_DATE=${{ env.BUILD_DATE }} --build-arg VCS_REF=${{ env.VCS_REF }} --build-arg GITHUB_REF=${{ github.ref }} --build-arg GITHUB_RUN_ID=${{ github.run_id }} --build-arg GITHUB_RUN_NUMBER=${{ github.run_number }} --build-arg GITHUB_RUN_ATTEMPT=${{ github.run_attempt }} . 39 | - name: Run Snyk to check Docker image for vulnerabilities 40 | # Snyk can be used to break the build when it detects vulnerabilities. 41 | # In this case we want to upload the issues to GitHub Code Scanning 42 | continue-on-error: true 43 | uses: snyk/actions/docker@cdb760004ba9ea4d525f2e043745dfe85bb9077e 44 | env: 45 | # In order to use the Snyk Action you will need to have a Snyk API token. 46 | # More details in https://github.com/snyk/actions#getting-your-snyk-token 47 | # or you can signup for free at https://snyk.io/login 48 | SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} 49 | with: 50 | image: patrikjuvonen/docker-nginx-http3:snyk-ci 51 | args: --file=Dockerfile 52 | # Patch for https://github.com/github/codeql-action/issues/2187 53 | - name: Replace null security-severity for license-related findings 54 | run: | 55 | sed -i 's/"security-severity": "null"/"security-severity": "0"/g' snyk.sarif 56 | - name: Upload result to GitHub Code Scanning 57 | uses: github/codeql-action/upload-sarif@v3 58 | with: 59 | sarif_file: snyk.sarif 60 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | 3 | pid /var/run/nginx.pid; 4 | 5 | ################################################################################## 6 | # nginx.conf Performance Tuning: https://github.com/denji/nginx-tuning 7 | ################################################################################## 8 | 9 | # you must set worker processes based on your CPU cores, nginx does not benefit from setting more than that 10 | worker_processes auto; #some last versions calculate it automatically 11 | 12 | # number of file descriptors used for nginx 13 | # the limit for the maximum FDs on the server is usually set by the OS. 14 | # if you don't set FD's then OS settings will be used which is by default 2000 15 | worker_rlimit_nofile 100000; 16 | 17 | # only log critical errors 18 | error_log /var/log/nginx/error.log crit; 19 | 20 | # provides the configuration file context in which the directives that affect connection processing are specified. 21 | events { 22 | # determines how much clients will be served per worker 23 | # max clients = worker_connections * worker_processes 24 | # max clients is also limited by the number of socket connections available on the system (~64k) 25 | worker_connections 4000; 26 | 27 | # optmized to serve many clients with each thread, essential for linux -- for testing environment 28 | use epoll; 29 | 30 | # accept as many connections as possible, may flood worker connections if set too low -- for testing environment 31 | multi_accept on; 32 | } 33 | 34 | http { 35 | include /etc/nginx/mime.types; 36 | default_type application/octet-stream; 37 | 38 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 39 | '$status $body_bytes_sent "$http_referer" ' 40 | '"$http_user_agent" "$http_x_forwarded_for"'; 41 | 42 | # cache informations about FDs, frequently accessed files 43 | # can boost performance, but you need to test those values 44 | open_file_cache max=200000 inactive=20s; 45 | open_file_cache_valid 30s; 46 | open_file_cache_min_uses 2; 47 | open_file_cache_errors on; 48 | 49 | # to boost I/O on HDD we can disable access logs 50 | access_log off; 51 | 52 | # copies data between one FD and other from within the kernel 53 | # faster then read() + write() 54 | sendfile on; 55 | 56 | # send headers in one peace, its better then sending them one by one 57 | tcp_nopush on; 58 | 59 | # don't buffer data sent, good for small data bursts in real time 60 | tcp_nodelay on; 61 | 62 | # reduce the data that needs to be sent over network -- for testing environment 63 | gzip on; 64 | gzip_min_length 10240; 65 | gzip_proxied expired no-cache no-store private auth; 66 | gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/json application/xml; 67 | gzip_disable msie6; 68 | 69 | # allow the server to close connection on non responding client, this will free up memory 70 | reset_timedout_connection on; 71 | 72 | # request timed out -- default 60 73 | client_body_timeout 10; 74 | 75 | # if client stop responding, free up memory -- default 60 76 | send_timeout 2; 77 | 78 | # server will close connection after this time -- default 75 79 | keepalive_timeout 30; 80 | 81 | # number of requests client can make over keep-alive -- for testing environment 82 | keepalive_requests 100000; 83 | 84 | ######################################### 85 | # Just For Security Reason 86 | ######################################### 87 | 88 | # Security reasons, turn off nginx versions 89 | server_tokens off; 90 | 91 | more_clear_headers Server; # Custom module: headers-more-nginx-module (https://github.com/openresty/headers-more-nginx-module) 92 | 93 | ######################################### 94 | # ModSecurity 95 | ######################################### 96 | 97 | modsecurity on; 98 | modsecurity_rules_file /etc/nginx/modsec/main.conf; 99 | 100 | # ######################################### 101 | # # NGINX Simple DDoS Defense 102 | # ######################################### 103 | 104 | # limit the number of connections per single IP 105 | limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m; 106 | 107 | # limit the number of requests for a given session 108 | limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s; 109 | 110 | # zone which we want to limit by upper values, we want limit whole server 111 | server { 112 | limit_conn conn_limit_per_ip 10; 113 | limit_req zone=req_limit_per_ip burst=10 nodelay; 114 | } 115 | 116 | # if the request body size is more than the buffer size, then the entire (or partial) 117 | # request body is written into a temporary file 118 | client_body_buffer_size 128k; 119 | 120 | # headerbuffer size for the request header from client -- for testing environment 121 | client_header_buffer_size 3m; 122 | 123 | # maximum number and size of buffers for large headers to read from client request 124 | large_client_header_buffers 4 256k; 125 | 126 | # read timeout for the request body from client -- for testing environment 127 | # client_body_timeout 3m; 128 | 129 | # how long to wait for the client to send a request header -- for testing environment 130 | client_header_timeout 3m; 131 | 132 | include /etc/nginx/conf.d/*.conf; 133 | } 134 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | - Demonstrating empathy and kindness toward other people 21 | - Being respectful of differing opinions, viewpoints, and experiences 22 | - Giving and gracefully accepting constructive feedback 23 | - Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | - Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | - The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | - Trolling, insulting or derogatory comments, and personal or political attacks 33 | - Public or private harassment 34 | - Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | - Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | 22572159+patrikjuvonen@users.noreply.github.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | tags: 8 | - "v*.*.*" 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | runs-on: ${{ matrix.platform.os }} 14 | 15 | timeout-minutes: 100 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | platform: 21 | - os: ubuntu-latest 22 | platform: linux/amd64 23 | - os: ubuntu-latest 24 | platform: linux/arm/v6 25 | - os: ubuntu-latest 26 | platform: linux/arm/v7 27 | - os: ubuntu-24.04-arm 28 | platform: linux/arm64 29 | 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v4 33 | 34 | - name: Set environment variables 35 | run: | 36 | echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV 37 | echo "VCS_REF=$(git rev-parse --short HEAD)" >> $GITHUB_ENV 38 | 39 | - name: Docker meta 40 | id: meta 41 | uses: docker/metadata-action@v5 42 | with: 43 | flavor: | 44 | latest=false 45 | images: | 46 | ${{ secrets.DOCKERHUB_USERNAME }}/docker-nginx-http3 47 | ghcr.io/${{ github.repository_owner }}/docker-nginx-http3 48 | 49 | - name: Set up QEMU 50 | if: ${{ (matrix.platform.platform == 'linux/arm/v6') || matrix.platform.platform == 'linux/arm/v7' }} 51 | uses: docker/setup-qemu-action@v3 52 | 53 | - name: Set up Docker Buildx 54 | uses: docker/setup-buildx-action@v3 55 | 56 | - name: Login to Docker Hub 57 | if: github.event_name != 'pull_request' 58 | uses: docker/login-action@v3 59 | with: 60 | username: ${{ secrets.DOCKERHUB_USERNAME }} 61 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 62 | 63 | - name: Login to GitHub Container Registry 64 | if: github.event_name != 'pull_request' 65 | uses: docker/login-action@v3 66 | with: 67 | registry: ghcr.io 68 | username: ${{ github.repository_owner }} 69 | password: ${{ secrets.GITHUB_TOKEN }} 70 | 71 | - name: Build and push 72 | id: build 73 | uses: docker/build-push-action@v6 74 | with: 75 | context: . 76 | platforms: ${{ matrix.platform.platform }} 77 | push: ${{ github.event_name != 'pull_request' }} 78 | labels: ${{ steps.meta.outputs.labels }} 79 | outputs: type=image,"name=${{ secrets.DOCKERHUB_USERNAME }}/docker-nginx-http3,ghcr.io/${{ github.repository_owner }}/docker-nginx-http3",push-by-digest=true,name-canonical=true,push=true 80 | cache-from: type=gha,scope=${{ github.repository }}-${{ github.ref_name }}-${{ matrix.platform.platform }} 81 | cache-to: type=gha,scope=${{ github.repository }}-${{ github.ref_name }}-${{ matrix.platform.platform }},mode=max 82 | build-args: | 83 | BUILD_DATE=${{ env.BUILD_DATE }} 84 | VCS_REF=${{ env.VCS_REF }} 85 | GITHUB_REF=${{ github.ref }} 86 | GITHUB_RUN_ID=${{ github.run_id }} 87 | GITHUB_RUN_NUMBER=${{ github.run_number }} 88 | GITHUB_RUN_ATTEMPT=${{ github.run_attempt }} 89 | 90 | - name: Export digest 91 | run: | 92 | mkdir -p ${{ runner.temp }}/digests 93 | digest="${{ steps.build.outputs.digest }}" 94 | touch "${{ runner.temp }}/digests/${digest#sha256:}" 95 | 96 | - name: Platform slug 97 | id: platform-slug 98 | run: echo "platform=${{ matrix.platform.platform }}" | tr '/' '-' >> $GITHUB_OUTPUT 99 | 100 | - name: Upload digest 101 | uses: actions/upload-artifact@v4 102 | with: 103 | name: digests-${{ steps.platform-slug.outputs.platform }} 104 | path: ${{ runner.temp }}/digests/* 105 | if-no-files-found: error 106 | retention-days: 1 107 | 108 | merge: 109 | runs-on: ubuntu-latest 110 | 111 | timeout-minutes: 10 112 | 113 | needs: 114 | - build 115 | 116 | steps: 117 | - name: Download digests 118 | uses: actions/download-artifact@v4 119 | with: 120 | path: ${{ runner.temp }}/digests 121 | pattern: digests-* 122 | merge-multiple: true 123 | 124 | - name: Set up Docker Buildx 125 | uses: docker/setup-buildx-action@v3 126 | 127 | - name: Docker meta 128 | id: meta 129 | uses: docker/metadata-action@v5 130 | with: 131 | flavor: | 132 | latest=false 133 | images: | 134 | ${{ secrets.DOCKERHUB_USERNAME }}/docker-nginx-http3 135 | ghcr.io/${{ github.repository_owner }}/docker-nginx-http3 136 | tags: | 137 | type=schedule 138 | type=ref,event=branch 139 | type=ref,event=tag 140 | type=semver,pattern={{version}} 141 | type=semver,pattern={{major}}.{{minor}} 142 | type=semver,pattern={{major}} 143 | type=sha,prefix= 144 | type=sha,prefix=,format=long 145 | 146 | - name: Login to Docker Hub 147 | if: github.event_name != 'pull_request' 148 | uses: docker/login-action@v3 149 | with: 150 | username: ${{ secrets.DOCKERHUB_USERNAME }} 151 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 152 | 153 | - name: Login to GitHub Container Registry 154 | if: github.event_name != 'pull_request' 155 | uses: docker/login-action@v3 156 | with: 157 | registry: ghcr.io 158 | username: ${{ github.repository_owner }} 159 | password: ${{ secrets.GITHUB_TOKEN }} 160 | 161 | - name: Create manifest list and push 162 | if: github.event_name != 'pull_request' 163 | working-directory: ${{ runner.temp }}/digests 164 | run: | 165 | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ 166 | $(printf '${{ secrets.DOCKERHUB_USERNAME }}/docker-nginx-http3@sha256:%s ' *) 167 | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ 168 | $(printf 'ghcr.io/${{ github.repository_owner }}/docker-nginx-http3@sha256:%s ' *) 169 | 170 | - name: Inspect images 171 | run: | 172 | docker buildx imagetools inspect ${{ secrets.DOCKERHUB_USERNAME }}/docker-nginx-http3:${{ steps.meta.outputs.version }} 173 | docker buildx imagetools inspect ghcr.io/${{ github.repository_owner }}/docker-nginx-http3:${{ steps.meta.outputs.version }} 174 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-nginx-http3 2 | 3 | [![Docker Pulls](https://img.shields.io/docker/pulls/patrikjuvonen/docker-nginx-http3?color=brightgreen)](https://hub.docker.com/r/patrikjuvonen/docker-nginx-http3) 4 | ![MIT License](https://img.shields.io/github/license/patrikjuvonen/docker-nginx-http3) 5 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](code_of_conduct.md) 6 | [![Build Status](https://github.com/patrikjuvonen/docker-nginx-http3/actions/workflows/ci.yml/badge.svg?event=push)](https://github.com/patrikjuvonen/docker-nginx-http3/actions/workflows/ci.yml?event=push) 7 | [![Arch](https://img.shields.io/badge/docker%20arch-linux%2Famd64-blue)](https://hub.docker.com/r/patrikjuvonen/docker-nginx-http3/tags) 8 | [![Arch](https://img.shields.io/badge/docker%20arch-linux%2Farm64-blue)](https://hub.docker.com/r/patrikjuvonen/docker-nginx-http3/tags) 9 | [![Arch](https://img.shields.io/badge/docker%20arch-linux%2Farm%2Fv7-blue)](https://hub.docker.com/r/patrikjuvonen/docker-nginx-http3/tags) 10 | [![Arch](https://img.shields.io/badge/docker%20arch-linux%2Farm%2Fv6-blue)](https://hub.docker.com/r/patrikjuvonen/docker-nginx-http3/tags) 11 | 12 | Alpine Linux image with nginx `1.23.4` (mainline) with HTTP/3 (QUIC), TLSv1.3, 13 | 0-RTT, HPACK, brotli, NJS, Cookie-Flag, headers, ModSecurity with coreruleset 14 | and BoringSSL with OCSP support. 15 | 16 | Total size is only about ~47 MB uncompressed. 17 | 18 | This is a fork of 19 | [ranadeeppolavarapu/docker-nginx-http3](https://github.com/ranadeeppolavarapu/docker-nginx-http3). 20 | Thanks to him for doing the ground work. 21 | 22 | Special in this fork: 23 | 24 | - [ModSecurity for nginx](https://github.com/SpiderLabs/ModSecurity-nginx) 25 | (SpiderLabs) with [coreruleset](https://github.com/coreruleset/coreruleset/) 26 | - HPACK enabled and nginx quiche patch by [kn007/patch](https://github.com/kn007/patch/) 27 | - BoringSSL OCSP enabled with [kn007/patch](https://github.com/kn007/patch/) 28 | - Removed nginx debug build 29 | 30 | HTTP/3 support provided from the smart people at 31 | [Cloudflare](https://cloudflare.com) with the 32 | [cloudflare/quiche](https://github.com/cloudflare/quiche) project. 33 | 34 | Images for this are available on 35 | [Docker Hub](https://hub.docker.com/r/patrikjuvonen/docker-nginx-http3) and 36 | [GHCR](https://github.com/patrikjuvonen/docker-nginx-http3/pkgs/container/docker-nginx-http3). 37 | 38 | ## Usage 39 | 40 | **Docker Hub:** `docker pull patrikjuvonen/docker-nginx-http3` 41 | 42 | **GitHub Container Registry (GHCR):** 43 | `docker pull ghcr.io/patrikjuvonen/docker-nginx-http3` 44 | 45 | Semantic versioning is enabled since [519e20d7f65d53b976cf7d13e364dca326e988b7](https://github.com/patrikjuvonen/docker-nginx-http3/commit/519e20d7f65d53b976cf7d13e364dca326e988b7), 46 | the first semantic version being 2.0.0. You can use a semantical version using tags 47 | such as `:x.y.z`, `:x.y`, `:x`. I also provide a `latest` tag which is the latest 48 | release, and `master` which is the latest image from master branch. 49 | 50 | This is a base image like the default _nginx_ image. It is meant to be used as a 51 | drop-in replacement for the nginx base image. 52 | 53 | Best practice example Nginx configs are available in this repo. See 54 | [_nginx.conf_](nginx.conf) and [_h3.nginx.conf_](h3.nginx.conf). 55 | 56 | Example: 57 | 58 | ```Dockerfile 59 | # Base Nginx HTTP/3 Image 60 | FROM patrikjuvonen/docker-nginx-http3:latest 61 | 62 | # Copy your certs. 63 | COPY localhost.key /etc/ssl/private/ 64 | COPY localhost.pem /etc/ssl/ 65 | 66 | # Copy your configs. 67 | COPY nginx.conf /etc/nginx/ 68 | COPY h3.nginx.conf /etc/nginx/conf.d/ 69 | ``` 70 | 71 | H3 runs over UDP so, you will need to port map both TCP and UDP. Ex: 72 | `docker run -p 80:80 -p 443:443/tcp -p 443:443/udp ...` 73 | 74 | **NOTE**: Please note that you need a valid 75 | [CA](https://en.wikipedia.org/wiki/Certificate_authority) signed certificate for 76 | the client to upgrade you to HTTP/3. [Let's Encrypt](https://letsencrypt.org/) 77 | is a option for getting a free valid CA signed certificate. 78 | 79 | ## Contributing 80 | 81 | Contributions are welcome. Please feel free to contribute 😊. 82 | 83 | ## Features 84 | 85 | - HTTP/3 (QUIC) via Cloudflare's quiche 86 | - HTTP/2 (with Server Push) 87 | - HTTP/2 88 | - BoringSSL (Google's flavor of OpenSSL) 89 | - TLS 1.3 **with 0-RTT support** 90 | - [HPACK](https://blog.cloudflare.com/hpack-the-silent-killer-feature-of-http-2/) 91 | - Brotli compression 92 | - [headers-more-nginx-module](https://github.com/openresty/headers-more-nginx-module) 93 | - [NJS](https://www.nginx.com/blog/introduction-nginscript/) 94 | - [nginx_cookie_flag_module](https://www.nginx.com/products/nginx/modules/cookie-flag/) 95 | - PCRE latest with 96 | [JIT compilation](http://nginx.org/en/docs/ngx_core_module.html#pcre_jit) 97 | enabled 98 | - zlib latest 99 | - Alpine Linux (total size of **12 MB** compressed) 100 | 101 | ### In this fork 102 | 103 | - [ModSecurity for nginx](https://github.com/SpiderLabs/ModSecurity-nginx) 104 | (SpiderLabs) with [coreruleset](https://github.com/coreruleset/coreruleset/) 105 | - HPACK enabled and nginx quiche patch by [kn007/patch](https://github.com/kn007/patch/) 106 | - BoringSSL OCSP enabled with [kn007/patch](https://github.com/kn007/patch/) 107 | - Removed nginx debug build 108 | 109 | ## Future Additions 110 | 111 | Possible additions in the future pending IETF spec approvals. 112 | 113 | - [Facebook's zstd over the web](https://tools.ietf.org/html/rfc8478) 114 | 115 | ## HTTP/3 ENABLED! 116 | 117 | Using Chrome Canary with the following CLI flags: 118 | 119 | ```bash 120 | --flag-switches-begin --enable-quic --quic-version=h3-29 --enable-features=EnableTLS13EarlyData --flag-switches-end 121 | ``` 122 | 123 | Run on Mac OS (**darwin**): 124 | 125 | ```bash 126 | "/Applications/Google Chrome Canary.app Contents/MacOS/Google Chrome Canary" \ 127 | --flag-switches-begin \ 128 | --enable-quic \ 129 | --quic-version=h3-29 \ 130 | --enable-features=EnableTLS13EarlyData \ 131 | --flag-switches-end 132 | ``` 133 | 134 | Windows: 135 | 136 | ![Windows Chrome Canary](https://user-images.githubusercontent.com/13495525/68124347-21b9d380-ff4a-11e9-9963-e1102762c466.JPG) 137 | 138 | ### HTTP/3 (QUIC) Proof 139 | 140 | Since HTTP/3 is experimental, we have to be sensible with it. Therefore, below 141 | is HTTP/3 in production on one of my web apps 🙃. 142 | 143 | ![h3](https://user-images.githubusercontent.com/7084995/67162952-831d5800-f337-11e9-9297-05241a693cc4.png) 144 | 145 | ## HTTP/2 with Server Push 146 | 147 | ![alt](https://user-images.githubusercontent.com/7084995/67162942-654ff300-f337-11e9-9dc0-6d7a915d517c.png) 148 | 149 | ## TLS v1.3 150 | 151 | ![ssllabs](https://user-images.githubusercontent.com/7084995/67164526-89b4cb00-f349-11e9-87a2-d2dc81610ed4.png) 152 | 153 | ### 0-RTT Proof 154 | 155 | ![tls-0-rtt](https://user-images.githubusercontent.com/7084995/67163692-08a50600-f340-11e9-830c-c8a11c824a1f.png) 156 | 157 | ### Testing 0-RTT 158 | 159 | ```bash 160 | host=domain.example.com # Replace your domain. 161 | echo -e "GET / HTTP/1.1\r\nHost: $host\r\nConnection: close\r\n\r\n" > request.txt 162 | openssl s_client -connect $host:443 -tls1_3 -sess_out session.pem -ign_eof < request.txt 163 | openssl s_client -connect $host:443 -tls1_3 -sess_in session.pem -early_data request.txt 164 | ``` 165 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ################################################## 2 | # Nginx with Quiche (HTTP/3), Brotli, Headers More 3 | # and ModSec modules. 4 | ################################################## 5 | # This is a fork of: 6 | # github.com/ranadeeppolavarapu/docker-nginx-http3 7 | # 8 | # Special in this fork: 9 | # - ModSecurity for nginx (SpiderLabs) with 10 | # coreruleset 11 | # - HPACK enabled and nginx quiche patch by 12 | # kn007/patch 13 | # - BoringSSL OCSP enabled with kn007/patch 14 | # - Removed nginx debug build 15 | # 16 | # Thanks to ranadeeppolavarapu/docker-nginx-http3 17 | # for doing the ground work! 18 | ################################################## 19 | 20 | FROM alpine:edge AS deps 21 | 22 | ARG NGINX_VERSION=1.23.4 23 | ARG QUICHE_CHECKOUT=28cb72b7c6a1f134f4d2e2f36ed04a81e113a0a6 24 | ARG MODSEC_TAG=v3/master 25 | ARG MODSEC_NGX_TAG=master 26 | ARG NJS_TAG=0.9.0 27 | 28 | ARG BUILD_DATE 29 | ARG VCS_REF 30 | ARG GITHUB_REF 31 | ARG GITHUB_RUN_ID 32 | ARG GITHUB_RUN_NUMBER 33 | ARG GITHUB_RUN_ATTEMPT 34 | 35 | ARG GPG_KEYS=13C82A63B603576156E30A4EA0EA981B66B0D967 36 | 37 | ARG CONFIG=" \ 38 | --prefix=/etc/nginx \ 39 | --sbin-path=/usr/sbin/nginx \ 40 | --modules-path=/usr/lib/nginx/modules \ 41 | --conf-path=/etc/nginx/nginx.conf \ 42 | --error-log-path=/var/log/nginx/error.log \ 43 | --http-log-path=/var/log/nginx/access.log \ 44 | --pid-path=/var/run/nginx.pid \ 45 | --lock-path=/var/run/nginx.lock \ 46 | --http-client-body-temp-path=/var/cache/nginx/client_temp \ 47 | --http-proxy-temp-path=/var/cache/nginx/proxy_temp \ 48 | --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \ 49 | --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \ 50 | --http-scgi-temp-path=/var/cache/nginx/scgi_temp \ 51 | --user=nginx \ 52 | --group=nginx \ 53 | --with-pcre-jit \ 54 | --with-http_ssl_module \ 55 | --with-http_realip_module \ 56 | --with-http_addition_module \ 57 | --with-http_sub_module \ 58 | --with-http_dav_module \ 59 | --with-http_flv_module \ 60 | --with-http_mp4_module \ 61 | --with-http_gunzip_module \ 62 | --with-http_gzip_static_module \ 63 | --with-http_random_index_module \ 64 | --with-http_secure_link_module \ 65 | --with-http_stub_status_module \ 66 | --with-http_auth_request_module \ 67 | --with-http_xslt_module=dynamic \ 68 | --with-http_image_filter_module=dynamic \ 69 | --with-http_geoip_module=dynamic \ 70 | --with-http_perl_module=dynamic \ 71 | --with-threads \ 72 | --with-stream \ 73 | --with-stream_ssl_module \ 74 | --with-stream_ssl_preread_module \ 75 | --with-stream_realip_module \ 76 | --with-stream_geoip_module=dynamic \ 77 | --with-http_slice_module \ 78 | --with-mail \ 79 | --with-mail_ssl_module \ 80 | --with-compat \ 81 | --with-file-aio \ 82 | --with-http_v2_module \ 83 | --with-http_v2_hpack_enc \ 84 | --with-http_v3_module \ 85 | --with-openssl=/usr/src/quiche/quiche/deps/boringssl \ 86 | --with-quiche=/usr/src/quiche \ 87 | --add-module=/usr/src/ngx_brotli \ 88 | --add-module=/usr/src/headers-more-nginx-module \ 89 | --add-module=/usr/src/njs/nginx \ 90 | --add-module=/usr/src/nginx_cookie_flag_module \ 91 | --add-module=/usr/src/ModSecurity-nginx \ 92 | --with-cc-opt=-Wno-error \ 93 | --with-select_module \ 94 | --with-poll_module \ 95 | " 96 | 97 | RUN set -eux \ 98 | && echo $NGINX_VERSION $QUICHE_CHECKOUT $MODSEC_TAG $MODSEC_NGX_TAG $NJS_TAG $BUILD_DATE $VCS_REF $GITHUB_REF $GITHUB_RUN_ID $GITHUB_RUN_NUMBER $GITHUB_RUN_ATTEMPT $GPG_KEYS $CONFIG \ 99 | && addgroup -S nginx \ 100 | && adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx \ 101 | && apk update \ 102 | && apk upgrade \ 103 | && apk add --no-cache ca-certificates openssl \ 104 | && update-ca-certificates \ 105 | && apk add --no-cache --virtual .build-deps \ 106 | gcc \ 107 | libc-dev \ 108 | make \ 109 | pcre-dev \ 110 | zlib-dev \ 111 | linux-headers \ 112 | gnupg \ 113 | libxslt-dev \ 114 | gd-dev \ 115 | geoip-dev \ 116 | perl-dev \ 117 | && apk add --no-cache --virtual .brotli-build-deps \ 118 | autoconf \ 119 | libtool \ 120 | automake \ 121 | git \ 122 | g++ \ 123 | cmake \ 124 | go \ 125 | perl \ 126 | rust \ 127 | cargo \ 128 | patch \ 129 | && apk add --no-cache --virtual .modsec-build-deps \ 130 | libxml2-dev \ 131 | byacc \ 132 | flex \ 133 | libstdc++ \ 134 | libmaxminddb-dev \ 135 | lmdb-dev \ 136 | file \ 137 | pcre2-dev 138 | 139 | WORKDIR /usr/src/ 140 | 141 | FROM deps AS clone_ngx_brotli 142 | RUN git clone --depth=1 --recursive --shallow-submodules https://github.com/google/ngx_brotli 143 | 144 | FROM deps AS clone_headers-more-nginx-module 145 | RUN git clone --depth=1 --recursive --shallow-submodules https://github.com/openresty/headers-more-nginx-module 146 | 147 | FROM deps AS clone_njs 148 | RUN git clone --branch $NJS_TAG --depth=1 --recursive --shallow-submodules https://github.com/nginx/njs 149 | 150 | FROM deps AS clone_nginx_cookie_flag_module 151 | RUN git clone --depth=1 --recursive --shallow-submodules https://github.com/AirisX/nginx_cookie_flag_module 152 | 153 | FROM deps AS clone_quiche 154 | RUN git clone --recursive https://github.com/cloudflare/quiche \ 155 | && cd quiche \ 156 | && git checkout --recurse-submodules $QUICHE_CHECKOUT 157 | 158 | FROM deps AS clone_modsecurity 159 | RUN set -eux \ 160 | && git clone --recursive --branch $MODSEC_TAG --single-branch https://github.com/SpiderLabs/ModSecurity \ 161 | && cd ModSecurity \ 162 | && ./build.sh \ 163 | && ./configure --with-lmdb --enable-examples=no \ 164 | && make -j$(getconf _NPROCESSORS_ONLN) \ 165 | && make -j$(getconf _NPROCESSORS_ONLN) install \ 166 | && strip /usr/local/modsecurity/bin/* \ 167 | && strip /usr/local/modsecurity/lib/*.so.* \ 168 | && strip /usr/local/modsecurity/lib/*.a 169 | 170 | FROM deps AS clone_modsecurity-nginx 171 | RUN git clone --depth=1 --recursive --shallow-submodules --branch $MODSEC_NGX_TAG --single-branch https://github.com/SpiderLabs/ModSecurity-nginx 172 | 173 | FROM deps AS clone_coreruleset 174 | RUN git clone --depth=1 https://github.com/coreruleset/coreruleset /usr/local/share/coreruleset \ 175 | && cp /usr/local/share/coreruleset/crs-setup.conf.example /usr/local/share/coreruleset/crs-setup.conf 176 | 177 | FROM deps AS clone_nginx 178 | COPY nginx.patch /usr/src/nginx.patch 179 | RUN set -eux \ 180 | && wget -q https://raw.githubusercontent.com/kn007/patch/ce70f81a3098afe0e27a8e579b77b6fb32870c54/nginx_with_quic.patch \ 181 | && wget -q https://raw.githubusercontent.com/kn007/patch/cd03b77647c9bf7179acac0125151a0fbb4ac7c8/Enable_BoringSSL_OCSP.patch \ 182 | && wget -qO nginx.tar.gz https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz \ 183 | && wget -qO nginx.tar.gz.asc https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz.asc \ 184 | && export GNUPGHOME="$(mktemp -d)" \ 185 | && found=''; \ 186 | for server in \ 187 | ha.pool.sks-keyservers.net \ 188 | hkp://keyserver.ubuntu.com:80 \ 189 | hkp://p80.pool.sks-keyservers.net:80 \ 190 | pgp.mit.edu \ 191 | ; do \ 192 | echo "Fetching GPG key $GPG_KEYS from $server"; \ 193 | gpg --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$GPG_KEYS" && found=yes && break; \ 194 | done; \ 195 | test -z "$found" && echo >&2 "error: failed to fetch GPG key $GPG_KEYS" && exit 1; \ 196 | gpg --batch --verify nginx.tar.gz.asc nginx.tar.gz \ 197 | && rm -rf "$GNUPGHOME" nginx.tar.gz.asc \ 198 | && tar -zxC /usr/src -f nginx.tar.gz \ 199 | && rm nginx.tar.gz \ 200 | && cd /usr/src/nginx-$NGINX_VERSION \ 201 | && patch -p01 < /usr/src/nginx_with_quic.patch \ 202 | && patch -p01 < /usr/src/Enable_BoringSSL_OCSP.patch \ 203 | && patch -p01 < /usr/src/nginx.patch 204 | 205 | FROM deps AS builder 206 | COPY --from=clone_nginx /usr/src/nginx-$NGINX_VERSION /usr/src/nginx-$NGINX_VERSION 207 | COPY --from=clone_coreruleset /usr/local/share/coreruleset /usr/local/share/coreruleset 208 | COPY --from=clone_modsecurity /usr/local/modsecurity /usr/local/modsecurity 209 | COPY --from=clone_modsecurity /usr/src/ModSecurity /usr/src/ModSecurity 210 | COPY --from=clone_modsecurity-nginx /usr/src/ModSecurity-nginx /usr/src/ModSecurity-nginx 211 | COPY --from=clone_quiche /usr/src/quiche /usr/src/quiche 212 | COPY --from=clone_ngx_brotli /usr/src/ngx_brotli /usr/src/ngx_brotli 213 | COPY --from=clone_headers-more-nginx-module /usr/src/headers-more-nginx-module /usr/src/headers-more-nginx-module 214 | COPY --from=clone_njs /usr/src/njs /usr/src/njs 215 | COPY --from=clone_nginx_cookie_flag_module /usr/src/nginx_cookie_flag_module /usr/src/nginx_cookie_flag_module 216 | RUN set -eux \ 217 | && mkdir /root/.cargo \ 218 | && echo $'[net]\ngit-fetch-with-cli = true' > /root/.cargo/config.toml \ 219 | && cd /usr/src/nginx-$NGINX_VERSION \ 220 | && ./configure $CONFIG --build="docker-nginx-http3-$VCS_REF-$BUILD_DATE-$GITHUB_REF-$GITHUB_RUN_ID-$GITHUB_RUN_NUMBER-$GITHUB_RUN_ATTEMPT ModSecurity-$(git --git-dir=/usr/src/ModSecurity/.git rev-parse --short HEAD) ModSecurity-nginx-$(git --git-dir=/usr/src/ModSecurity-nginx/.git rev-parse --short HEAD) coreruleset-$(git --git-dir=/usr/local/share/coreruleset/.git rev-parse --short HEAD) quiche-$(git --git-dir=/usr/src/quiche/.git rev-parse --short HEAD) ngx_brotli-$(git --git-dir=/usr/src/ngx_brotli/.git rev-parse --short HEAD) headers-more-nginx-module-$(git --git-dir=/usr/src/headers-more-nginx-module/.git rev-parse --short HEAD) njs-$(git --git-dir=/usr/src/njs/.git rev-parse --short HEAD) nginx_cookie_flag_module-$(git --git-dir=/usr/src/nginx_cookie_flag_module/.git rev-parse --short HEAD)" \ 221 | && make -j$(getconf _NPROCESSORS_ONLN) \ 222 | && make -j$(getconf _NPROCESSORS_ONLN) install 223 | 224 | RUN set -eux \ 225 | && rm -rf /etc/nginx/html/ \ 226 | && mkdir /etc/nginx/conf.d/ \ 227 | && mkdir /etc/nginx/modsec/ \ 228 | && mkdir -p /usr/share/nginx/html/ \ 229 | && install -m644 /usr/src/nginx-$NGINX_VERSION/html/index.html /usr/share/nginx/html/ \ 230 | && install -m644 /usr/src/nginx-$NGINX_VERSION/html/50x.html /usr/share/nginx/html/ \ 231 | && ln -s /usr/lib/nginx/modules /etc/nginx/modules \ 232 | && strip /usr/sbin/nginx* \ 233 | && strip /usr/lib/nginx/modules/*.so \ 234 | && find /usr/local/share/coreruleset \! -name '*.conf' -type f -mindepth 1 -maxdepth 1 -delete \ 235 | && find /usr/local/share/coreruleset \! -name 'rules' -type d -mindepth 1 -maxdepth 1 | xargs rm -rf \ 236 | && rm -rf /etc/nginx/*.default /etc/nginx/*.so \ 237 | && rm -rf /usr/src \ 238 | \ 239 | # Bring in gettext so we can get `envsubst`, then throw 240 | # the rest away. To do this, we need to install `gettext` 241 | # then move `envsubst` out of the way so `gettext` can 242 | # be deleted completely, then move `envsubst` back. 243 | && apk add --no-cache --virtual .gettext "gettext>=0.21-r2" \ 244 | && mv /usr/bin/envsubst /tmp/ \ 245 | \ 246 | && runDeps="$( \ 247 | scanelf --needed --nobanner /usr/sbin/nginx /usr/lib/nginx/modules/*.so /tmp/envsubst \ 248 | | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \ 249 | | sort -u \ 250 | | xargs -r apk info --installed \ 251 | | sort -u \ 252 | )" \ 253 | && apk add --no-cache --virtual .nginx-rundeps $runDeps \ 254 | && apk del .modsec-build-deps \ 255 | && apk del .brotli-build-deps \ 256 | && apk del .build-deps \ 257 | && apk del .gettext \ 258 | && rm -rf /root/.cargo \ 259 | && rm -rf /var/cache/apk/* \ 260 | && mv /tmp/envsubst /usr/local/bin/ \ 261 | # Create self-signed certificate 262 | && mkdir -p /etc/ssl/private \ 263 | && openssl req -x509 -newkey rsa:4096 -nodes -keyout /etc/ssl/private/localhost.key -out /etc/ssl/localhost.pem -days 365 -sha256 -subj '/CN=localhost' 264 | 265 | COPY --from=clone_modsecurity /usr/src/ModSecurity/modsecurity.conf-recommended /etc/nginx/modsec/modsecurity.conf 266 | COPY --from=clone_modsecurity /usr/src/ModSecurity/unicode.mapping /etc/nginx/modsec/unicode.mapping 267 | 268 | FROM alpine:edge 269 | 270 | COPY --from=builder /usr/sbin/nginx /usr/sbin/ 271 | COPY --from=builder /usr/lib/libbrotli* /usr/lib/ 272 | COPY --from=builder /usr/lib/nginx /usr/lib/nginx 273 | COPY --from=builder /usr/share/nginx/html/* /usr/share/nginx/html/ 274 | COPY --from=builder /etc/nginx/ /etc/nginx/ 275 | COPY --from=builder /usr/local/bin/envsubst /usr/local/bin/ 276 | COPY --from=builder /etc/ssl/private/localhost.key /etc/ssl/private/ 277 | COPY --from=builder /etc/ssl/localhost.pem /etc/ssl/ 278 | COPY --from=builder /usr/local/share/coreruleset /usr/local/share/coreruleset/ 279 | COPY --from=builder /usr/local/modsecurity /usr/local/modsecurity/ 280 | 281 | RUN \ 282 | apk add --no-cache \ 283 | # Bring in tzdata so users could set the timezones through the environment 284 | # variables 285 | tzdata \ 286 | # Dependencies 287 | pcre \ 288 | pcre2 \ 289 | libgcc \ 290 | libintl \ 291 | # ModSecurity dependencies 292 | libxml2-dev \ 293 | yajl-dev \ 294 | geoip-dev \ 295 | libstdc++ \ 296 | libmaxminddb-dev \ 297 | lmdb-dev \ 298 | && addgroup -S nginx \ 299 | && adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx \ 300 | # forward request and error logs to docker log collector 301 | && mkdir -p /var/log/nginx \ 302 | && touch /var/log/nginx/access.log /var/log/nginx/error.log \ 303 | && chown nginx: /var/log/nginx/access.log /var/log/nginx/error.log \ 304 | && ln -sf /dev/stdout /var/log/nginx/access.log \ 305 | && ln -sf /dev/stderr /var/log/nginx/error.log 306 | 307 | COPY modsec/* /etc/nginx/modsec/ 308 | 309 | # Recommended nginx configuration. Please copy the config you wish to use. 310 | # COPY nginx.conf /etc/nginx/ 311 | # COPY h3.nginx.conf /etc/nginx/conf.d/ 312 | 313 | RUN nginx -V 314 | 315 | STOPSIGNAL SIGTERM 316 | 317 | CMD ["nginx", "-g", "daemon off;"] 318 | --------------------------------------------------------------------------------