├── .dockerignore ├── .drone.yml ├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── build.sh ├── ci-build.sh ├── client_certs ├── .gitignore ├── create_ca.sh ├── create_client_csr_and_key.sh ├── create_server_csr_and_key.sh ├── settings.cfg ├── sign_client_key_with_ca.sh └── sign_server_key_with_ca.sh ├── code_of_conduct.md ├── defaults.sh ├── docker-config ├── Dockerfile.mockserver ├── Dockerfile.mutual-tls ├── Dockerfile.slowmockserver └── Dockerfile.standard-tls ├── docs └── GeneratedConfigs.md ├── enable_location.sh ├── go.sh ├── helper.sh ├── html ├── 404.shtml ├── 418-request-denied.shtml ├── 500.shtml ├── 501.shtml ├── 502.shtml ├── 503.shtml ├── 504.shtml └── 50x.shtml ├── location_template.conf ├── logging.conf ├── lua ├── get_env.lua └── set_uuid.lua ├── monkey-business.yaml ├── naxsi └── location.rules ├── nginx.conf ├── nginx_rate_limits_null.conf ├── nginx_statsd_metrics.conf ├── nginx_statsd_server.conf ├── nginx_web_sockets_proxy.conf ├── publish.sh ├── readyness.sh ├── refresh_geoip.sh ├── security_defaults.conf ├── test-servers.yaml └── whitelist /.dockerignore: -------------------------------------------------------------------------------- 1 | ./README.md -------------------------------------------------------------------------------- /.drone.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | type: kubernetes 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | workspace: 10 | path: /workdir 11 | 12 | steps: 13 | - name: build_and_test_image 14 | image: 340268328991.dkr.ecr.eu-west-2.amazonaws.com/acp/dind 15 | commands: 16 | - apk add curl bash wget tar 17 | - /usr/local/bin/wait 18 | - ./ci-build.sh 19 | environment: 20 | GEOIP_ACCOUNT_ID: 21 | from_secret: geoip_account_id 22 | GEOIP_LICENSE_KEY: 23 | from_secret: geoip_license_key 24 | when: 25 | event: 26 | - pull_request 27 | - push 28 | - tag 29 | 30 | - name: trivy_scan 31 | image: 340268328991.dkr.ecr.eu-west-2.amazonaws.com/acp/trivy/client:latest 32 | pull: always 33 | environment: 34 | IMAGE_NAME: ngx:latest 35 | LOCAL_IMAGE: true 36 | ALLOW_CVE_LIST_FILE: whitelist 37 | SEVERITY: HIGH,CRITICAL 38 | # Prevent build failure due to self-signed cert private key detection. 39 | # This is a non-production key generated at build time for testing. 40 | FAIL_ON_DETECTION: false 41 | when: 42 | event: 43 | - push 44 | - pull_request 45 | depends_on: 46 | - build_and_test_image 47 | 48 | - name: push_image_to_artifactory 49 | image: 340268328991.dkr.ecr.eu-west-2.amazonaws.com/acp/dind 50 | commands: 51 | - docker login -u="$${DOCKER_USERNAME}" -p="$${DOCKER_PASSWORD}" "$${DOCKER_REPO}" 52 | - ./publish.sh "ngx" "$${DOCKER_REPO}$${DOCKER_BASEDIR}$${DOCKER_IMAGE}" "$${DRONE_TAG}" 53 | environment: 54 | DOCKER_BASEDIR: / 55 | DOCKER_IMAGE: nginx-proxy 56 | DOCKER_PASSWORD: 57 | from_secret: docker_password 58 | DOCKER_REPO: artifactory-internal.digital.homeoffice.gov.uk 59 | DOCKER_USERNAME: docker-nginx-proxy-robot 60 | depends_on: 61 | - trivy_scan 62 | when: 63 | event: 64 | - tag 65 | 66 | - name: push_image_to_quay 67 | image: 340268328991.dkr.ecr.eu-west-2.amazonaws.com/acp/dind 68 | commands: 69 | - docker login -u="$${DOCKER_USERNAME}" -p="$${DOCKER_QUAY_PASSWORD}" "$${DOCKER_REPO}" 70 | - ./publish.sh "ngx" "$${DOCKER_REPO}$${DOCKER_BASEDIR}$${DOCKER_IMAGE}" "$${DRONE_TAG}" 71 | environment: 72 | DOCKER_BASEDIR: /ukhomeofficedigital/ 73 | DOCKER_IMAGE: nginx-proxy 74 | DOCKER_QUAY_PASSWORD: 75 | from_secret: docker_quay_password 76 | DOCKER_REPO: quay.io 77 | DOCKER_USERNAME: ukhomeofficedigital+nginx_proxy 78 | depends_on: 79 | - trivy_scan 80 | when: 81 | event: 82 | - tag 83 | 84 | services: 85 | - name: docker 86 | image: 340268328991.dkr.ecr.eu-west-2.amazonaws.com/acp/dind 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /tmp/* 3 | *.swp 4 | client-ca 5 | upstream-* 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 17 | do not have permission to do that, you may request the second reviewer to merge it for you. 18 | 19 | ## Contributor Code of Conduct 20 | 21 | As contributors and maintainers of this project, and in the interest of fostering an open and 22 | welcoming community, we pledge to respect all people who contribute through reporting issues, 23 | posting feature requests, updating documentation, submitting pull requests or patches, and other 24 | activities. 25 | 26 | We are committed to making participation in this project a harassment-free experience for everyone, 27 | regardless of level of experience, gender, gender identity and expression, sexual orientation, 28 | disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. 29 | 30 | Examples of unacceptable behavior by participants include: 31 | 32 | * The use of sexualized language or imagery 33 | * Personal attacks 34 | * Trolling or insulting/derogatory comments 35 | * Public or private harassment 36 | * Publishing other's private information, such as physical or electronic addresses, without explicit 37 | permission 38 | * Other unethical or unprofessional conduct. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, 41 | code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By 42 | adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently 43 | applying these principles to every aspect of managing this project. Project maintainers who do not 44 | follow or enforce the Code of Conduct may be permanently removed from the project team. 45 | 46 | This code of conduct applies both within project spaces and in public spaces when an individual is 47 | representing the project or its community. 48 | 49 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an 50 | issue or contacting one or more of the project maintainers. 51 | 52 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), 53 | version 1.2.0, available at 54 | [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 55 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM almalinux:9.5 2 | 3 | RUN dnf update -y && \ 4 | dnf autoremove -y && \ 5 | dnf clean all && \ 6 | rm -rf /var/cache/dnf 7 | 8 | ARG GEOIP_ACCOUNT_ID 9 | ARG GEOIP_LICENSE_KEY 10 | 11 | WORKDIR /root 12 | ADD ./build.sh /root/ 13 | RUN ./build.sh 14 | 15 | RUN dnf install -y openssl && \ 16 | dnf clean all && \ 17 | mkdir -p /etc/keys && \ 18 | openssl req -x509 -newkey rsa:2048 -keyout /etc/keys/key -out /etc/keys/crt -days 360 -nodes -subj '/CN=test' && \ 19 | chmod 600 /etc/keys/key 20 | 21 | # This takes a while so best to do it during build 22 | RUN openssl dhparam -out /usr/local/openresty/nginx/conf/dhparam.pem 2048 23 | 24 | RUN dnf install -y bind-utils dnsmasq diffutils && \ 25 | dnf clean all 26 | 27 | ADD ./naxsi/location.rules /usr/local/openresty/naxsi/location.template 28 | ADD ./nginx*.conf /usr/local/openresty/nginx/conf/ 29 | RUN mkdir -p /usr/local/openresty/nginx/conf/locations /usr/local/openresty/nginx/lua 30 | ADD ./lua/* /usr/local/openresty/nginx/lua/ 31 | RUN md5sum /usr/local/openresty/nginx/conf/nginx.conf | cut -d' ' -f 1 > /container_default_ngx 32 | ADD ./defaults.sh / 33 | ADD ./go.sh / 34 | ADD ./enable_location.sh / 35 | ADD ./location_template.conf / 36 | ADD ./logging.conf /usr/local/openresty/nginx/conf/ 37 | ADD ./security_defaults.conf /usr/local/openresty/nginx/conf/ 38 | ADD ./html/ /usr/local/openresty/nginx/html/ 39 | ADD ./readyness.sh / 40 | ADD ./helper.sh / 41 | ADD ./refresh_geoip.sh / 42 | 43 | RUN dnf remove -y kernel-headers && \ 44 | dnf clean all 45 | 46 | RUN useradd -u 1000 nginx && \ 47 | install -o nginx -g nginx -d \ 48 | /usr/local/openresty/naxsi/locations \ 49 | /usr/local/openresty/nginx/{client_body,fastcgi,proxy,scgi,uwsgi}_temp && \ 50 | chown -R nginx:nginx /usr/local/openresty/nginx/{conf,logs} /usr/share/GeoIP /etc/keys 51 | 52 | WORKDIR /usr/local/openresty 53 | 54 | EXPOSE 10080 10443 55 | 56 | USER 1000 57 | 58 | ENTRYPOINT [ "/go.sh" ] 59 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2015 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenResty Docker Container 2 | 3 | [![Build Status](https://travis-ci.org/UKHomeOffice/docker-nginx-proxy.svg?branch=master)](https://travis-ci.org/UKHomeOffice/docker-nginx-proxy) 4 | 5 | This container aims to be a generic proxy layer for your web services. It includes OpenResty with 6 | Lua and NAXSI filtering compiled in. 7 | 8 | ## Getting Started 9 | 10 | In this section I'll show you some examples of how you might run this container with docker. 11 | 12 | ### Prerequisites 13 | 14 | In order to run this container you'll need docker installed. 15 | 16 | * [Windows](https://docs.docker.com/windows/started) 17 | * [OS X](https://docs.docker.com/mac/started/) 18 | * [Linux](https://docs.docker.com/linux/started/) 19 | 20 | ## Creating builds 21 | When an update has been deployed to master, tag and push the master branch to kick off a Drone build: 22 | ``` 23 | git tag 24 | git push --tags 25 | ``` 26 | 27 | ## Testing Locally 28 | Authentic GEOIP credentials are needed when curling MaxMind Urls otherwise a 401 is returned. Therefore if no `GEOIP_LICENSE_KEY` secret/env var is set when running `./ci-build.sh`, a `LOCAL_TEST` variable is set to true to ensure the relevant urls are not called. This means not having to start up a Drone build every single time you want to test something and your local machine reuses any Docker cache available to it making the test/retry process much faster. 29 | 30 | *MAC USERS* 31 | You will need to install wget so that this can run in the `./ci-build.sh` script: 32 | ``` 33 | brew install wget 34 | ``` 35 | 36 | ## Usage 37 | 38 | ### Environment Variables 39 | 40 | #### Multi-location Variables 41 | 42 | Variables to control how to configure the proxy (can be set per location, see 43 | [Using Multiple Locations](#using-multiple-locations)). 44 | 45 | * `PROXY_SERVICE_HOST` - The upstream host you want this service to proxy. 46 | * `PROXY_SERVICE_PORT` - The port of the upstream host you want this service to proxy. 47 | * `NAXSI_RULES_URL_CSV` - A CSV of [Naxsi](https://github.com/nbs-system/naxsi) URL's of files to download and use. 48 | (Files must end in .rules to be loaded) 49 | * `NAXSI_RULES_MD5_CSV` - A CSV of md5 hashes for the files specified above 50 | * `EXTRA_NAXSI_RULES` - Allows NAXSI rules to be specified as an environment variable. This allows one or two extra 51 | rules to be specified without downloading or mounting in a rule file. 52 | * `NAXSI_USE_DEFAULT_RULES` - If set to "FALSE" will delete the default rules file. 53 | * `ENABLE_UUID_PARAM` - If set to "FALSE", will NOT add a UUID url parameter to all requests. The Default will add this 54 | for easy tracking in down stream logs e.g. `nginxId=50c91049-667f-4286-c2f0-86b04b27d3f0`. 55 | If set to `HEADER` it will add `nginxId` to the headers, not append to the get params. 56 | * `CLIENT_CERT_REQUIRED` - if set to `TRUE`, will deny access at this location, see [Client Certs](#client-certs). 57 | * `VERIFY_SERVER_CERT` - if set to `TRUE`, will verify the upstream server's TLS certificate is valid and signed by the CA, see [Verifying Upstream Server](#verifying-upstream-server). 58 | * `USE_UPSTREAM_CLIENT_CERT` - if set to `TRUE`, will use the set of upstream client certs when connecting upstream, see [Upstream Client Certs](#upstream-client-certs). 59 | * `ERROR_REDIRECT_CODES` - Can override when Nginx will redirect requests to its own error page. Defaults to 60 | "`500 501 502 503 504`". To support a new code, say `505`, an error page must be provided at 61 | `/usr/local/openresty/nginx/html/505.shtml`, see [Useful File Locations](#useful-file-locations). Set to `FALSE` to disable all error pages. 62 | * `ADD_NGINX_LOCATION_CFG` - Arbitrary extra NGINX configuration to be added to the location context, see 63 | [Arbitrary Config](#arbitrary-config). 64 | * `PORT_IN_HOST_HEADER` - If FALSE will remove the port from the http `Host` header. 65 | * `BASIC_AUTH` - Define a path for username and password file (in `username:password` format), this will turn the file into a .htpasswd file. 66 | * `REQS_PER_MIN_PER_IP` - Will limit requests based on IP e.g. set to 60 to allow one request per second. 67 | * `CONCURRENT_CONNS_PER_IP` - Will limit concurrent connections based on IP e.g. set to 10 to allow max of 10 connections per browser or proxy! 68 | * `REQS_PER_PAGE` - Will limit requests to 'bursts' of x requests at a time before terminating (will default to 20) 69 | * `DENY_COUNTRY_ON` - Set to `TRUE` to deny access to countries not listed in ALLOW_COUNTRY_CSV with 403 status for a location (set location for 403 with ADD_NGINX_LOCATION_CFG). 70 | * `VERBOSE_ERROR_PAGES` - Set to TRUE to display debug info in 418 error pages. 71 | * `FEEDBACK_EMAIL` - Set to add feedback email in 418 error pages. 72 | 73 | #### Single set Variables 74 | 75 | Note the following variables can only be set once: 76 | 77 | * `ADD_NGINX_SERVER_CFG` - Arbitrary extra NGINX configuration to be added to the server context, see 78 | [Arbitrary Config](#arbitrary-config) 79 | * `ADD_NGINX_HTTP_CFG` - Arbitrary extra NGINX configuration to be added to the http context, see 80 | [Arbitrary Config](#arbitrary-config) 81 | * `LOCATIONS_CSV` - Set to a list of locations that are to be independently proxied, see the example 82 | [Using Multiple Locations](#using-multiple-locations). Note, if this isn't set, `/` will be used as the default 83 | location. 84 | * `LOAD_BALANCER_CIDR` - Set to preserve client IP addresses. *Important*, to enable, see 85 | [Preserve Client IP](#preserve-client-ip). 86 | * `NAME_RESOLVER` - Can override the *default* DNS server used to re-resolve the backend proxy (based on TTL). 87 | The *Default DNS Server* is the first entry in the resolve.conf file in the container and is normally correct and 88 | managed by Docker or Kubernetes. 89 | * `CLIENT_MAX_BODY_SIZE` - Can set a larger upload than Nginx defaults in MB. 90 | * `HTTPS_REDIRECT_PORT` - Only required for http to https redirect and only when a non-standard https port is in use. 91 | This is useful when testing or for development instances or when a load-balancer mandates a non-standard port. 92 | * `LOG_FORMAT_NAME` - Can be set to `text` or `json` (default). 93 | * `NO_LOGGING_URL_PARAMS` - Can be set to `TRUE` if you don't want to log url params. Default is empty which means URL params are logged 94 | * `NO_LOGGING_BODY` - Defaults to true `TRUE`. Set otherwise and nginx should log the request_body. 95 | * `NO_LOGGING_RESPONSE` - Defaults to true `TRUE`. Set otherwise and nginx should log the response_body 96 | * `SERVER_CERT` - Can override where to find the server's SSL cert. 97 | * `SERVER_KEY` - Can override where to find the server's SSL key. 98 | * `SSL_CIPHERS` - Change the SSL ciphers support default only AES256+EECDH:AES256+EDH:!aNULL 99 | * `SSL_PROTOCOLS` - Change the SSL protocols supported default only TLSv1.2 100 | * `SSL_SESSION_TIMEOUT` - Specifies a time during which a client may reuse the session parameters (defaults to 10min) 101 | * `HTTP_LISTEN_PORT` - Change the default inside the container from 10080. 102 | * `HTTPS_LISTEN_PORT` - Change the default inside the container from 10443. 103 | * `HTTP2` - Defaults to false `FALSE`. Specifies whether http2 should be used 104 | * `HTTPS_REDIRECT` - Toggle whether or not we force redirects to HTTPS. Defaults to true. 105 | * `ALLOW_COUNTRY_CSV` - List of [country codes](http://dev.maxmind.com/geoip/legacy/codes/iso3166/) to allow. 106 | * `STATSD_METRICS` - Toggle if metrics are logged to statsd (defaults to true) 107 | * `STATSD_SERVER` - Server to send statsd metrics to, defaults to 127.0.0.1 108 | * `DISABLE_SYSDIG_METRICS` - Set to any non-empty string to disable support for Sysdig's metric collection 109 | 110 | ### Ports 111 | 112 | This container exposes 113 | 114 | * `10080` - HTTP 115 | * `10443` - HTTPS 116 | 117 | N.B. see HTTP(S)_LISTEN_PORT above 118 | 119 | ### Useful File Locations 120 | 121 | * `nginx.conf` is stored at `/usr/local/openresty/nginx/conf/nginx.conf` 122 | * `/etc/keys/crt` & `/etc/keys/key` - A certificate can be mounted here to make OpenResty use it. However a self 123 | signed one is provided if they have not been mounted. 124 | * `/etc/keys/client-ca` If a client CA is mounted here, it will be loaded and configured. 125 | See `CLIENT_CERT_REQUIRED` above in [Environment Variables](#environment-variables). 126 | * `/etc/keys/upstream-server-ca` A CA public cert must be mounted here when verifying the upstream server's certificate is required. 127 | See `VERIFY_SERVER_CERT` above in [Environment Variables](#environment-variables). 128 | * `/etc/keys/upstream-client-crt` A public client cert must be mounted here when when the upstream server requires client cert authentication. 129 | See `USE_UPSTREAM_CLIENT_CERT` above in [Environment Variables](#environment-variables). 130 | * `/etc/keys/upstream-client-key` A private client key must be mounted here when when the upstream server requires client cert authentication. 131 | See `USE_UPSTREAM_CLIENT_CERT` above in [Environment Variables](#environment-variables). 132 | * `/usr/local/openresty/naxsi/*.conf` - [Naxsi](https://github.com/nbs-system/naxsi) rules location in default 133 | nginx.conf. 134 | * `/usr/local/openresty/nginx/html/$CODE.shtml` - HTML (with SSI support) displayed when a the status code $CODE 135 | is encountered upstream and the proxy is configured to intercept. See ERROR_REDIRECT_CODES to change this. 136 | * `/usr/local/openresty/nginx/html/418-request-denied.shtml` - HTML (with SSI support) displayed when NAXSI 137 | blocks a request. 138 | 139 | ### Examples 140 | 141 | #### Self signed SSL Certificate 142 | 143 | ```shell 144 | docker run -e 'PROXY_SERVICE_HOST=http://stackexchange.com' \ 145 | -e 'PROXY_SERVICE_PORT=80' \ 146 | -p 8443:443 \ 147 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 148 | ``` 149 | 150 | #### Custom SSL Certificate 151 | 152 | ```shell 153 | docker run -e 'PROXY_SERVICE_HOST=http://stackexchange.com' \ 154 | -e 'PROXY_SERVICE_PORT=80' \ 155 | -p 8443:443 \ 156 | -v /path/to/key:/etc/keys/key:ro \ 157 | -v /path/to/crt:/etc/keys/crt:ro \ 158 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 159 | ``` 160 | 161 | #### Preserve Client IP 162 | 163 | This proxy supports [Proxy Protocol](http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt). 164 | 165 | To use this feature you will need: 166 | 167 | * To enable [proxy protocol](http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt) on your load balancer. 168 | For AWS, see [Enabling Proxy Protocol for AWS](http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/enable-proxy-protocol.html). 169 | * Find the private address range of your load balancer. 170 | For AWS, this could be any address in the destination network. E.g. 171 | if you have three compute subnets defined as 10.50.0.0/24, 10.50.1.0/24 and 10.50.2.0/24, 172 | then a suitable range would be 10.50.0.0/22 see [CIDR Calculator](http://www.subnet-calculator.com/cidr.php). 173 | 174 | ```shell 175 | docker run -e 'PROXY_SERVICE_HOST=http://stackexchange.com' \ 176 | -e 'PROXY_SERVICE_PORT=80' \ 177 | -e 'LOAD_BALANCER_CIDR=10.50.0.0/22' \ 178 | -p 8443:443 \ 179 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 180 | ``` 181 | 182 | #### Extra NAXSI Rules from Environment 183 | 184 | The example below allows large documents to be POSTED to the /documents/uploads and /documents/other_uploads locations. 185 | See [Whitelist NAXSI rules](https://github.com/nbs-system/naxsi/wiki/whitelists) for more examples. 186 | 187 | ```shell 188 | docker run -e 'PROXY_SERVICE_HOST=http://myapp.svc.cluster.local' \ 189 | -e 'PROXY_SERVICE_PORT=8080' \ 190 | -e 'EXTRA_NAXSI_RULES=BasicRule wl:2 "mz:$URL:/documents/uploads|BODY"; 191 | BasicRule wl:2 "mz:$URL:/documents/other_uploads|BODY";' \ 192 | -p 8443:443 \ 193 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 194 | ``` 195 | 196 | #### Using Multiple Locations 197 | 198 | When the LOCATIONS_CSV option is set, multiple locations can be proxied. The settings for each proxy location can be 199 | controlled with the use of any [Multi-location Variables](#multi-location-variables) by suffixing the variable name with 200 | both a number, and the '_' character, as listed in the LOCATIONS_CSV variable. 201 | 202 | ##### Two servers 203 | 204 | The example below configures a simple proxy with two locations '/' (location 1) and '/api' (location 2): 205 | 206 | ```shell 207 | docker run -e 'PROXY_SERVICE_HOST_1=http://stackexchange.com' \ 208 | -e 'PROXY_SERVICE_PORT_1=80' \ 209 | -e 'PROXY_SERVICE_HOST_2=https://api.svc.cluster.local' \ 210 | -e 'PROXY_SERVICE_PORT_2=8888' \ 211 | -e 'LOCATIONS_CSV=/,/api' \ 212 | -p 8443:443 \ 213 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 214 | ``` 215 | 216 | For more detail, see the [generated config](./docs/GeneratedConfigs.md#two-separate-proxied-servers). 217 | 218 | ##### One Server, Multiple locations 219 | 220 | The example below will proxy the same address for two locations but will disable the UUID (nginxId) parameter for the 221 | /about location only. 222 | 223 | See the [generated config](./docs/GeneratedConfigs.md#same-server-proxied) for below: 224 | 225 | ```shell 226 | docker run -e 'PROXY_SERVICE_HOST=http://stackexchange.com' \ 227 | -e 'PROXY_SERVICE_PORT=80' \ 228 | -e 'LOCATIONS_CSV=/,/about' \ 229 | -e 'ENABLE_UUID_PARAM_2=FALSE' \ 230 | -p 8443:443 \ 231 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 232 | ``` 233 | 234 | #### Client Certs 235 | 236 | If a client CA certificate is mounted, the proxy will be configured to load it. If a client has the cert, the client CN 237 | will be set in the X-Username header and logged. 238 | ```shell 239 | docker run -e 'PROXY_SERVICE_HOST=http://stackexchange.com' \ 240 | -e 'PROXY_SERVICE_PORT=80' \ 241 | -v "${PWD}/client_certs/ca.crt:/etc/keys/client-ca" \ 242 | -p 8443:443 \ 243 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 244 | ``` 245 | 246 | The following example will specifically deny access to clients without a cert: 247 | 248 | ```shell 249 | docker run -e 'PROXY_SERVICE_HOST=http://serverfault.com' \ 250 | -e 'PROXY_SERVICE_PORT=80' \ 251 | -e 'LOCATIONS_CSV=/,/about' \ 252 | -e 'CLIENT_CERT_REQUIRED_2=TRUE' \ 253 | -v "${PWD}/client_certs/ca.crt:/etc/keys/client-ca" \ 254 | -p 8443:443 \ 255 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 256 | ``` 257 | See [./client_certs](./client_certs) for scripts that can be used to generate a CA and client certs. 258 | 259 | #### Upstream Client Certs 260 | 261 | If the environment variable `USE_UPSTREAM_CLIENT_CERT` is set to `TRUE` 262 | then the client certs at `/etc/keys/upstream-client-crt` and 263 | `/etc/keys/upstream-client-key` will be used to authenticate with the 264 | upstream HTTPS service. 265 | 266 | ```shell 267 | docker run -e 'PROXY_SERVICE_HOST=https://stackexchange.com' \ 268 | -e 'PROXY_SERVICE_PORT=443' \ 269 | -e 'USE_UPSTREAM_CLIENT_CERT=TRUE' \ 270 | -v "/path/to/client-public.crt:/etc/keys/upstream-client-crt" \ 271 | -v "/path/to/client-private.key:/etc/keys/upstream-client-key" \ 272 | -p 8443:443 \ 273 | quay.io/ukhomeofficedigital/nginx-proxy:v2.1.0 274 | ``` 275 | 276 | #### Verifying Upstream Server 277 | 278 | If the environment variable `VERIFY_SERVER_CERT` is set to `TRUE` then 279 | the upstream server's certificate will be validated against the CA 280 | public cert at `/etc/keys/upstream-server-ca`. 281 | 282 | ```shell 283 | docker run -e 'PROXY_SERVICE_HOST=https://stackexchange.com' \ 284 | -e 'PROXY_SERVICE_PORT=443' \ 285 | -e 'VERIFY_SERVER_CERT=TRUE' \ 286 | -v "/path/to/ca.crt:/etc/keys/upstream-server-ca" \ 287 | -p 8443:443 \ 288 | quay.io/ukhomeofficedigital/nginx-proxy:v2.1.0 289 | ``` 290 | 291 | #### Arbitrary Config 292 | 293 | The example below will return "ping ok" for the URL /ping. 294 | ```shell 295 | docker run -e 'PROXY_SERVICE_HOST=http://stackexchange.com' \ 296 | -e 'PROXY_SERVICE_PORT=80' \ 297 | -e 'ADD_NGINX_LOCATION_CFG=if ($uri = /proxy-ping) return 200 "ping ok";' \ 298 | -p 8443:443 \ 299 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 300 | ``` 301 | 302 | The example below will return "404" for the URL /notfound. 303 | ```shell 304 | docker run -e 'PROXY_SERVICE_HOST=http://stackexchange.com' \ 305 | -e 'PROXY_SERVICE_PORT=80' \ 306 | -e 'ADD_NGINX_SERVER_CFG=location /notfound { return 404; };' \ 307 | -p 8443:443 \ 308 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 309 | ``` 310 | 311 | The example below enables proxy_cache_path directive. Allows you to define where cached files are stored. 312 | ```shell 313 | docker run -e 'PROXY_SERVICE_HOST=http://stackexchange.com' \ 314 | -e 'PROXY_SERVICE_PORT=80' \ 315 | -e 'ADD_NGINX_HTTP_CFG=proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=static:10m;' \ 316 | -p 8443:443 \ 317 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 318 | ``` 319 | 320 | #### Basic Auth 321 | 322 | To add basic auth to your server you need to define the username and password by mounting a file and defining that file in the `BASIC_AUTH` variable, then add the location config to you config. 323 | 324 | ```shell 325 | docker run -e 'PROXY_SERVICE_HOST=http://stackexchange.com' \ 326 | -e 'PROXY_SERVICE_PORT=80' \ 327 | -e 'ADD_NGINX_LOCATION_CFG='auth_basic "Restricted"; auth_basic_user_file /etc/secrets/.htpasswd;' \ 328 | -e BASIC_AUTH='/etc/secrets/basic-auth' 329 | -p 8443:443 \ 330 | -v ~/Documents:/etc/secrets/ 331 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 332 | ``` 333 | 334 | The basic auth file will look like this. 335 | ```shell 336 | admin:testing 337 | username:password 338 | ``` 339 | ##### Basic Auth on mutliple Locations 340 | 341 | If you're using multiple locations then we need to define the location that basic_auth will be set in relation to the `LOCATIONS_CSV` 342 | 343 | ```shell 344 | docker run -e 'PROXY_SERVICE_HOST=http://serverfault.com' \ 345 | -e 'PROXY_SERVICE_PORT=80' \ 346 | -e 'LOCATIONS_CSV=/,/about' \ 347 | -e 'CLIENT_CERT_REQUIRED_2=TRUE' \ 348 | -e BASIC_AUTH_2=/etc/secrets/basic-auth \ 349 | -v "${PWD}/client_certs/ca.crt:/etc/keys/client-ca" \ 350 | -p 8443:443 \ 351 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 352 | ``` 353 | 354 | this will setup basic-auth for the the `/about` location or simply swap the 2 for a 1 to setup basic auth for the root location. 355 | 356 | 357 | 358 | ## Built With 359 | 360 | * [OpenResty](https://openresty.org/) - OpenResty (aka. ngx_openresty) is a full-fledged web 361 | application server by bundling the standard Nginx core, lots of 3rd-party Nginx modules, as well 362 | as most of their external dependencies. 363 | * [Nginx](https://www.nginx.com/resources/wiki/) - The proxy server core software. 364 | * [ngx_lua](http://wiki.nginx.org/HttpLuaModule) - Embed the power of Lua into Nginx 365 | * [Naxsi](https://github.com/nbs-system/naxsi) - NAXSI is an open-source, high performance, low 366 | rules maintenance WAF for NGINX 367 | * [GeoLite data](http://www.maxmind.com">http://www.maxmind.com) This product includes GeoLite data created by MaxMind. 368 | 369 | ## Find Us 370 | 371 | * [GitHub](https://github.com/UKHomeOffice/docker-nginx-proxy) 372 | * [Quay.io](https://quay.io/repository/ukhomeofficedigital/nginx-proxy) 373 | 374 | ## Contributing 375 | 376 | Feel free to submit pull requests and issues. If it's a particularly large PR, you may wish to 377 | discuss it in an issue first. 378 | 379 | Please note that this project is released with a [Contributor Code of Conduct](code_of_conduct.md). 380 | By participating in this project you agree to abide by its terms. 381 | 382 | ## Versioning 383 | 384 | We use [SemVer](http://semver.org/) for the version tags available See the tags on this repository. 385 | 386 | ## Authors 387 | 388 | * **Lewis Marshal** - *Initial work* - [lewismarshall](https://github.com/lewismarshall) 389 | 390 | See also the list of 391 | [contributors](https://github.com/UKHomeOffice/docker-nginx-proxy/graphs/contributors) who 392 | participated in this project. 393 | 394 | ## License 395 | 396 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 397 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Script to install the openresty from source and to tidy up after... 3 | 4 | set -eu 5 | set -o pipefail 6 | 7 | if [ -z "$GEOIP_LICENSE_KEY" ]; then 8 | LOCAL_TEST=true 9 | else 10 | LOCAL_TEST=false 11 | fi 12 | 13 | GEOIP_ACCOUNT_ID="${GEOIP_ACCOUNT_ID:-123456}" 14 | GEOIP_LICENSE_KEY="${GEOIP_LICENSE_KEY:-xxxxxx}" 15 | GEOIP_CITY_URL="https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=${GEOIP_LICENSE_KEY}&suffix=tar.gz" 16 | GEOIP_COUNTRY_URL="https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${GEOIP_LICENSE_KEY}&suffix=tar.gz" 17 | GEOIP_MOD_URL='https://github.com/leev/ngx_http_geoip2_module/archive/3.3.tar.gz' 18 | GEOIP_UPDATE_CLI='https://github.com/maxmind/geoipupdate/releases/download/v7.1.0/geoipupdate_7.1.0_linux_amd64.tar.gz' 19 | GEOIP_URL='https://github.com/maxmind/libmaxminddb/releases/download/1.6.0/libmaxminddb-1.6.0.tar.gz' 20 | LUAROCKS_URL='https://luarocks.github.io/luarocks/releases/luarocks-3.7.0.tar.gz' 21 | NAXSI_URL='https://github.com/nbs-system/naxsi/archive/1.3.tar.gz' 22 | OPEN_RESTY_URL='http://openresty.org/download/openresty-1.19.3.2.tar.gz' 23 | STATSD_URL='https://github.com/UKHomeOffice/nginx-statsd/archive/0.0.1-ngxpatch.tar.gz' 24 | 25 | MAXMIND_PATH='/usr/share/GeoIP' 26 | 27 | # Install dependencies to build from source 28 | dnf -y install \ 29 | gcc-c++ \ 30 | gcc \ 31 | git \ 32 | make \ 33 | libcurl-devel \ 34 | openssl-devel \ 35 | openssl \ 36 | perl \ 37 | pcre-devel \ 38 | pcre \ 39 | readline-devel \ 40 | tar \ 41 | unzip \ 42 | wget \ 43 | zlib-devel 44 | 45 | mkdir -p openresty luarocks naxsi nginx-statsd geoip geoipupdate ngx_http_geoip2_module 46 | 47 | # Prepare 48 | wget -qO - "$OPEN_RESTY_URL" | tar xzv --strip-components 1 -C openresty/ 49 | wget -qO - "$LUAROCKS_URL" | tar xzv --strip-components 1 -C luarocks/ 50 | wget -qO - "$NAXSI_URL" | tar xzv --strip-components 1 -C naxsi/ 51 | wget -qO - "$STATSD_URL" | tar xzv --strip-components 1 -C nginx-statsd/ 52 | wget -qO - "$GEOIP_URL" | tar xzv --strip-components 1 -C geoip/ 53 | wget -qO - "$GEOIP_UPDATE_CLI" | tar xzv --strip-components 1 -C geoipupdate/ 54 | wget -qO - "$GEOIP_MOD_URL" | tar xzv --strip-components 1 -C ngx_http_geoip2_module/ 55 | 56 | # Build 57 | pushd geoip 58 | mkdir -p ${MAXMIND_PATH} 59 | ./configure 60 | make check install 61 | echo "/usr/local/lib" >> /etc/ld.so.conf.d/libmaxminddb.conf 62 | 63 | # Only run if not testing locally 64 | if [ "$LOCAL_TEST" = false ]; then 65 | curl -fSL ${GEOIP_COUNTRY_URL} | tar -xz > ${MAXMIND_PATH}/GeoLite2-Country.mmdb 66 | curl -fSL ${GEOIP_CITY_URL} | tar -xz > ${MAXMIND_PATH}/GeoLite2-City.mmdb 67 | fi 68 | 69 | chown -R 1000:1000 ${MAXMIND_PATH} 70 | popd 71 | 72 | pushd geoipupdate 73 | sed -i 's/YOUR_ACCOUNT_ID_HERE/'"${GEOIP_ACCOUNT_ID}"'/g' GeoIP.conf 74 | sed -i 's/YOUR_LICENSE_KEY_HERE/'"${GEOIP_LICENSE_KEY}"'/g' GeoIP.conf 75 | 76 | # Only run if not testing locally 77 | if [ "$LOCAL_TEST" = false ]; then 78 | ./geoipupdate -f GeoIP.conf -d ${MAXMIND_PATH} 79 | fi 80 | popd 81 | 82 | echo "Checking libmaxminddb module" 83 | ldconfig && ldconfig -p | grep libmaxminddb 84 | 85 | echo "Install openresty" 86 | pushd openresty 87 | ./configure --add-dynamic-module="/root/ngx_http_geoip2_module" \ 88 | --add-module="../naxsi/naxsi_src" \ 89 | --add-module="../nginx-statsd" \ 90 | --with-http_realip_module \ 91 | --with-http_v2_module \ 92 | --with-http_stub_status_module 93 | make install 94 | popd 95 | 96 | echo "Install NAXSI default rules" 97 | mkdir -p /usr/local/openresty/naxsi/ 98 | cp "./naxsi/naxsi_config/naxsi_core.rules" /usr/local/openresty/naxsi/ 99 | 100 | echo "Installing luarocks" 101 | pushd luarocks 102 | ./configure --with-lua=/usr/local/openresty/luajit \ 103 | --lua-suffix=jit-2.1.0-beta2 \ 104 | --with-lua-include=/usr/local/openresty/luajit/include/luajit-2.1 105 | make build install 106 | popd 107 | 108 | echo "Installing luarocks packages" 109 | luarocks install uuid 110 | luarocks install luasocket 111 | luarocks install lua-resty-openssl 112 | 113 | echo "Removing unnecessary developer tooling" 114 | rm -fr openresty naxsi nginx-statsd geoip luarocks ngx_http_geoip2_module 115 | dnf -y remove \ 116 | gcc-c++ \ 117 | gcc \ 118 | git \ 119 | make \ 120 | openssl-devel \ 121 | libcurl-devel \ 122 | perl \ 123 | pcre-devel \ 124 | readline-devel 125 | 126 | dnf clean all 127 | -------------------------------------------------------------------------------- /ci-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | TAG=ngx 6 | BUILD_NUMBER="${BUILD_NUMBER:-${DRONE_BUILD_NUMBER}}" 7 | PORT="${HTTPS_LISTEN_PORT:-10443}" 8 | BUILD_NUMBER="${BUILD_NUMBER:-local}" 9 | START_INSTANCE="docker run " 10 | DOCKER_HOST_NAME="localhost" 11 | MOCKSERVER="mockserver-${BUILD_NUMBER}" 12 | SLOWMOCKSERVER="slowmockserver-${BUILD_NUMBER}" 13 | MUTUAL_TLS="mutual-tls-${BUILD_NUMBER}" 14 | STANDARD_TLS="standard-tls-${BUILD_NUMBER}" 15 | MOCKSERVER_PORT=9000 16 | SLOWMOCKSERVER_PORT=9001 17 | WORKDIR="${PWD}" 18 | 19 | function tear_down_container() { 20 | container=$1 21 | if docker ps -a | grep "${container}" &>/dev/null ; then 22 | if docker ps | grep "${container}" &>/dev/null ; then 23 | docker kill "${container}" &>/dev/null || true 24 | fi 25 | docker rm "${container}" &>/dev/null || true 26 | fi 27 | } 28 | 29 | function tear_down() { 30 | tear_down_container "${INSTANCE}" 31 | } 32 | 33 | function clean_up() { 34 | rm -f /tmp/file.txt 35 | tear_down_container "${MOCKSERVER}" 36 | tear_down_container "${SLOWMOCKSERVER}" 37 | tear_down_container "${MUTUAL_TLS}" 38 | tear_down_container "${STANDARD_TLS}" 39 | tear_down_container "${TAG}-${BUILD_NUMBER}" 40 | } 41 | 42 | function add_files_to_container() { 43 | echo "Copying files to container: $1" 44 | local CONTAINER=$1 45 | shift 46 | while [[ -n $@ ]]; do 47 | local file=$1 48 | shift 49 | local rename=$1 50 | shift 51 | local destdir=$1 52 | cp ${file} ${rename} 53 | tar -cf - ${rename} --mode u=+rw,g=+r,o=+r --owner root --group root | docker cp - ${CONTAINER}:${destdir} 54 | shift 55 | done 56 | } 57 | 58 | function start_test() { 59 | INSTANCE="${TAG}-${BUILD_NUMBER}" 60 | tear_down 61 | HTTPS_LISTEN_PORT=${HTTPS_LISTEN_PORT:-10443} 62 | echo "" 63 | echo "" 64 | echo "_____________" 65 | echo "STARTING TEST:$1" 66 | echo "=============" 67 | shift 68 | # handle files that need to be mounted in 69 | local files="" 70 | while [[ $@ != docker* ]]; do 71 | # should be in format - file destination file destination etc. 72 | files="${files} $1" 73 | shift 74 | done 75 | echo "Running: $@ --name ${INSTANCE} -p ${PORT}:${HTTPS_LISTEN_PORT} ${TAG}" 76 | bash -c "$@ --name ${INSTANCE} -d -p ${PORT}:${HTTPS_LISTEN_PORT} ${TAG}" 77 | # if files needed to be mounted in, the container stops immediately so start it again 78 | if [[ ${files} != "" ]]; then 79 | echo "${files}" 80 | add_files_to_container ${INSTANCE} ${files} 81 | docker start ${INSTANCE} 82 | fi 83 | docker run --rm --link ${INSTANCE}:${INSTANCE} martin/wait 84 | } 85 | 86 | clean_up 87 | 88 | STD_CMD="${START_INSTANCE}" 89 | 90 | echo "========" 91 | echo "BUILD..." 92 | echo "========" 93 | echo "travis_fold:start:BUILD" 94 | docker build --build-arg GEOIP_ACCOUNT_ID=${GEOIP_ACCOUNT_ID} --build-arg GEOIP_LICENSE_KEY=${GEOIP_LICENSE_KEY} -t ${TAG} . 95 | echo "travis_fold:end:BUILD" 96 | 97 | echo "Running mocking-server..." 98 | docker build -t mockserver:latest ${WORKDIR} -f docker-config/Dockerfile.mockserver 99 | ${STD_CMD} -d \ 100 | --name="${MOCKSERVER}" mockserver:latest \ 101 | -config=/test-servers.yaml \ 102 | -debug \ 103 | -port=${MOCKSERVER_PORT} 104 | docker run --rm --link "${MOCKSERVER}:${MOCKSERVER}" martin/wait -c "${MOCKSERVER}:${MOCKSERVER_PORT}" 105 | 106 | echo "Running slow-mocking-server..." 107 | docker build -t slowmockserver:latest ${WORKDIR} -f docker-config/Dockerfile.slowmockserver 108 | ${STD_CMD} -d \ 109 | --name="${SLOWMOCKSERVER}" slowmockserver:latest \ 110 | -config=/test-servers.yaml \ 111 | -monkeyConfig=/monkey-business.yaml \ 112 | -debug \ 113 | -port=${SLOWMOCKSERVER_PORT} 114 | docker run --rm --link "${SLOWMOCKSERVER}:${SLOWMOCKSERVER}" martin/wait -c "${SLOWMOCKSERVER}:${SLOWMOCKSERVER_PORT}" 115 | 116 | echo "==========" 117 | echo "TESTING..." 118 | echo "==========" 119 | 120 | start_test "Start with minimal settings" "${STD_CMD} \ 121 | -e \"PROXY_SERVICE_HOST=https://www.w3.org\" \ 122 | -e \"PROXY_SERVICE_PORT=443\"" 123 | 124 | echo "Test it's up and working..." 125 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}/ 126 | echo "Test limited protcol and SSL cipher... " 127 | docker run --link ${INSTANCE}:${INSTANCE} --rm --entrypoint bash ngx -c "echo GET / | /usr/bin/openssl s_client -cipher 'AES256+EECDH' -tls1_2 -connect ${INSTANCE}:10443" &> /dev/null; 128 | echo "Test sslv2 not accepted...." 129 | if docker run --link ${INSTANCE}:${INSTANCE} --rm --entrypoint bash ngx -c "echo GET / | /usr/bin/openssl s_client -ssl2 -connect ${INSTANCE}:10443" &> /dev/null; then 130 | echo "FAIL SSL defaults settings allow ssl2 ......" 131 | exit 2 132 | fi 133 | 134 | start_test "Test enabling GEODB settings" "${STD_CMD} \ 135 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 136 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 137 | -e \"DNSMASK=TRUE\" \ 138 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 139 | -e \"ALLOW_COUNTRY_CSV=GB,FR,O1\" \ 140 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 141 | echo "Test GeoIP config isn't rejected..." 142 | curl --fail -s -v -k https://${DOCKER_HOST_NAME}:${PORT}/ 143 | 144 | start_test "Test GEODB settings can reject..." "${STD_CMD} \ 145 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 146 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 147 | -e \"DNSMASK=TRUE\" \ 148 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 149 | -e \"ALLOW_COUNTRY_CSV=CG\" \ 150 | -e \"DENY_COUNTRY_ON=TRUE\" \ 151 | -e \"ADD_NGINX_LOCATION_CFG=error_page 403 /nginx-proxy/50x.shtml;\" \ 152 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 153 | echo "Test GeoIP config IS rejected..." 154 | if ! curl -v -k -H "X-Forwarded-For: 8.8.8.8" https://${DOCKER_HOST_NAME}:${PORT}/ 2>&1 \/ | grep '403 Forbidden' ; then 155 | echo "We were expecting to be rejected with 403 error here - we are not in the Congo!" 156 | exit 2 157 | else 158 | echo "Rejected as expected - we are not in the Congo!" 159 | fi 160 | 161 | start_test "Test rate limits 1 per second" "${STD_CMD} \ 162 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 163 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 164 | -e \"DNSMASK=TRUE\" \ 165 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 166 | -e \"REQS_PER_MIN_PER_IP=60\" \ 167 | -e \"REQS_PER_PAGE=0\" \ 168 | -e \"CONCURRENT_CONNS_PER_IP=1\" \ 169 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 170 | echo "Test two connections in the same second get blocked..." 171 | curl --fail -v -k https://${DOCKER_HOST_NAME}:${PORT}/ 172 | if curl -v -k https://${DOCKER_HOST_NAME}:${PORT}/ 2>&1 \ 173 | | grep '503 Service Temporarily Unavailable' ; then 174 | echo "Passed return text on error with REQS_PER_MIN_PER_IP" 175 | else 176 | echo "Failed return text on error with REQS_PER_MIN_PER_IP" 177 | exit 1 178 | fi 179 | 180 | start_test "Test multiple concurrent connections in the same second get blocked" "${STD_CMD} \ 181 | -e \"PROXY_SERVICE_HOST=http://${SLOWMOCKSERVER}\" \ 182 | -e \"PROXY_SERVICE_PORT=${SLOWMOCKSERVER_PORT}\" \ 183 | -e \"DNSMASK=TRUE\" \ 184 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 185 | -e \"REQS_PER_MIN_PER_IP=60\" \ 186 | -e \"REQS_PER_PAGE=0\" \ 187 | -e \"CONCURRENT_CONNS_PER_IP=1\" \ 188 | --link \"${SLOWMOCKSERVER}:${SLOWMOCKSERVER}\" " 189 | echo "First background some requests..." 190 | curl -v -k https://${DOCKER_HOST_NAME}:${PORT} &>/dev/null & 191 | curl -v -k https://${DOCKER_HOST_NAME}:${PORT} &>/dev/null & 192 | curl -v -k https://${DOCKER_HOST_NAME}:${PORT} &>/dev/null & 193 | curl -v -k https://${DOCKER_HOST_NAME}:${PORT} &>/dev/null & 194 | echo "Now test we get blocked with second concurrent request..." 195 | if curl -v -k https://${DOCKER_HOST_NAME}:${PORT}/ 2>&1 \ 196 | | grep '503 Service Temporarily Unavailable' ; then 197 | echo "Passed return text on error with CONCURRENT_CONNS_PER_IP" 198 | else 199 | echo "Failed return text on error with CONCURRENT_CONNS_PER_IP" 200 | exit 1 201 | fi 202 | 203 | start_test "Test response has gzip" "${STD_CMD} \ 204 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 205 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 206 | -e \"DNSMASK=TRUE\" \ 207 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 208 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 209 | echo "Test gzip ok..." 210 | curl -s -I -X GET -k --compressed https://${DOCKER_HOST_NAME}:${PORT}/gzip | grep -q 'Content-Encoding: gzip' 211 | 212 | start_test "Start with SSL CIPHER set and PROTOCOL" "${STD_CMD} \ 213 | -e \"PROXY_SERVICE_HOST=www.w3.org\" \ 214 | -e \"PROXY_SERVICE_PORT=80\" \ 215 | -e \"SSL_CIPHERS=DHE-RSA-AES256-SHA\" \ 216 | -e \"SSL_PROTOCOLS=TLSv1.2\"" 217 | echo "Test accepts defined protocol and cipher....." 218 | docker run --link ${INSTANCE}:${INSTANCE} --rm --entrypoint bash ngx -c "echo GET / | /usr/bin/openssl s_client -cipher 'DHE-RSA-AES256-SHA' -tls1_2 -connect ${INSTANCE}:10443" &> /dev/null; 219 | 220 | 221 | 222 | start_test "Start we auto add a protocol " "${STD_CMD} \ 223 | -e \"PROXY_SERVICE_HOST=www.w3.org\" \ 224 | -e \"PROXY_SERVICE_PORT=80\"" 225 | 226 | echo "Test it works if we do not define the protocol.." 227 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}/ 228 | 229 | start_test "Start with multi locations settings" "${STD_CMD} \ 230 | -e \"LOCATIONS_CSV=/,/wiki/Wikipedia:About\" \ 231 | -e \"PROXY_SERVICE_HOST_1=https://www.w3.org\" \ 232 | -e \"PROXY_SERVICE_PORT_1=443\" \ 233 | -e \"PROXY_SERVICE_HOST_2=https://en.wikipedia.org\" \ 234 | -e \"PROXY_SERVICE_PORT_2=443\"" 235 | 236 | 237 | echo "Test for location 1 @ /..." 238 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}/ 239 | echo "Test for wikipedia about page..." 240 | curl -sk -o /dev/null -H "Host: en.wikipedia.org" https://${DOCKER_HOST_NAME}:${PORT}/wiki/Wikipedia:About 241 | 242 | start_test "Start with Multiple locations, single proxy and NAXSI download." "${STD_CMD} \ 243 | -e \"PROXY_SERVICE_HOST=https://en.wikipedia.org\" \ 244 | -e \"PROXY_SERVICE_PORT=443\" \ 245 | -e \"LOCATIONS_CSV=/,/wiki/Wikipedia:About\" \ 246 | -e \"NAXSI_RULES_URL_CSV_1=https://raw.githubusercontent.com/nbs-system/naxsi-rules/master/drupal.rules\" \ 247 | -e \"NAXSI_RULES_MD5_CSV_1=3b3c24ed61683ab33d8441857c315432\"" 248 | 249 | echo "Test for all OK..." 250 | curl -sk -o /dev/null -H "Host: en.wikipedia.org" https://${DOCKER_HOST_NAME}:${PORT}/ 251 | 252 | echo "Test client certs..." 253 | cd ./client_certs/ 254 | ./create_ca.sh 255 | ./create_client_csr_and_key.sh 256 | ./sign_client_key_with_ca.sh 257 | cd .. 258 | start_test "Start with Client CA, and single proxy. Block unauth for /standards" \ 259 | "${WORKDIR}/client_certs/ca.crt" "client-ca" "/etc/keys/" \ 260 | "${STD_CMD} \ 261 | -e \"PROXY_SERVICE_HOST=http://www.w3.org\" \ 262 | -e \"PROXY_SERVICE_PORT=80\" \ 263 | -e \"LOCATIONS_CSV=/,/standards/\" \ 264 | -e \"CLIENT_CERT_REQUIRED_2=TRUE\" " 265 | 266 | echo "Test access OK for basic area..." 267 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}/ 268 | 269 | echo "Test access denied for /standards/..." 270 | if curl -sk -o /dev/null --fail https://${DOCKER_HOST_NAME}:${PORT}/standards/ ; then 271 | echo "Error - expecting auth fail!" 272 | exit 1 273 | else 274 | echo "Passed auth fail" 275 | fi 276 | echo "Test access OK for /standards/... with client cert..." 277 | curl -sk -o /dev/null \ 278 | --cert ./client_certs/client.crt \ 279 | --key ./client_certs/client.key \ 280 | https://${DOCKER_HOST_NAME}:${PORT}/standards/ 281 | 282 | echo "Test upstream client certs..." 283 | docker build -t mutual-tls:latest ${WORKDIR} -f docker-config/Dockerfile.mutual-tls 284 | ${STD_CMD} -d \ 285 | -e "HTTP_LISTEN_PORT=10081" \ 286 | -e "HTTPS_LISTEN_PORT=10444" \ 287 | -e "PROXY_SERVICE_HOST=http://www.w3.org" \ 288 | -e "PROXY_SERVICE_PORT=80" \ 289 | -e "CLIENT_CERT_REQUIRED=TRUE" \ 290 | -p 10444:10444 --name="${MUTUAL_TLS}" mutual-tls:latest 291 | docker run --link "${MUTUAL_TLS}:${MUTUAL_TLS}" --rm martin/wait -p 10444 292 | 293 | start_test "Start with upstream client certs" \ 294 | "${WORKDIR}/client_certs/client.crt" "upstream-client-crt" "/etc/keys/" \ 295 | "${WORKDIR}/client_certs/client.key" "upstream-client-key" "/etc/keys/" \ 296 | "${STD_CMD} \ 297 | -e \"PROXY_SERVICE_HOST=https://${MUTUAL_TLS}\" \ 298 | -e \"PROXY_SERVICE_PORT=10444\" \ 299 | -e \"DNSMASK=TRUE\" \ 300 | -e \"USE_UPSTREAM_CLIENT_CERT=TRUE\" \ 301 | --link \"${MUTUAL_TLS}:${MUTUAL_TLS}\" " 302 | 303 | echo "Test it's up and working..." 304 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}/ 305 | tear_down_container "${MUTUAL_TLS}" 306 | 307 | echo "Test failure to verify upstream server cert..." 308 | docker build -t standard-tls:latest ${WORKDIR} -f docker-config/Dockerfile.standard-tls 309 | ${STD_CMD} -d \ 310 | -e "HTTP_LISTEN_PORT=10081" \ 311 | -e "HTTPS_LISTEN_PORT=10444" \ 312 | -e "PROXY_SERVICE_HOST=http://www.w3.org" \ 313 | -e "PROXY_SERVICE_PORT=80" \ 314 | -p 10444:10444 --name="${STANDARD_TLS}" standard-tls:latest 315 | docker run --link "${STANDARD_TLS}:${STANDARD_TLS}" --rm martin/wait -p 10444 316 | 317 | start_test "Start with failing upstream server verification" \ 318 | "${WORKDIR}/client_certs/ca.crt" "upstream-server-ca" "/etc/keys/" \ 319 | "${STD_CMD} \ 320 | -e \"PROXY_SERVICE_HOST=https://${STANDARD_TLS}\" \ 321 | -e \"PROXY_SERVICE_PORT=10444\" \ 322 | -e \"DNSMASK=TRUE\" \ 323 | -e \"VERIFY_SERVER_CERT=TRUE\" \ 324 | --link \"${STANDARD_TLS}:${STANDARD_TLS}\" " 325 | 326 | echo "Test it blocks the request, returning a 502..." 327 | if curl -ki https://${DOCKER_HOST_NAME}:${PORT}/ | grep "502 Bad Gateway" ; then 328 | echo "Passed failure to verify upstream server cert" 329 | else 330 | echo "Failed failure to verify upstream server cert" 331 | exit 1 332 | fi 333 | tear_down_container "${STANDARD_TLS}" 334 | 335 | cd ./client_certs/ 336 | ./create_server_csr_and_key.sh 337 | ./sign_server_key_with_ca.sh 338 | cd .. 339 | ${STD_CMD} -d \ 340 | -e "HTTP_LISTEN_PORT=10081" \ 341 | -e "HTTPS_LISTEN_PORT=10444" \ 342 | -e "PROXY_SERVICE_HOST=http://www.w3.org" \ 343 | -e "PROXY_SERVICE_PORT=80" \ 344 | -p 10444:10444 --name="${STANDARD_TLS}" ${TAG} 345 | docker start ${STANDARD_TLS} 346 | docker run --link "${STANDARD_TLS}:${STANDARD_TLS}" --rm martin/wait -p 10444 347 | 348 | start_test "Start with succeeding upstream server verification" \ 349 | "${WORKDIR}/client_certs/ca.crt" "upstream-server-ca" "/etc/keys/" \ 350 | "${STD_CMD} \ 351 | -e \"PROXY_SERVICE_HOST=https://${STANDARD_TLS}\" \ 352 | -e \"PROXY_SERVICE_PORT=10444\" \ 353 | -e \"DNSMASK=TRUE\" \ 354 | -e \"VERIFY_SERVER_CERT=TRUE\" \ 355 | --link \"${STANDARD_TLS}:${STANDARD_TLS}\" " 356 | 357 | tear_down_container "${STANDARD_TLS}" 358 | 359 | start_test "Start with Custom error pages redirect off" "${STD_CMD} \ 360 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 361 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 362 | -e \"LOCATIONS_CSV=/,/api/\" \ 363 | -e \"ERROR_REDIRECT_CODES_2=502\" \ 364 | -e \"DNSMASK=TRUE\" \ 365 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 366 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 367 | echo "Test All ok..." 368 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}/ 369 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}/api/ 370 | if curl -v -k https://${DOCKER_HOST_NAME}:${PORT}/api/dead | grep "Oh dear" ; then 371 | echo "Passed return text on error with ERROR_REDIRECT_CODES" 372 | else 373 | echo "Failed return text on error with ERROR_REDIRECT_CODES" 374 | exit 1 375 | fi 376 | 377 | #-------------------------------------------------------------------------------------------------- 378 | # currently fails here 379 | #wget -O /dev/null --quiet --no-check-certificate https://${DOCKER_HOST_NAME}:${PORT}/ 380 | #docker ps -a --filter "status=running" | grep ${INSTANCE} 381 | #docker logs ${INSTANCE} 382 | #wget -O /dev/null --no-check-certificate https://${DOCKER_HOST_NAME}:${PORT}/ 383 | #tear_down_container "${STANDARD_TLS}" 384 | 385 | # testing stuff 386 | #clean_up 387 | #exit 388 | # ------------- 389 | 390 | start_test "Test custom error pages..." "${STD_CMD} \ 391 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 392 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 393 | -e \"DNSMASK=TRUE\" \ 394 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 395 | -e \"ERROR_REDIRECT_CODES=502 404 500\" \ 396 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 397 | if curl -k https://${DOCKER_HOST_NAME}:${PORT}/not-found | grep "404 Not Found" ; then 398 | if curl -k https://${DOCKER_HOST_NAME}:${PORT}/api/dead | grep "An error occurred" ; then 399 | echo "Passed custom error pages with ERROR_REDIRECT_CODES" 400 | else 401 | echo "Failed custom error pages with ERROR_REDIRECT_CODES on code 500" 402 | exit 1 403 | fi 404 | else 405 | echo "Failed custom error pages with ERROR_REDIRECT_CODES on code 404" 406 | exit 1 407 | fi 408 | 409 | start_test "Start with Custom upload size" "${STD_CMD} \ 410 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 411 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 412 | -e \"CLIENT_MAX_BODY_SIZE=15\" \ 413 | -e \"NAXSI_USE_DEFAULT_RULES=FALSE\" \ 414 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 415 | -e \"DNSMASK=TRUE\" \ 416 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 417 | dd if=/dev/urandom of=/tmp/file.txt bs=1048576 count=10 418 | 419 | echo "Upload a large file" 420 | curl -k -F "file=@/tmp/file.txt;filename=nameinpost" \ 421 | https://${DOCKER_HOST_NAME}:${PORT}/uploads/doc &> /tmp/upload_test.txt 422 | grep "Thanks for the big doc" /tmp/upload_test.txt &> /dev/null 423 | 424 | 425 | start_test "Start with listen for port 80" "${STD_CMD} \ 426 | -p 8888:10080 \ 427 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 428 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 429 | -e \"DNSMASK=TRUE\" \ 430 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 431 | -e \"HTTPS_REDIRECT_PORT=${PORT}\" \ 432 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 433 | echo "Test Redirect ok..." 434 | curl -s -o /dev/null http://${DOCKER_HOST_NAME}:8888/ 435 | 436 | 437 | start_test "Test text logging format..." "${STD_CMD} \ 438 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 439 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 440 | -e \"DNSMASK=TRUE\" \ 441 | -e \"LOG_FORMAT_NAME=text\" \ 442 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 443 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 444 | echo "Test request (with logging as text)..." 445 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}/ 446 | echo "Testing text logs format..." 447 | docker logs ${INSTANCE} | grep "\"GET / HTTP/1.1\" 200" 448 | 449 | start_test "Test json logging format..." "${STD_CMD} \ 450 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 451 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 452 | -e \"DNSMASK=TRUE\" \ 453 | -e \"LOG_FORMAT_NAME=json\" \ 454 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 455 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 456 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}?animal=cow 457 | echo "Testing json logs format..." 458 | docker logs ${INSTANCE} | grep '{"proxy_proto_address":' 459 | docker logs ${INSTANCE} | grep 'animal=cow' 460 | 461 | 462 | start_test "Test param logging off option works..." "${STD_CMD} \ 463 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 464 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 465 | -e \"DNSMASK=TRUE\" \ 466 | -e \"LOG_FORMAT_NAME=json\" \ 467 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 468 | -e \"NO_LOGGING_URL_PARAMS=TRUE\" \ 469 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 470 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}?animal=cow 471 | echo "Testing no logging of url params option works..." 472 | docker logs ${INSTANCE} 2>/dev/null | grep '{"proxy_proto_address":' 473 | docker logs ${INSTANCE} 2>/dev/null | grep 'animal=cow' | wc -l | grep 0 474 | 475 | start_test "Test ENABLE_WEB_SOCKETS..." "${STD_CMD} \ 476 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 477 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 478 | -e \"DNSMASK=TRUE\" \ 479 | -e \"ENABLE_WEB_SOCKETS=TRUE\" \ 480 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 481 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 482 | curl -sk -o /dev/null https://${DOCKER_HOST_NAME}:${PORT}/ 483 | 484 | start_test "Test ADD_NGINX_LOCATION_CFG param..." "${STD_CMD} \ 485 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 486 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 487 | -e \"LOCATIONS_CSV=/,/api/\" \ 488 | -e \"ADD_NGINX_LOCATION_CFG=return 200 NICE;\" \ 489 | -e \"DNSMASK=TRUE\" \ 490 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 491 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 492 | echo "Test extra param works" 493 | curl -sk https://${DOCKER_HOST_NAME}:${PORT}/wow | grep "NICE" 494 | 495 | 496 | start_test "Test UUID GET param logging option works..." "${STD_CMD} \ 497 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 498 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 499 | -e \"DNSMASK=TRUE\" \ 500 | -e \"ENABLE_UUID_PARAM=TRUE\" \ 501 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 502 | curl -sk https://${DOCKER_HOST_NAME}:${PORT} 503 | echo "Testing no logging of url params option works..." 504 | docker logs "${MOCKSERVER}" | grep '?nginxId=' 505 | docker logs ${INSTANCE} | grep '"nginx_uuid": "' 506 | 507 | start_test "Test UUID GET param logging option works with other params..." "${STD_CMD} \ 508 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 509 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 510 | -e \"DNSMASK=TRUE\" \ 511 | -e \"ENABLE_UUID_PARAM=TRUE\" \ 512 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 513 | curl -sk https://${DOCKER_HOST_NAME}:${PORT}/?foo=bar 514 | echo "Testing no logging of url params option works..." 515 | docker logs "${MOCKSERVER}" | grep '?foo=bar&nginxId=' 516 | docker logs ${INSTANCE} | grep '"nginx_uuid": "' 517 | 518 | start_test "Test UUID header logging option works..." "${STD_CMD} \ 519 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 520 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 521 | -e \"DNSMASK=TRUE\" \ 522 | -e \"ENABLE_UUID_PARAM=HEADER\" \ 523 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 524 | curl -sk https://${DOCKER_HOST_NAME}:${PORT} 525 | echo "Testing no logging of url params option works..." 526 | docker logs "${MOCKSERVER}" | grep 'nginxid->' 527 | docker logs ${INSTANCE} | grep '"nginx_uuid": "' 528 | 529 | start_test "Test UUID header logging option passes through supplied value..." "${STD_CMD} \ 530 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 531 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 532 | -e \"DNSMASK=TRUE\" \ 533 | -e \"ENABLE_UUID_PARAM=HEADER\" \ 534 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 535 | curl -sk -H "nginxId: 00000000-1111-2222-3333-444455556666" https://${DOCKER_HOST_NAME}:${PORT} 536 | echo "Testing no logging of url params option works..." 537 | docker logs "${MOCKSERVER}" | grep 'nginxid->00000000-1111-2222-3333-444455556666' 538 | docker logs ${INSTANCE} | grep '"nginx_uuid": "00000000-1111-2222-3333-444455556666"' 539 | 540 | start_test "Test VERBOSE_ERROR_PAGES=TRUE displays debug info" "${STD_CMD} \ 541 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 542 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 543 | -e \"DNSMASK=TRUE\" \ 544 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 545 | -e \"VERBOSE_ERROR_PAGES=TRUE\" \ 546 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 547 | if curl -k https://${DOCKER_HOST_NAME}:${PORT}/\?\"==\` | grep "Sorry, we are refusing to process your request." ; then 548 | echo "Testing VERBOSE_ERROR_PAGES works..." 549 | else 550 | echo "Testing VERBOSE_ERROR_PAGES failed..." 551 | exit 1 552 | fi 553 | 554 | start_test "Test VERBOSE_ERROR_PAGES is not set does not display debug info" "${STD_CMD} \ 555 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 556 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 557 | -e \"DNSMASK=TRUE\" \ 558 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 559 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 560 | if curl -k https://${DOCKER_HOST_NAME}:${PORT}/\?\"==\` | grep "Sorry, we are refusing to process your request." ; then 561 | echo "Testing VERBOSE_ERROR_PAGES failed..." 562 | exit 1 563 | else 564 | echo "Testing VERBOSE_ERROR_PAGES works..." 565 | fi 566 | 567 | start_test "Test VERBOSE_ERROR_PAGES is not set displays default message info" "${STD_CMD} \ 568 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 569 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 570 | -e \"DNSMASK=TRUE\" \ 571 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 572 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 573 | if curl -k https://${DOCKER_HOST_NAME}:${PORT}/\?\"==\` | grep "Something went wrong." ; then 574 | echo "Testing VERBOSE_ERROR_PAGES works..." 575 | else 576 | echo "Testing VERBOSE_ERROR_PAGES failed..." 577 | exit 1 578 | fi 579 | 580 | start_test "Test FEEDBACK_EMAIL is set, displays contact message info" "${STD_CMD} \ 581 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 582 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 583 | -e \"DNSMASK=TRUE\" \ 584 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 585 | -e \"FEEDBACK_EMAIL=test@test.com\" \ 586 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 587 | if curl -k https://${DOCKER_HOST_NAME}:${PORT}/\?\"==\` | grep "test@test.com" ; then 588 | echo "Testing VERBOSE_ERROR_PAGES works..." 589 | else 590 | echo "Testing VERBOSE_ERROR_PAGES failed..." 591 | exit 1 592 | fi 593 | 594 | start_test "Test FEEDBACK_EMAIL is not set, does not display email message info" "${STD_CMD} \ 595 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 596 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 597 | -e \"DNSMASK=TRUE\" \ 598 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 599 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 600 | if curl -k https://${DOCKER_HOST_NAME}:${PORT}/\?\"==\` | grep "please contact us on" ; then 601 | echo "Testing VERBOSE_ERROR_PAGES failed..." 602 | exit 1 603 | else 604 | echo "Testing VERBOSE_ERROR_PAGES works..." 605 | fi 606 | 607 | start_test "Test to ensure HTTP/2 is enabled when HTTP2 is set to true" "${STD_CMD} \ 608 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 609 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 610 | -e \"DNSMASK=TRUE\" \ 611 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 612 | -e \"HTTP2=TRUE\" \ 613 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 614 | if curl -kv https://${DOCKER_HOST_NAME}:${PORT}/ 2>&1 | grep 'HTTP/2 200' ; then 615 | echo "Testing HTTP2 Works" 616 | else 617 | echo "HTTP2 didnt work" 618 | exit 1 619 | fi 620 | 621 | start_test "Test to ensure HTTP/2 is disabled when HTTP2 is set to false" "${STD_CMD} \ 622 | -e \"PROXY_SERVICE_HOST=http://${MOCKSERVER}\" \ 623 | -e \"PROXY_SERVICE_PORT=${MOCKSERVER_PORT}\" \ 624 | -e \"DNSMASK=TRUE\" \ 625 | -e \"ENABLE_UUID_PARAM=FALSE\" \ 626 | -e \"HTTP2=FALSE\" \ 627 | --link \"${MOCKSERVER}:${MOCKSERVER}\" " 628 | if ! curl -kv https://${DOCKER_HOST_NAME}:${PORT}/ 2>&1 | grep 'HTTP/2 200' ; then 629 | echo "Testing HTTP2 FALSE Flag Works" 630 | else 631 | echo "HTTP2 FALSE didnt work" 632 | exit 1 633 | fi 634 | 635 | echo "_________________________________" 636 | echo "We got here, ALL tests successful" 637 | clean_up 638 | -------------------------------------------------------------------------------- /client_certs/.gitignore: -------------------------------------------------------------------------------- 1 | *.crt 2 | *.csr 3 | *.key 4 | -------------------------------------------------------------------------------- /client_certs/create_ca.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./settings.cfg 3 | openssl genrsa -out ca.key 4096 4 | openssl req -new -x509 -days 730 -key ca.key -subj "${DN}/CN=FDCS" -out ca.crt 5 | -------------------------------------------------------------------------------- /client_certs/create_client_csr_and_key.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./settings.cfg 3 | openssl genrsa -out client.key 4096 4 | openssl req -new -key client.key -subj "${DN}/CN=Test Client" -out client.csr -------------------------------------------------------------------------------- /client_certs/create_server_csr_and_key.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | . ./settings.cfg 3 | openssl genrsa -out server.key 4096 4 | openssl req -new -key server.key -subj "${DN}/CN=standard-tls" -out server.csr 5 | -------------------------------------------------------------------------------- /client_certs/settings.cfg: -------------------------------------------------------------------------------- 1 | DN="/C=GB/ST=London/L=London/O=Home Office/OU=IT" -------------------------------------------------------------------------------- /client_certs/sign_client_key_with_ca.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt -------------------------------------------------------------------------------- /client_certs/sign_server_key_with_ca.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt 3 | -------------------------------------------------------------------------------- /code_of_conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open and 4 | welcoming community, we pledge to respect all people who contribute through reporting issues, 5 | posting feature requests, updating documentation, submitting pull requests or patches, and other 6 | activities. 7 | 8 | We are committed to making participation in this project a harassment-free experience for everyone, 9 | regardless of level of experience, gender, gender identity and expression, sexual orientation, 10 | disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. 11 | 12 | Examples of unacceptable behavior by participants include: 13 | 14 | * The use of sexualized language or imagery 15 | * Personal attacks 16 | * Trolling or insulting/derogatory comments 17 | * Public or private harassment 18 | * Publishing other's private information, such as physical or electronic addresses, without explicit 19 | permission 20 | * Other unethical or unprofessional conduct. 21 | 22 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, 23 | code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By 24 | adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently 25 | applying these principles to every aspect of managing this project. Project maintainers who do not 26 | follow or enforce the Code of Conduct may be permanently removed from the project team. 27 | 28 | This code of conduct applies both within project spaces and in public spaces when an individual is 29 | representing the project or its community. 30 | 31 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an 32 | issue or contacting one or more of the project maintainers. 33 | 34 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), 35 | version 1.2.0, available at 36 | [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) 37 | -------------------------------------------------------------------------------- /defaults.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | export NGIX_CONF_DIR=/usr/local/openresty/nginx/conf 3 | export NGINX_BIN=/usr/local/openresty/nginx/sbin/nginx 4 | export UUID_FILE=/tmp/uuid_on 5 | export DEFAULT_ERROR_CODES="500 501 502 503 504" 6 | export LOG_FORMAT_NAME=${LOG_FORMAT_NAME:-json} 7 | export SERVER_CERT=${SERVER_CERT:-/etc/keys/crt} 8 | export SERVER_KEY=${SERVER_KEY:-/etc/keys/key} 9 | export SSL_CIPHERS=${SSL_CIPHERS:-'AES256+EECDH:AES256+EDH:!aNULL'} 10 | export SSL_PROTOCOLS=${SSL_PROTOCOLS:-'TLSv1.2'} 11 | export SSL_SESSION_TIMEOUT=${SSL_SESSION_TIMEOUT:-'10m'} 12 | export HTTP_LISTEN_PORT=${HTTP_LISTEN_PORT:-10080} 13 | export HTTPS_LISTEN_PORT=${HTTPS_LISTEN_PORT:-10443} 14 | export HTTP2=${HTTP2:-'FALSE'} 15 | export HTTPS_REDIRECT=${HTTPS_REDIRECT:-'TRUE'} 16 | export NO_LOGGING_BODY=${NO_LOGGING_BODY:-'TRUE'} 17 | export NO_LOGGING_RESPONSE=${NO_LOGGING_RESPONSE:-'TRUE'} 18 | export STATSD_METRICS=${STATSD_METRICS:-'TRUE'} 19 | export STATSD_SERVER=${STATSD_SERVER:-'127.0.0.1'} 20 | 21 | export HTTPS_REDIRECT_PORT_STRING=":${HTTPS_REDIRECT_PORT}" 22 | if [ "${HTTPS_REDIRECT_PORT_STRING}" == ":" ]; then 23 | export HTTPS_REDIRECT_PORT_STRING="" 24 | fi 25 | 26 | function download() { 27 | 28 | file_url=$1 29 | if [ $# -eq 3 ]; then 30 | file_md5=$2 31 | download_path=$3 32 | else 33 | download_path=$2 34 | fi 35 | 36 | file_path=${download_path}/$(basename ${file_url}) 37 | error=0 38 | 39 | for i in {1..5}; do 40 | if [ ${i} -gt 1 ]; then 41 | msg "About to retry download for ${file_url}..." 42 | sleep 1 43 | fi 44 | if curl --max-time 30 --fail -s -o ${file_path} ${file_url} ; then 45 | error=0 46 | fi 47 | if [ -n ${file_md5} ]; then 48 | md5=$(md5sum ${file_path} | cut -d' ' -f1) 49 | 50 | if [ "${md5}" == "${file_md5}" ] ; then 51 | error=0 52 | else 53 | msg "Error: MD5 expecting '${file_md5}' but got '${md5}' for ${file_url}" 54 | error=1 55 | fi 56 | fi 57 | if [ ${error} -eq 0 ]; then 58 | msg "File downloaded & OK:${file_url}" 59 | break 60 | fi 61 | done 62 | return ${error} 63 | } 64 | 65 | function get_id_var() { 66 | LOCATION_ID=$1 67 | VAR_NAME=$2 68 | NEW_VAR_NAME="${VAR_NAME}_${LOCATION_ID}" 69 | if [ "${!NEW_VAR_NAME}" == "" ]; then 70 | NEW_VAR_NAME=${VAR_NAME} 71 | fi 72 | echo ${!NEW_VAR_NAME} 73 | } 74 | 75 | function msg() { 76 | if [ "${LOCATION}" != "" ]; then 77 | LOC_TXT=${LOCATION_ID}:${LOCATION}: 78 | fi 79 | echo "SETUP:${LOC_TXT}$1" 80 | } 81 | 82 | function exit_error_msg() { 83 | echo "ERROR:$1" 84 | exit 1 85 | } 86 | -------------------------------------------------------------------------------- /docker-config/Dockerfile.mockserver: -------------------------------------------------------------------------------- 1 | FROM quii/mockingjay-server:1.12.0 2 | 3 | COPY test-servers.yaml /test-servers.yaml 4 | -------------------------------------------------------------------------------- /docker-config/Dockerfile.mutual-tls: -------------------------------------------------------------------------------- 1 | FROM ngx 2 | 3 | COPY client_certs/ca.crt /etc/keys/client-ca 4 | 5 | EXPOSE 10081 10444 6 | -------------------------------------------------------------------------------- /docker-config/Dockerfile.slowmockserver: -------------------------------------------------------------------------------- 1 | FROM quii/mockingjay-server:1.12.0 2 | 3 | COPY test-servers.yaml /test-servers.yaml 4 | COPY monkey-business.yaml /monkey-business.yaml 5 | -------------------------------------------------------------------------------- /docker-config/Dockerfile.standard-tls: -------------------------------------------------------------------------------- 1 | FROM ngx 2 | 3 | USER 0 4 | 5 | COPY client_certs/ca.crt /etc/keys/crt 6 | COPY client_certs/ca.key /etc/keys/key 7 | RUN chmod 644 /etc/keys/* 8 | 9 | USER 1000 10 | 11 | EXPOSE 10081 10444 -------------------------------------------------------------------------------- /docs/GeneratedConfigs.md: -------------------------------------------------------------------------------- 1 | ## Example Generated Configurations: 2 | 3 | ### Multiple locations 4 | 5 | #### Two separate proxied servers 6 | 7 | The command below will proxy two separate web servers at separate addresses: 8 | 9 | ``` 10 | docker run -e 'LOCATIONS_CSV=/,/api' \ 11 | -e 'PROXY_SERVICE_HOST_1=myapp.svc.cluster.local' \ 12 | -e 'PROXY_SERVICE_PORT_1=8080' \ 13 | -e 'PROXY_SERVICE_HOST_2=myapi.svc.cluster.local' \ 14 | -e 'PROXY_SERVICE_PORT_2=8888' \ 15 | -d \ 16 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0 17 | ``` 18 | 19 | The configurations below are generated: 20 | 21 | ``` 22 | location / { 23 | set $args $args$uuidopt; 24 | 25 | 26 | set $proxy_address "myapp.svc.cluster.local:8080"; 27 | 28 | include /usr/local/openresty/naxsi/locations/1/*.rules ; 29 | 30 | 31 | set $backend_upstream "http://$proxy_address"; 32 | proxy_pass $backend_upstream; 33 | proxy_redirect off; 34 | proxy_intercept_errors on; 35 | proxy_set_header X-Real-IP $remote_addr; 36 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 37 | proxy_set_header X-Forwarded-Proto $scheme; 38 | proxy_set_header Host $host:$server_port; 39 | proxy_set_header X-Username ""; 40 | } 41 | location /news { 42 | set $args $args$uuidopt; 43 | 44 | 45 | set $proxy_address "myapi.svc.cluster.local:8888"; 46 | 47 | include /usr/local/openresty/naxsi/locations/2/*.rules ; 48 | 49 | 50 | set $backend_upstream "http://$proxy_address"; 51 | proxy_pass $backend_upstream; 52 | proxy_redirect off; 53 | proxy_intercept_errors on; 54 | proxy_set_header X-Real-IP $remote_addr; 55 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 56 | proxy_set_header X-Forwarded-Proto $scheme; 57 | proxy_set_header Host $host:$server_port; 58 | proxy_set_header X-Username ""; 59 | } 60 | ``` 61 | 62 | #### Same server proxied 63 | 64 | Given the command below to proxy one web server and the two locations: 65 | 66 | ``` 67 | docker run --rm=true -it -p 8443:443 \ 68 | -e 'LOCATIONS_CSV=/,/news' \ 69 | -e 'PROXY_SERVICE_HOST=www.bbc.co.uk' \ 70 | -e 'PROXY_SERVICE_PORT=80' \ 71 | -e 'ENABLE_UUID_PARAM_2=FALSE' \ 72 | -e 'PORT_IN_HOST_HEADER_1=FALSE' \ 73 | quay.io/ukhomeofficedigital/nginx-proxy:v1.0.0` 74 | ``` 75 | 76 | The configuration below is generated for `/`. Note specifically the `PORT_IN_HOST_HEADER_1` option above and that the 77 | port is missing from the line `proxy_set_header Host $host;` in the configuration below. 78 | 79 | ``` 80 | cat /usr/local/openresty/nginx/conf/locations/1.conf 81 | location / { 82 | set $args $args$uuidopt; 83 | 84 | 85 | set $proxy_address "www.bbc.co.uk:80"; 86 | 87 | include /usr/local/openresty/naxsi/locations/1/*.rules ; 88 | 89 | 90 | set $backend_upstream "http://$proxy_address"; 91 | proxy_pass $backend_upstream; 92 | proxy_redirect off; 93 | proxy_intercept_errors on; 94 | proxy_set_header X-Real-IP $remote_addr; 95 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 96 | proxy_set_header X-Forwarded-Proto $scheme; 97 | proxy_set_header Host $host; 98 | proxy_set_header X-Username ""; 99 | } 100 | ``` 101 | 102 | The configuration below is generated for `/news`. Note specifically the options `ENABLE_UUID_PARAM_2=FALSE` and setting 103 | missing from below: `set $args $args$uuidopt;` but present above. 104 | ``` 105 | cat /usr/local/openresty/nginx/conf/locations/2.conf 106 | location /news { 107 | 108 | 109 | 110 | set $proxy_address "www.bbc.co.uk:80"; 111 | 112 | include /usr/local/openresty/naxsi/locations/2/*.rules ; 113 | 114 | 115 | set $backend_upstream "http://$proxy_address"; 116 | proxy_pass $backend_upstream; 117 | proxy_redirect off; 118 | proxy_intercept_errors on; 119 | proxy_set_header X-Real-IP $remote_addr; 120 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 121 | proxy_set_header X-Forwarded-Proto $scheme; 122 | proxy_set_header Host $host:$server_port; 123 | proxy_set_header X-Username ""; 124 | } 125 | ``` -------------------------------------------------------------------------------- /enable_location.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | . /defaults.sh 6 | 7 | # TODO have an identifier to resolve all variables if present: 8 | 9 | LOCATION_ID=$1 10 | LOCATION=$2 11 | NAXSI_LOCATION_RULES=/usr/local/openresty/naxsi/locations/${LOCATION_ID} 12 | mkdir -p ${NAXSI_LOCATION_RULES} 13 | 14 | # Resolve any location specific variable names here: 15 | PROXY_SERVICE_HOST=$(get_id_var ${LOCATION_ID} PROXY_SERVICE_HOST) 16 | PROXY_SERVICE_PORT=$(get_id_var ${LOCATION_ID} PROXY_SERVICE_PORT) 17 | NAXSI_RULES_URL_CSV=$(get_id_var ${LOCATION_ID} NAXSI_RULES_URL_CSV) 18 | NAXSI_RULES_MD5_CSV=$(get_id_var ${LOCATION_ID} NAXSI_RULES_MD5_CSV) 19 | NAXSI_USE_DEFAULT_RULES=$(get_id_var ${LOCATION_ID} NAXSI_USE_DEFAULT_RULES) 20 | EXTRA_NAXSI_RULES=$(get_id_var ${LOCATION_ID} EXTRA_NAXSI_RULES) 21 | CLIENT_CERT_REQUIRED=$(get_id_var ${LOCATION_ID} CLIENT_CERT_REQUIRED) 22 | USE_UPSTREAM_CLIENT_CERT=$(get_id_var ${LOCATION_ID} USE_UPSTREAM_CLIENT_CERT) 23 | VERIFY_SERVER_CERT=$(get_id_var ${LOCATION_ID} VERIFY_SERVER_CERT) 24 | PORT_IN_HOST_HEADER=$(get_id_var ${LOCATION_ID} PORT_IN_HOST_HEADER) 25 | ENABLE_UUID_PARAM=$(get_id_var ${LOCATION_ID} ENABLE_UUID_PARAM) 26 | ERROR_REDIRECT_CODES=$(get_id_var ${LOCATION_ID} ERROR_REDIRECT_CODES) 27 | ENABLE_WEB_SOCKETS=$(get_id_var ${LOCATION_ID} ENABLE_WEB_SOCKETS) 28 | ADD_NGINX_LOCATION_CFG=$(get_id_var ${LOCATION_ID} ADD_NGINX_LOCATION_CFG) 29 | BASIC_AUTH=$(get_id_var ${LOCATION_ID} BASIC_AUTH) 30 | REQS_PER_MIN_PER_IP=$(get_id_var ${LOCATION_ID} REQS_PER_MIN_PER_IP) 31 | REQS_PER_PAGE=$(get_id_var ${LOCATION_ID} REQS_PER_PAGE) 32 | CONCURRENT_CONNS_PER_IP=$(get_id_var ${LOCATION_ID} CONCURRENT_CONNS_PER_IP) 33 | DENY_COUNTRY_ON=$(get_id_var ${LOCATION_ID} DENY_COUNTRY_ON) 34 | 35 | # Backwards compatability 36 | # This tests for the presence of :// which if missing means we do nt have 37 | # a protocol so we default to http:// 38 | if [ "`echo ${PROXY_SERVICE_HOST} | grep '://'`" = "" ]; then 39 | PROXY_SERVICE_HOST="http://${PROXY_SERVICE_HOST}" 40 | fi 41 | 42 | msg "Setting up location '${LOCATION}' to be proxied to " \ 43 | "${PROXY_SERVICE_HOST}:${PROXY_SERVICE_PORT}${LOCATION}" 44 | 45 | eval PROXY_HOST=$(eval "echo $PROXY_SERVICE_HOST") 46 | export PROXY_SERVICE_PORT=$(eval "echo $PROXY_SERVICE_PORT") 47 | 48 | # Detect default configuration... 49 | md5sum ${NGIX_CONF_DIR}/nginx.conf | cut -d' ' -f 1 >/tmp/nginx_new 50 | if diff /container_default_ngx /tmp/nginx_new ; then 51 | if [ "$PROXY_SERVICE_HOST" == "" ] || [ "$PROXY_SERVICE_PORT" == "" ] || [ "$PROXY_HOST" == "" ]; then 52 | echo "Default config requires PROXY_SERVICE_HOST and PROXY_SERVICE_PORT to be set." 53 | echo "PROXY_SERVICE_HOST=$PROXY_HOST" 54 | echo "PROXY_SERVICE_PORT=$PROXY_SERVICE_PORT" 55 | exit 1 56 | fi 57 | msg "Proxying to : $PROXY_SERVICE_HOST:$PROXY_SERVICE_PORT" 58 | fi 59 | 60 | 61 | if [ "${NAXSI_RULES_URL_CSV}" != "" ]; then 62 | if [ "${NAXSI_RULES_MD5_CSV}" == "" ]; then 63 | exit_error_msg "Error, must specify NAXSI_RULES_MD5_CSV if NAXSI_RULES_URL_CSV is specified" 64 | fi 65 | IFS=',' read -a NAXSI_RULES_URL_ARRAY <<< "$NAXSI_RULES_URL_CSV" 66 | IFS=',' read -a NAXSI_RULES_MD5_ARRAY <<< "$NAXSI_RULES_MD5_CSV" 67 | if [ ${#NAXSI_RULES_URL_ARRAY[@]} -ne ${#NAXSI_RULES_MD5_ARRAY[@]} ]; then 68 | exit_error_msg "Must specify the same number of items in \$NAXSI_RULES_URL_CSV and \$NAXSI_RULES_MD5_CSV" 69 | fi 70 | for i in "${!NAXSI_RULES_URL_ARRAY[@]}"; do 71 | download ${NAXSI_RULES_URL_ARRAY[$i]} ${NAXSI_RULES_MD5_ARRAY[$i]} ${NAXSI_LOCATION_RULES} 72 | done 73 | fi 74 | if [ "${NAXSI_USE_DEFAULT_RULES}" == "FALSE" ]; then 75 | msg "Not setting up NAXSI default rules for location:'${LOCATION}'" 76 | else 77 | msg "Core NAXSI rules enabled @ /usr/local/openresty/naxsi/naxsi_core.rules" 78 | msg "NAXSI location rules enabled @ ${NAXSI_LOCATION_RULES}/${LOCATION_ID}.rules" 79 | cp /usr/local/openresty/naxsi/location.template ${NAXSI_LOCATION_RULES}/${LOCATION_ID}.rules 80 | if [ "${EXTRA_NAXSI_RULES}" != "" ]; then 81 | msg "Adding extra NAXSI rules from environment" 82 | echo ''>>${NAXSI_LOCATION_RULES}/location.rules 83 | echo ${EXTRA_NAXSI_RULES}>>${NAXSI_LOCATION_RULES}/${LOCATION_ID}.rules 84 | fi 85 | fi 86 | # creates .htpasswd file from file 87 | if [[ "${BASIC_AUTH}" == "" ]]; then 88 | 89 | echo "Basic Auth not set for Location $LOCATION_ID, skipping..." 90 | else 91 | HTPASSWD=$(dirname ${BASIC_AUTH}) 92 | if [ -f "$HTPASSWD/.htpasswd_${LOCATION_ID}" ]; then #has the htpasswd file already been created. 93 | echo "$HTPASSWD/.htpasswd_$LOCATION_ID already created, skipping" 94 | else 95 | echo "Creating .htpasswd file from ${BASIC_AUTH} in location ${LOCATION_ID}" 96 | sed -i '/^$/d' ${BASIC_AUTH} #remove all empty lines. 97 | while IFS= read line 98 | do 99 | #for every line in the file add user and password to .htpasswd 100 | USER=$(echo $line | cut -d ":" -f 1 | tr -d '[[:space:]]') 101 | PASSWORD=$(echo $line | cut -d ":" -f 2 | tr -d '[[:space:]]') #remove whitespace from lines. 102 | printf "$USER:$(openssl passwd -crypt $PASSWORD)\n" >> ${HTPASSWD}/.htpasswd_$LOCATION_ID 103 | done < ${BASIC_AUTH} 104 | rm ${BASIC_AUTH} #delete file now not needed 105 | fi 106 | BASIC_AUTH_CONFIG="auth_basic \"Restricted\"; auth_basic_user_file $HTPASSWD/.htpasswd_$LOCATION_ID;" 107 | fi 108 | if [ "${CLIENT_CERT_REQUIRED}" == "TRUE" ]; then 109 | if [ ! -f /etc/keys/client-ca ]; then 110 | exit_error_msg "Missing client CA cert at location:/etc/keys/client-ca" 111 | fi 112 | msg "Denying access to '${LOCATION}' for clients with no certs." 113 | CERT_TXT="if (\$ssl_client_verify != SUCCESS) { return 403; }" 114 | export LOAD_CLIENT_CA=TRUE 115 | else 116 | CERT_TXT="" 117 | fi 118 | if [ "${USE_UPSTREAM_CLIENT_CERT}" == "TRUE" ]; then 119 | if [ ! -f /etc/keys/upstream-client-crt ]; then 120 | exit_error_msg "Missing client public cert, for upstream server, at location:/etc/keys/upstream-client-crt" 121 | elif [ ! -f /etc/keys/upstream-client-key ]; then 122 | exit_error_msg "Missing client private key, for upstream server, at location:/etc/keys/upstream-client-key" 123 | fi 124 | msg "Will use upstream client certs for '${LOCATION}'." 125 | SSL_CERTIFICATE="proxy_ssl_certificate /etc/keys/upstream-client-crt; proxy_ssl_certificate_key /etc/keys/upstream-client-key;" 126 | else 127 | SSL_CERTIFICATE="" 128 | fi 129 | if [ "${VERIFY_SERVER_CERT}" == "TRUE" ]; then 130 | if [ ! -f /etc/keys/upstream-server-ca ]; then 131 | exit_error_msg "Missing server CA cert at location:/etc/keys/upstream-server-ca" 132 | fi 133 | msg "Will require '${LOCATION}'s certificate to be verified." 134 | SSL_VERIFY="proxy_ssl_trusted_certificate /etc/keys/upstream-server-ca; proxy_ssl_verify on;" 135 | else 136 | SSL_VERIFY="" 137 | fi 138 | 139 | if [ "${PORT_IN_HOST_HEADER}" == "FALSE" ]; then 140 | msg "Setting host only proxy header" 141 | PROXY_HOST_SETTING='$host' 142 | else 143 | msg "Setting host and port proxy header" 144 | PROXY_HOST_SETTING='$host:$server_port' 145 | fi 146 | if [ "${ENABLE_UUID_PARAM}" == "FALSE" ]; then 147 | UUID_ARGS='' 148 | msg "Auto UUID request parameter disabled for location ${LOCATION_ID}." 149 | elif [ "${ENABLE_UUID_PARAM}" == "HEADER" ]; then 150 | UUID_ARGS='proxy_set_header nginxId $uuidopt;' 151 | # Ensure nginx enables this globaly 152 | msg "Auto UUID request header enabled for location ${LOCATION_ID}." 153 | touch ${UUID_FILE} 154 | else 155 | UUID_ARGS='if ($is_args) {set $args $args&nginxId=$uuidopt;} if ($is_args = "") { set $args nginxId=$uuidopt;}' 156 | # Ensure nginx enables this globaly 157 | msg "Auto UUID request parameter enabled for location ${LOCATION_ID}." 158 | touch ${UUID_FILE} 159 | fi 160 | 161 | if [ "${ERROR_REDIRECT_CODES}" == "" ]; then 162 | ERROR_REDIRECT_CODES="${DEFAULT_ERROR_CODES}" 163 | fi 164 | ERROR_PAGES="" 165 | if [ "${ERROR_REDIRECT_CODES}" != "FALSE" ]; then 166 | for code in ${ERROR_REDIRECT_CODES}; do 167 | # Set up an individual error page for each code 168 | msg "Enabling redirect on status code: ${code}" 169 | ERROR_PAGES="${ERROR_PAGES} error_page ${code} /nginx-proxy/${code}.shtml;" 170 | done 171 | fi 172 | 173 | if [ "${ENABLE_WEB_SOCKETS}" == "TRUE" ]; then 174 | msg "Enable web socket support" 175 | WEB_SOCKETS="include ${NGIX_CONF_DIR}/nginx_web_sockets_proxy.conf;" 176 | else 177 | unset WEB_SOCKETS 178 | fi 179 | if [ "${ADD_NGINX_LOCATION_CFG}" != "" ]; then 180 | msg "Enabling extra ADD_NGINX_LOCATION_CFG:${ADD_NGINX_LOCATION_CFG}" 181 | fi 182 | #nginx_var_for_loc=$(get_namefrom_number ${LOCATION_ID}) 183 | if [ "${REQS_PER_MIN_PER_IP}" != "" ]; then 184 | REQS_PER_PAGE=${REQS_PER_PAGE:-20} 185 | msg "Enabling REQS_PER_MIN_PER_IP:${REQS_PER_MIN_PER_IP}" 186 | msg "Enabling REQS_PER_PAGE:${REQS_PER_PAGE}" 187 | if [ "${REQS_PER_PAGE}" != "0" ]; then 188 | burst_setting="burst=${REQS_PER_PAGE}" 189 | else 190 | unset burst_setting 191 | fi 192 | echo "limit_req_zone \$${REMOTE_IP_VAR} zone=reqsbuffer${LOCATION_ID}:10m rate=${REQS_PER_MIN_PER_IP}r/m;" \ 193 | >${NGIX_CONF_DIR}/nginx_rate_limits_${LOCATION_ID}.conf 194 | REQ_LIMITS="limit_req zone=reqsbuffer${LOCATION_ID} ${burst_setting};" 195 | fi 196 | if [ "${CONCURRENT_CONNS_PER_IP}" != "" ]; then 197 | msg "Enabling CONCURRENT_CONNS_PER_IP:${CONCURRENT_CONNS_PER_IP}" 198 | echo "limit_conn_zone \$${REMOTE_IP_VAR} zone=connbuffer${LOCATION_ID}:10m;" \ 199 | >>${NGIX_CONF_DIR}/nginx_rate_limits_${LOCATION_ID}.conf 200 | CONN_LIMITS="limit_conn connbuffer${LOCATION_ID} ${CONCURRENT_CONNS_PER_IP};" 201 | fi 202 | if [ "${DENY_COUNTRY_ON}" == "TRUE" ]; then 203 | msg "Enabling GeoIP denies, unless IP is one of ${ALLOW_COUNTRY_CSV}, for location ${LOCATION_ID}." 204 | DENY_COUNTRY="if (\$allowed_country = no) { return 403; }" 205 | fi 206 | 207 | # Now create the location specific include file. 208 | cat > /usr/local/openresty/nginx/conf/locations/${LOCATION_ID}.conf <<- EOF_LOCATION_CONF 209 | location ${LOCATION} { 210 | ${REQ_LIMITS} 211 | ${CONN_LIMITS} 212 | ${UUID_ARGS} 213 | ${CERT_TXT} 214 | ${ADD_NGINX_LOCATION_CFG} 215 | ${BASIC_AUTH_CONFIG} 216 | ${DENY_COUNTRY} 217 | 218 | ${ERROR_PAGES} 219 | 220 | set \$proxy_address "${PROXY_SERVICE_HOST}:${PROXY_SERVICE_PORT}"; 221 | 222 | include ${NAXSI_LOCATION_RULES}/*.rules ; 223 | 224 | ${WEB_SOCKETS} 225 | $(cat /location_template.conf) 226 | ${SSL_CERTIFICATE} 227 | ${SSL_VERIFY} 228 | proxy_set_header Host ${PROXY_HOST_SETTING}; 229 | proxy_set_header X-Username "$ssl_client_s_dn_cn"; 230 | proxy_set_header X-Real-IP \$${REMOTE_IP_VAR}; 231 | 232 | } 233 | EOF_LOCATION_CONF 234 | -------------------------------------------------------------------------------- /go.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | export LOG_UUID=FALSE 6 | 7 | . /defaults.sh 8 | 9 | if [ "$HTTP2" = "TRUE" ]; then 10 | HTTP2="http2" 11 | else 12 | HTTP2="" 13 | fi 14 | 15 | 16 | cat > ${NGIX_CONF_DIR}/server_certs.conf <<-EOF_CERT_CONF 17 | ssl_certificate ${SERVER_CERT}; 18 | ssl_certificate_key ${SERVER_KEY}; 19 | # Can add SSLv3 for IE 6 but this opens up to poodle 20 | ssl_protocols ${SSL_PROTOCOLS}; 21 | # reduction to only the best ciphers 22 | # And make sure we prefer them 23 | ssl_ciphers ${SSL_CIPHERS}; 24 | ssl_prefer_server_ciphers on; 25 | ssl_session_cache shared:SSL:10m; 26 | ssl_session_timeout ${SSL_SESSION_TIMEOUT}; 27 | ssl_stapling on; 28 | ssl_dhparam ${NGIX_CONF_DIR}/dhparam.pem; 29 | EOF_CERT_CONF 30 | 31 | : "${LOCATIONS_CSV:=/}" 32 | 33 | NGIX_LISTEN_CONF="${NGIX_CONF_DIR}/nginx_listen.conf" 34 | 35 | cat > ${NGIX_LISTEN_CONF} <<-EOF-LISTEN 36 | set \$http_listen_port '${HTTP_LISTEN_PORT}'; 37 | set \$https_listen_port '${HTTPS_LISTEN_PORT}'; 38 | EOF-LISTEN 39 | 40 | if [ "${CUSTOM_SECURITY_DEFAULTS:-}" == "TRUE" ]; then 41 | msg "Disabling inbuilt security headers add per location" 42 | > /usr/local/openresty/nginx/conf/security_defaults.conf 43 | fi 44 | 45 | if [ -n "${LOAD_BALANCER_CIDR:-}" ]; then 46 | msg "Using proxy_protocol from '$LOAD_BALANCER_CIDR' (real client ip is forwarded correctly by loadbalancer)..." 47 | export REMOTE_IP_VAR="proxy_protocol_addr" 48 | cat >> ${NGIX_LISTEN_CONF} <<-EOF-LISTEN-PP 49 | listen ${HTTP_LISTEN_PORT} proxy_protocol; 50 | listen ${HTTPS_LISTEN_PORT} proxy_protocol ssl; 51 | real_ip_recursive on; 52 | real_ip_header proxy_protocol; 53 | set \$real_client_ip_if_set '\$proxy_protocol_addr '; 54 | set_real_ip_from ${LOAD_BALANCER_CIDR}; 55 | EOF-LISTEN-PP 56 | else 57 | msg "No \$LOAD_BALANCER_CIDR set, using straight SSL (client ip will be from loadbalancer if used)..." 58 | export REMOTE_IP_VAR="remote_addr" 59 | cat >> ${NGIX_LISTEN_CONF} <<-EOF-LISTEN-NONPP 60 | listen ${HTTP_LISTEN_PORT}; 61 | listen ${HTTPS_LISTEN_PORT} ssl ${HTTP2}; 62 | set \$real_client_ip_if_set ''; 63 | EOF-LISTEN-NONPP 64 | fi 65 | 66 | NGIX_SYSDIG_SERVER_CONF="${NGIX_CONF_DIR}/nginx_sysdig_server.conf" 67 | touch ${NGIX_SYSDIG_SERVER_CONF} 68 | if [ -n "${DISABLE_SYSDIG_METRICS:-}" ]; then 69 | cat > ${NGIX_SYSDIG_SERVER_CONF} <<-EOF-SYSDIG-SERVER 70 | server { 71 | listen 10088; 72 | location /nginx_status { 73 | stub_status on; 74 | access_log off; 75 | allow 127.0.0.1; 76 | deny all; 77 | } 78 | } 79 | EOF-SYSDIG-SERVER 80 | fi 81 | 82 | if [ "${CUSTOM_PROXY_CONFIG}" != "TRUE" ]; then 83 | IFS=',' read -a LOCATIONS_ARRAY <<< "$LOCATIONS_CSV" 84 | for i in "${!LOCATIONS_ARRAY[@]}"; do 85 | /enable_location.sh $((${i} + 1)) ${LOCATIONS_ARRAY[$i]} 86 | done 87 | fi 88 | 89 | if [ -z "${NAME_RESOLVER:-}" ]; then 90 | if [ "${DNSMASK}" == "TRUE" ]; then 91 | dnsmasq -p 5462 92 | export NAME_RESOLVER=127.0.0.1:5462 93 | else 94 | export NAME_RESOLVER=$(grep 'nameserver' /etc/resolv.conf | head -n1 | cut -d' ' -f2) 95 | fi 96 | fi 97 | 98 | if [ "${HTTPS_REDIRECT}" == "TRUE" ]; then 99 | cat > ${NGIX_CONF_DIR}/ssl_redirect.conf <<-EOF-REDIRECT-TRUE 100 | if (\$ssl_protocol = "") { 101 | rewrite ^ https://\$host\$https_port_string\$request_uri? permanent; 102 | } 103 | EOF-REDIRECT-TRUE 104 | else 105 | touch ${NGIX_CONF_DIR}/ssl_redirect.conf 106 | fi 107 | 108 | msg "Resolving proxied names using resolver:${NAME_RESOLVER}" 109 | echo "resolver ${NAME_RESOLVER};">${NGIX_CONF_DIR}/resolver.conf 110 | 111 | echo "HTTPS_LISTEN_PORT=${HTTPS_LISTEN_PORT}">/tmp/readyness.cfg 112 | 113 | if [ -f ${UUID_FILE} ]; then 114 | export LOG_UUID=TRUE 115 | fi 116 | if [ -n "${CLIENT_MAX_BODY_SIZE:-}" ]; then 117 | UPLOAD_SETTING="client_max_body_size ${CLIENT_MAX_BODY_SIZE}m;" 118 | echo "${UPLOAD_SETTING}">${NGIX_CONF_DIR}/upload_size.conf 119 | msg "Setting '${UPLOAD_SETTING};'" 120 | fi 121 | 122 | if [ -f /etc/keys/client-ca ]; then 123 | msg "Loading client certs." 124 | cat > ${NGIX_CONF_DIR}/client_certs.conf <<-EOF_CLIENT_CONF 125 | ssl_client_certificate /etc/keys/client-ca; 126 | ssl_verify_client optional; 127 | EOF_CLIENT_CONF 128 | else 129 | msg "No client certs mounted - not loading..." 130 | fi 131 | 132 | case "${LOG_FORMAT_NAME}" in 133 | json|text) 134 | msg "Logging set to ${LOG_FORMAT_NAME}" 135 | 136 | if [ "${NO_LOGGING_URL_PARAMS:-}" == TRUE ]; then 137 | sed -i -e 's/\$request_uri/\$uri/g' ${NGIX_CONF_DIR}/logging.conf 138 | fi 139 | 140 | if [ "${NO_LOGGING_BODY:-}" == TRUE ]; then 141 | sed --in-place '/\$request_body/d' ${NGIX_CONF_DIR}/logging.conf 142 | fi 143 | 144 | if [ "${NO_LOGGING_RESPONSE:-}" == TRUE ]; then 145 | sed --in-place '/\$response_body/d' ${NGIX_CONF_DIR}/logging.conf 146 | touch ${NGIX_CONF_DIR}/response_body.conf 147 | else 148 | cat > ${NGIX_CONF_DIR}/response_body.conf <<-EOF-LOGGING-BODY-TRUE 149 | 150 | lua_need_request_body on; 151 | set \$response_body ""; 152 | body_filter_by_lua ' 153 | local resp_body = string.sub(ngx.arg[1], 1, 1000) 154 | ngx.ctx.buffered = (ngx.ctx.buffered or "") .. resp_body 155 | if ngx.arg[2] then 156 | ngx.var.response_body = ngx.ctx.buffered 157 | end 158 | '; 159 | EOF-LOGGING-BODY-TRUE 160 | fi 161 | 162 | echo "map \$request_uri \$loggable { ~^/nginx_status/ 0; default 1;}">>${NGIX_CONF_DIR}/logging.conf #remove logging for the sysdig agent. 163 | 164 | echo "access_log /dev/stdout extended_${LOG_FORMAT_NAME} if=\$loggable;" >> ${NGIX_CONF_DIR}/logging.conf 165 | ;; 166 | *) 167 | exit_error_msg "Invalid log format specified:${LOG_FORMAT_NAME}. Expecting json or text." 168 | ;; 169 | esac 170 | 171 | if [ -n "${ADD_NGINX_SERVER_CFG:-}" ]; then 172 | msg "Adding extra config for server context." 173 | echo ${ADD_NGINX_SERVER_CFG}>${NGIX_CONF_DIR}/nginx_server_extras.conf 174 | fi 175 | 176 | if [ -n "${ADD_NGINX_HTTP_CFG:-}" ]; then 177 | msg "Adding extra config for http context." 178 | echo ${ADD_NGINX_HTTP_CFG}>${NGIX_CONF_DIR}/nginx_http_extras.conf 179 | fi 180 | 181 | GEO_CFG="${NGIX_CONF_DIR}/nginx_geoip.conf" 182 | GEO_CFG_INIT="${NGIX_CONF_DIR}/nginx_geoip_init.conf" 183 | GEO_CFG_CONFIG="${NGIX_CONF_DIR}/nginx_geoip.conf" 184 | 185 | if [ -n "${ALLOW_COUNTRY_CSV:-}" ]; then 186 | msg "Enabling Country codes detection: ${ALLOW_COUNTRY_CSV}" 187 | 188 | cat > $GEO_CFG_INIT <<-EOF 189 | geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb { 190 | auto_reload 21600; 191 | \$geoip2_metadata_country_build metadata build_epoch; 192 | \$geoip2_data_country_code default=NA source=\$realip country iso_code; 193 | \$geoip2_data_country_name country names en; 194 | } 195 | 196 | geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb { 197 | \$geoip2_data_city_name default=NA city names en; 198 | } 199 | 200 | geoip2_proxy_recursive on; 201 | geoip2_proxy 0.0.0.0/0; 202 | 203 | map \$geoip2_data_country_code \$allowed_country { 204 | default no; 205 | NA yes; 206 | $(echo -n "${ALLOW_COUNTRY_CSV}" | awk -F',' "{ for (i=1; i<=NF; i++) { printf \"%s yes;\n\", \$i; }}") 207 | } 208 | 209 | EOF 210 | cat > $GEO_CFG_CONFIG < ${NGIX_CONF_DIR}/nginx_statsd_server.conf 235 | echo "statsd_count \"waf.status.\$status\" 1;" > ${NGIX_CONF_DIR}/nginx_statsd_metrics.conf 236 | fi 237 | 238 | eval "${NGINX_BIN} -g \"daemon off;\"" 239 | -------------------------------------------------------------------------------- /helper.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function wait_until_cmd() { 4 | cmd="$@" 5 | max_retries=5 6 | wait_time=${WAIT_TIME:-1} 7 | retries=0 8 | while true ; do 9 | if ! bash -c "${cmd}" &> /dev/null ; then 10 | retries=$((retries + 1)) 11 | echo "Testing for readyness..." 12 | if [ ${retries} -eq ${max_retries} ]; then 13 | return 1 14 | else 15 | echo "Retrying, $retries out of $max_retries..." 16 | sleep ${wait_time} 17 | fi 18 | else 19 | 20 | return 0 21 | fi 22 | done 23 | echo 24 | return 1 25 | } 26 | 27 | -------------------------------------------------------------------------------- /html/404.shtml: -------------------------------------------------------------------------------- 1 | 2 | 404 Not Found 3 | 4 |

404 Not Found

5 |
nginx
6 | 7 | 8 | -------------------------------------------------------------------------------- /html/418-request-denied.shtml: -------------------------------------------------------------------------------- 1 | 2 |

3 | Sorry, we are refusing to process your request. If you believe we have 4 | made a mistake please raise a support ticket with the following 5 | information: 6 |

7 | 8 | ORIG URL:
9 | ORIG ARGS:
10 | CODE:
11 | TIME: 12 |
13 | 14 |

Something went wrong.

15 |

Text entered appears suspicious. This is likely due to unusual repeat or individual special characters, i.e. @@ or && or |. Please delete any unnecessary special characters and try again.

16 | 17 | 18 | 19 |

If this problem persists, please contact us on so we can remedy any issues and improve our service.

20 | 21 | -------------------------------------------------------------------------------- /html/500.shtml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /html/501.shtml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /html/502.shtml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /html/503.shtml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /html/504.shtml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /html/50x.shtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Error 5 | 12 | 13 | 14 |

An error occurred

15 |

Sorry, the page you are looking for is currently unavailable.
16 | Please try again later.

17 |

If you are the system administrator of this resource then you should check 18 | the error log for details.

19 |

Faithfully yours, nginx.

20 | 21 | 22 | -------------------------------------------------------------------------------- /location_template.conf: -------------------------------------------------------------------------------- 1 | 2 | set $backend_upstream "$proxy_address"; 3 | proxy_pass $backend_upstream; 4 | proxy_redirect off; 5 | proxy_intercept_errors on; 6 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 7 | proxy_set_header X-Forwarded-Proto $scheme; 8 | -------------------------------------------------------------------------------- /logging.conf: -------------------------------------------------------------------------------- 1 | # Text format extended logging 2 | log_format extended_text '$real_client_ip_if_set$remote_addr - $remote_user [$time_local] ' 3 | '"$request"$uuid_log_opt $status $bytes_sent ' 4 | '"$http_referer" "$http_user_agent" "$gzip_ratio" ' 5 | '$ssl_client_verify ' 6 | '"$ssl_client_s_dn" '; 7 | 8 | # JSON formatted extended logging. (Nginx escapes quotes so should be safe http://serverfault.com/a/584887)..." 9 | log_format extended_json escape=json '{' 10 | '"proxy_proto_address": "$proxy_protocol_addr", ' 11 | '"remote_addr": "$remote_addr", ' 12 | '"country_code": "$country_code", ' 13 | '"http_host": "$host", ' 14 | '"remote_user": "$remote_user", ' 15 | '"timestamp": "$time_iso8601", ' 16 | '"nginx_uuid": "$uuid", ' 17 | '"request_uri": "$request_uri", ' 18 | '"request_method": "$request_method", ' 19 | '"request_body": "$request_body", ' 20 | '"response_body": "$response_body", ' 21 | '"http_status": "$status", ' 22 | '"body_bytes_sent": $body_bytes_sent, ' 23 | '"http_referer": "$http_referer", ' 24 | '"http_user_agent": "$http_user_agent", ' 25 | '"http_x_forwarded_for": "$http_x_forwarded_for", ' 26 | '"request_time": $request_time, ' 27 | '"upstream_response_time": $upstream_response_time, ' 28 | '"upstream_addr": "$upstream_addr", ' 29 | '"upstream_status": $upstream_status, ' 30 | '"scheme": "$scheme", ' 31 | '"gzip_ratio": $gzip_ratio, ' 32 | '"ssl_client_verify": "$ssl_client_verify", ' 33 | '"ssl_client_s_dn": "$ssl_client_s_dn" ' 34 | '}'; 35 | -------------------------------------------------------------------------------- /lua/get_env.lua: -------------------------------------------------------------------------------- 1 | if os.getenv(ngx.arg[1]) == "FALSE" then 2 | return "" 3 | else 4 | return os.getenv(ngx.arg[1]) 5 | end -------------------------------------------------------------------------------- /lua/set_uuid.lua: -------------------------------------------------------------------------------- 1 | if os.getenv("LOG_UUID") == "FALSE" then 2 | return "" 3 | else 4 | local uuid_str = "" 5 | if ngx.req.get_headers()["nginxId"] == nil then 6 | local uuid = require("uuid") 7 | local unpack = unpack or table.unpack 8 | 9 | uuid.set_rng(function() 10 | local random_bytes = require("resty.openssl.rand").bytes(16) 11 | return random_bytes 12 | end) 13 | 14 | uuid_str = uuid() 15 | else 16 | uuid_str = ngx.req.get_headers()["nginxId"] 17 | end 18 | ngx.var.uuid = uuid_str 19 | ngx.var.uuid_log_opt = " nginxId=" .. uuid_str 20 | return uuid_str 21 | end 22 | -------------------------------------------------------------------------------- /monkey-business.yaml: -------------------------------------------------------------------------------- 1 | - delay: 3000 2 | frequency: 1 -------------------------------------------------------------------------------- /naxsi/location.rules: -------------------------------------------------------------------------------- 1 | SecRulesEnabled; 2 | DeniedUrl "/RequestDenied"; 3 | ## check rules 4 | CheckRule "$SQL >= 8" BLOCK; 5 | CheckRule "$RFI >= 8" BLOCK; 6 | CheckRule "$TRAVERSAL >= 4" BLOCK; 7 | CheckRule "$EVADE >= 4" BLOCK; 8 | CheckRule "$XSS >= 8" BLOCK; -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | error_log /dev/stderr error; 2 | 3 | env LOG_UUID; 4 | env HTTPS_REDIRECT_PORT_STRING; 5 | env ALLOW_COUNTRY_CSV; 6 | env VERBOSE_ERROR_PAGES; 7 | env FEEDBACK_EMAIL; 8 | 9 | load_module modules/ngx_http_geoip2_module.so; 10 | 11 | http { 12 | include /usr/local/openresty/nginx/conf/nginx_statsd_server.conf; 13 | 14 | # get CN 15 | map $ssl_client_s_dn $ssl_client_s_dn_cn { 16 | default "should_not_happen"; 17 | ~/CN=(?[^/]+) $CN; 18 | } 19 | include /usr/local/openresty/naxsi/*.rules; 20 | include /usr/local/openresty/nginx/conf/resolver.conf; 21 | include /usr/local/openresty/nginx/conf/nginx_rate_limits*.conf; 22 | include /usr/local/openresty/nginx/conf/nginx_geoip_init.conf; 23 | include mime.types; 24 | default_type application/octet-stream; 25 | 26 | lua_package_path 'conf/?.lua;./nginx/lua/?.lua;;'; 27 | 28 | # Compression 29 | 30 | # Enable Gzip compressed. 31 | gzip on; 32 | 33 | # Compression level (1-9). 34 | # 5 is a perfect compromise between size and cpu usage, offering about 35 | # 75% reduction for most ascii files (almost identical to level 9). 36 | gzip_comp_level 5; 37 | 38 | # Don't compress anything that's already small and unlikely to shrink much 39 | # if at all (the default is 20 bytes, which is bad as that usually leads to 40 | # larger files after gzipping). 41 | gzip_min_length 256; 42 | 43 | # Compress data even for clients that are connecting to us via proxies, 44 | # identified by the "Via" header (required for CloudFront). 45 | gzip_proxied any; 46 | 47 | # Tell proxies to cache both the gzipped and regular version of a resource 48 | # whenever the client's Accept-Encoding capabilities header varies; 49 | # Avoids the issue where a non-gzip capable client (which is extremely rare 50 | # today) would display gibberish if their proxy gave them the gzipped version. 51 | gzip_vary on; 52 | 53 | # Compress all output labeled with one of the following MIME-types. 54 | gzip_types 55 | application/atom+xml 56 | application/javascript 57 | application/json 58 | application/ld+json 59 | application/manifest+json 60 | application/rss+xml 61 | application/vnd.geo+json 62 | application/vnd.ms-fontobject 63 | application/x-font-ttf 64 | application/x-web-app-manifest+json 65 | application/xhtml+xml 66 | application/xml 67 | font/opentype 68 | image/bmp 69 | image/svg+xml 70 | image/x-icon 71 | text/cache-manifest 72 | text/css 73 | text/plain 74 | text/vcard 75 | text/vnd.rim.location.xloc 76 | text/vtt 77 | text/x-component 78 | text/x-cross-domain-policy; 79 | # text/html is always compressed by HttpGzipModule 80 | 81 | include /usr/local/openresty/nginx/conf/logging.conf; 82 | include /usr/local/openresty/nginx/conf/upload_size*.conf; 83 | include /usr/local/openresty/nginx/conf/nginx_http_extras*.conf; 84 | 85 | include /usr/local/openresty/nginx/conf/security_defaults.conf; 86 | 87 | # Accept underscores in headers as NAXSI does this 88 | underscores_in_headers on; 89 | 90 | server { 91 | include /usr/local/openresty/nginx/conf/nginx_statsd_metrics.conf; 92 | include /usr/local/openresty/nginx/conf/response_body.conf; 93 | # Optionally listen to proxy protocol: 94 | include /usr/local/openresty/nginx/conf/nginx_listen.conf; 95 | 96 | # These should be volume added: 97 | include /usr/local/openresty/nginx/conf/server_certs.conf; 98 | 99 | # Optionally include client cert config: 100 | include /usr/local/openresty/nginx/conf/client_certs*.conf; 101 | 102 | # Set the correct host name from the request header... 103 | server_name $host; 104 | # Dont publish the version we are running 105 | server_tokens off; 106 | 107 | set_by_lua_file $https_port_string lua/get_env.lua 'HTTPS_REDIRECT_PORT_STRING'; 108 | # Will redirect requests not on https if HTTPS_REDIRECT=TRUE (the default) 109 | include /usr/local/openresty/nginx/conf/ssl_redirect.conf ; 110 | 111 | # Will set $country_code variables: 112 | set $country_code '??'; 113 | 114 | include /usr/local/openresty/nginx/conf/nginx_server_extras*.conf ; 115 | include /usr/local/openresty/nginx/conf/nginx_geoip.conf; 116 | 117 | set $uuid_log_opt ''; 118 | set $uuid ''; 119 | # Generate a unique ID for use in logs for passing onto applications 120 | set_by_lua_file $uuidopt /usr/local/openresty/nginx/lua/set_uuid.lua; 121 | 122 | location /nginx-proxy/ { 123 | alias /usr/local/openresty/nginx/html/; 124 | ssi on; 125 | error_page 404 /nginx-proxy/404.shtml; 126 | allow all; 127 | } 128 | location /ping { 129 | return 200; 130 | } 131 | 132 | location /RequestDenied { 133 | # Proxy to ourselves in order to access NAXSI debugging headers 134 | proxy_pass https://127.0.0.1:$https_listen_port/nginx-proxy/RequestDenied; 135 | internal; 136 | } 137 | 138 | location /nginx-proxy/RequestDenied { 139 | # Debug information now available in headers ($http_x_naxsi_sig etc.) 140 | # Return a 418 (Teapot) status 141 | set_by_lua_file $verbose_error_pages lua/get_env.lua 'VERBOSE_ERROR_PAGES'; 142 | set_by_lua_file $feedback_email lua/get_env.lua 'FEEDBACK_EMAIL'; 143 | error_page 418 /nginx-proxy/418-request-denied.shtml; 144 | return 418; 145 | } 146 | 147 | include /usr/local/openresty/nginx/conf/locations/*.conf ; 148 | } 149 | 150 | include /usr/local/openresty/nginx/conf/nginx_sysdig_server.conf ; 151 | } 152 | events { 153 | } 154 | -------------------------------------------------------------------------------- /nginx_rate_limits_null.conf: -------------------------------------------------------------------------------- 1 | # Empty "no - rules" file (to allow for the case where no rules generated): 2 | # include /usr/local/openresty/nginx/conf/nginx_rate_limits*.conf; 3 | -------------------------------------------------------------------------------- /nginx_statsd_metrics.conf: -------------------------------------------------------------------------------- 1 | # Populated programatically by go.sh -------------------------------------------------------------------------------- /nginx_statsd_server.conf: -------------------------------------------------------------------------------- 1 | # Populated programatically by go.sh -------------------------------------------------------------------------------- /nginx_web_sockets_proxy.conf: -------------------------------------------------------------------------------- 1 | 2 | proxy_http_version 1.1; 3 | proxy_set_header Upgrade $http_upgrade; 4 | proxy_set_header Connection "upgrade"; 5 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | usage() { 4 | cat <