├── Dockerfile ├── README.md ├── build.sh └── root └── etc ├── confd ├── conf.d │ ├── generate_self_signed_cert_sh.toml │ └── nginx_conf.toml └── templates │ ├── generate_self_signed_cert.sh.tmpl │ └── nginx.conf.tmpl ├── cont-init.d └── 10-run-confd ├── nginx └── conf.d │ └── 10-ip_access.conf └── services.d └── nginx └── run /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM outrigger/servicebaselight 2 | 3 | LABEL maintainer "Phase2 " 4 | 5 | # Install packages. 6 | RUN apk add --update --no-cache nginx openssl gettext curl tzdata 7 | 8 | # Create an nginx user for nginx. 9 | #RUN 10 | # chown -R nginx:nginx /var/lib/nginx && \ 11 | # mkdir -p /run/nginx 12 | 13 | # Set default values. 14 | ENV CLIENT_MAX_BODY_SIZE=20M \ 15 | # Space-delimited list of MIME types to include in gzip compression configuration. 16 | GZIP_APPROVED_MIME_TYPES="" \ 17 | PROXY_DOMAIN=www.example.com \ 18 | # Requests per second a given client IP address is permitted. 19 | RATE_LIMIT=20 \ 20 | # How many requests in excess of the rate limit will be pulled on a queued delay 21 | # before further traffic is given the 422 response. 22 | RATE_LIMIT_BURST_QUEUE=10 \ 23 | UPSTREAM=proxied.example.com:80 \ 24 | CONFD_VERSION=0.14.0 \ 25 | CONFD_OPTS='--backend=env --onetime' 26 | 27 | # Install confd. 28 | RUN curl -L "https://github.com/kelseyhightower/confd/releases/download/v$CONFD_VERSION/confd-$CONFD_VERSION-linux-amd64" > /usr/bin/confd && \ 29 | chmod +x /usr/bin/confd 30 | 31 | COPY root / 32 | 33 | # Forward request and error logs to Docker log collector. 34 | RUN ln -sf /dev/stdout /var/log/nginx/access.log && \ 35 | ln -sf /dev/stderr /var/log/nginx/error.log 36 | 37 | EXPOSE 443 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Outrigger HTTPS Proxy 2 | 3 | > Handles HTTPS proxying with automatic self-signed serts for SSL termination. 4 | 5 | [![GitHub tag](https://img.shields.io/github/tag/phase2/docker-https-proxy.svg)](https://github.com/phase2/docker-https-proxy) [![Docker Stars](https://img.shields.io/docker/stars/outrigger/https-proxy.svg)](https://hub.docker.com/r/outrigger/https-proxy) [![Docker Pulls](https://img.shields.io/docker/pulls/outrigger/https-proxy.svg)](https://hub.docker.com/r/outrigger/https-proxy) [![](https://images.microbadger.com/badges/image/outrigger/https-proxy:dev.svg)](https://microbadger.com/images/outrigger/https-proxy:dev 'Get your own image badge on microbadger.com') 6 | 7 | This HTTPS proxy container is intended primarily for use with APIs (headless web services). 8 | 9 | While many languages in which you write such services have a strong HTTP library, they 10 | are not full-featured, production-grade HTTP servers covering all the standard needs out-of-box, and it is easier to configure nginx and rely on it's maturity than build many 11 | of these features in custom code. 12 | 13 | ## Usage 14 | 15 | ### Docker Run 16 | 17 | ```bash 18 | docker run --rm -it \ 19 | -e "UPSTREAM_DOMAIN=api.projectname.vm" \ 20 | -e "UPSTREAM_PORT=3773" \ 21 | -e "PROXY_DOMAIN=proxy.projectname.vm" \ 22 | -l "com.dnsdock.name=proxy" \ 23 | -l "com.dnsdock.image=projectname" \ 24 | outrigger/https-proxy:1.0 25 | ``` 26 | 27 | ### Docker Compose 28 | 29 | ```yaml 30 | # docker-compose run --rm proxy 31 | # The UPSTREAM service must be running. 32 | # https://proxy.projectname.vm 33 | proxy: 34 | build: outrigger/https-proxy:1.0 35 | container_name: projectname_http_proxy 36 | depends_on: 37 | - api 38 | labels: 39 | com.dnsdock.name: proxy 40 | com.dnsdock.image: projectname 41 | environment: 42 | UPSTREAM_DOMAIN: api.projectname.vm 43 | UPSTREAM_PORT: 3773 44 | PROXY_DOMAIN: proxy.projectname.vm 45 | network_mode: bridge 46 | ``` 47 | 48 | ## Features 49 | 50 | ### SSL Termination 51 | 52 | This image was created after finding https://github.com/fsouza/docker-ssl-proxy 53 | was very difficult to customize, but the simple untrusted SSL for local development 54 | was still valuable. 55 | 56 | In the future steps might be taken to facilitate more of a trust mechanism. 57 | 58 | #### Certificates and CA location 59 | 60 | The SSL certificate is generated using a own-ROOT-ca that is available in the 61 | directory /etc/nginx/ca. If made available to other containers or the local 62 | system this will serve as the basis to trust the application certificate. 63 | 64 | #### Using Existing Certificate 65 | 66 | You can use existing SSL certificates for your proxy domain by mounting a volume 67 | to /etc/nginx/certs with following files inside: 68 | 69 | * **key.pem:** Private key file 70 | * **cert.pem:** Certificate file 71 | 72 | The certificate generator will abort if it finds an existing key.pem file. 73 | 74 | ### gzip Compression 75 | 76 | Responses of at least 1000 bytes will be subject to gzip compression at level 6. 77 | 78 | ### Rate Limiting 79 | 80 | Rate Limits are more common with API-based services than other webapps. It is a request 81 | throttle to ensure no one system monopolizes the available server resources. 82 | 83 | This is defaulted to enabled (at 20 requests per second) but can be disabled by setting the RATE_LIMIT environment variable to "0". 84 | 85 | In the event the limit is reached, nginx will respond with a 429 Too Many Requests response. 86 | 87 | * https://www.nginx.com/blog/rate-limiting-nginx/ 88 | * http://nginx.org/en/docs/http/ngx_http_limit_req_module.html 89 | * https://tools.ietf.org/html/rfc6585#section-4 90 | 91 | ### IP-based Access Example 92 | 93 | There is a configuration file to impose IP-based Whitelisting and Blacklisting rules. 94 | These are best handled in the nginx layer rather than in your application, as your application 95 | is unlikely to handle it as efficiently as nginx can. 96 | 97 | Follow the instructions in ./root/etc/nginx/conf.d/10-ip-access.conf to use it in your project. 98 | 99 | ## Environment Variables 100 | 101 | Outrigger images use Environment Variables and [confd](https://github.com/kelseyhightower/confd) 102 | to templatize a number of Docker environment configurations. These templates are 103 | processed on startup with environment variables passed in via the docker run 104 | command-line or via your `docker-compose.yml` manifest file. 105 | 106 | * `CLIENT_MAX_BODY_SIZE`: [`20M`] Maximium size of client uploads. 107 | * `GZIP_APPROVED_MIME_TYPES`: [``] Additional MIME types to include in gzip compression. 108 | * `PROXY_DOMAIN`: [`www.example.com`] The domain in the SSL certificate. Relayed via X-Forwarded-Host HTTP header. 109 | * `RATE_LIMIT`: [`20`] Throttled requests per second per client IP address. 110 | * `RATE_LIMIT_BURST_QUEUE`: [`10`] Number of requests to delay before enforcing the limit. 111 | * `UPSTREAM_DOMAIN`: [`proxied.example.com`] The target host for the reverse proxy. Relayed via Host HTTP header. 112 | * `UPSTREAM_PORT`: [`80`] The target port for the proxied service. 113 | 114 | ## Maintainers 115 | 116 | [![Phase2 Logo](https://s3.amazonaws.com/phase2.public/logos/phase2-logo.png)](https://www.phase2technology.com) 117 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | docker build -t outrigger/https-proxy . 4 | -------------------------------------------------------------------------------- /root/etc/confd/conf.d/generate_self_signed_cert_sh.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | src="/generate_self_signed_cert.sh.tmpl" 3 | dest="/generate_self_signed_cert.sh" 4 | mode = "0755" 5 | keys=[ 6 | "/", 7 | ] 8 | -------------------------------------------------------------------------------- /root/etc/confd/conf.d/nginx_conf.toml: -------------------------------------------------------------------------------- 1 | [template] 2 | src="nginx.conf.tmpl" 3 | dest="/etc/nginx/nginx.conf" 4 | keys=[ 5 | "/", 6 | ] 7 | -------------------------------------------------------------------------------- /root/etc/confd/templates/generate_self_signed_cert.sh.tmpl: -------------------------------------------------------------------------------- 1 | # @see https://github.com/fsouza/docker-ssl-proxy/blob/master/add_self_signed_certs.sh 2 | #!/bin/sh -e 3 | 4 | PROXY_DOMAIN={{getenv "PROXY_DOMAIN"}} 5 | OUTPUT_DIR=/etc/nginx/certs 6 | CA_DIR=/etc/nginx/ca 7 | 8 | mkdir -p $OUTPUT_DIR $CA_DIR 9 | 10 | # Generate the root CA if it doesn't exist 11 | if [ ! -f ${CA_DIR}/rootCA.crt ]; then 12 | openssl genrsa -out ${CA_DIR}/rootCA.key 2048 13 | openssl req -x509 -new -nodes -key ${CA_DIR}/rootCA.key -sha256 -days 1024 -subj "/C=US/ST=Denial/L=Springfield/O=DisRoot/CN=CompanyRoot" -out ${CA_DIR}/rootCA.crt 14 | fi 15 | 16 | if [ ! -f ${OUTPUT_DIR}/key.pem ]; then 17 | # Generate the certificate 18 | openssl genrsa -out ${OUTPUT_DIR}/key.pem 2048 19 | openssl req -new -sha256 -key ${OUTPUT_DIR}/key.pem -nodes -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=${PROXY_DOMAIN}" -out ${OUTPUT_DIR}/csr.pem 20 | openssl x509 -req -in ${OUTPUT_DIR}/csr.pem -CA ${CA_DIR}/rootCA.crt -CAkey ${CA_DIR}/rootCA.key -CAcreateserial -out ${OUTPUT_DIR}/cert.pem 21 | fi 22 | -------------------------------------------------------------------------------- /root/etc/confd/templates/nginx.conf.tmpl: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes 1; 3 | pid /var/run/nginx.pid; 4 | daemon off; 5 | 6 | events { 7 | worker_connections 1024; 8 | # Accept as many connections as possible. 9 | multi_accept on; 10 | } 11 | 12 | http { 13 | include /etc/nginx/mime.types; 14 | default_type application/octet-stream; 15 | 16 | # Default logging format. 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | # Logstash logging format. 21 | log_format logstash '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time $http_host $http_x_forwarded_proto'; 22 | 23 | sendfile on; 24 | gzip on; 25 | 26 | {{ $rate := getenv "RATE_LIMIT" -}} 27 | {{ if ne $rate "0" -}} 28 | # Define a Rate Limit Policy 29 | # @see https://www.nginx.com/blog/rate-limiting-nginx/ 30 | # @see http://nginx.org/en/docs/http/ngx_http_limit_req_module.html 31 | limit_req_zone $binary_remote_addr zone=flood:10m rate={{ $rate }}r/s; 32 | # Rate Limit Headers. 33 | # @see https://tools.ietf.org/html/rfc6585#section-4 34 | limit_req_status 429; 35 | limit_conn_status 429; 36 | {{ end }} 37 | 38 | server { 39 | listen 443; 40 | 41 | # The max body size for proxied POST and PUT requests. 42 | # This is most commonly a problem for file uploads. 43 | client_max_body_size {{ getenv "CLIENT_MAX_BODY_SIZE" }}; 44 | 45 | # Configure SSL Certification Location 46 | ssl_certificate /etc/nginx/certs/cert.pem; 47 | ssl_certificate_key /etc/nginx/certs/key.pem; 48 | 49 | # Activate SSL & Cipher algorithms. 50 | ssl on; 51 | ssl_session_cache builtin:1000 shared:SSL:10m; 52 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 53 | ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; 54 | ssl_prefer_server_ciphers on; 55 | 56 | # Configure gzip compression for responses. 57 | gzip on; 58 | gzip_buffers 16 8k; 59 | gzip_comp_level 6; 60 | gzip_min_length 1000; 61 | gzip_proxied any; 62 | gzip_types 63 | text/css text/javascript text/plain text/xml 64 | application/javascript application/json application/hal+json 65 | application/x-javascript 66 | application/xml application/xml+rss 67 | {{ getenv "GZIP_APPROVED_MIME_TYPES" }}; 68 | gzip_vary on; 69 | gzip_disable "MSIE [1-6]\.(?!.*SV1)"; 70 | 71 | location / { 72 | root /var/www/html/public; 73 | # The proxy will check from the root for files to meet a given 74 | # request before proxying along to the node service. 75 | try_files $uri @service; 76 | } 77 | 78 | location @service { 79 | {{ if ne $rate "0" -}} 80 | limit_req zone=flood burst={{ getenv "RATE_LIMIT_BURST_QUEUE" }} nodelay; 81 | {{ end -}} 82 | 83 | proxy_set_header Host {{ getenv "UPSTREAM_DOMAIN" }}; 84 | proxy_set_header X-Real-IP $remote_addr; 85 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 86 | proxy_set_header X-Forwarded-Proto $scheme; 87 | proxy_set_header X-Forwarded-Host {{ getenv "PROXY_DOMAIN"}}; 88 | proxy_set_header X-Forwarded-Port 443; 89 | 90 | proxy_pass http://{{ getenv "UPSTREAM_DOMAIN" }}:{{ getenv "UPSTREAM_PORT" "80" }}; 91 | } 92 | } 93 | 94 | include /etc/nginx/conf.d/*.conf; 95 | } 96 | -------------------------------------------------------------------------------- /root/etc/cont-init.d/10-run-confd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | 3 | echo '=> Running confd to write configuration templates' 4 | confd $CONFD_OPTS 5 | -------------------------------------------------------------------------------- /root/etc/nginx/conf.d/10-ip_access.conf: -------------------------------------------------------------------------------- 1 | ## 2 | # IP Access 3 | # 4 | # This configuration file demonstrates using IP Address based whitelisting or blacklisting 5 | # for access to the service. 6 | # 7 | # While this can be handled via a conventional firewall, this configuration can be used 8 | # for project-level rules, which can optionally be more restrictive than the server overall. 9 | # 10 | # To use this file, copy it into your project codebase (e.g., ./env/proxy/root/etc/conf.d) 11 | # and either add it to your Docker image or create a volume mount to /etc/nginx/conf.d 12 | # 13 | # @see https://www.cyberciti.biz/faq/linux-unix-nginx-access-control-howto/ 14 | ## 15 | 16 | ### Simple IP Blacklist. 17 | # deny 1.2.3.4; 18 | # deny 91.212.45.0/24; 19 | # deny 91.212.65.0/24; 20 | 21 | ### Simple IP Whitelist 22 | ## block one workstation 23 | # deny 192.168.1.1; 24 | ## allow anyone in 192.168.1.0/24 25 | # allow 192.168.1.0/24; 26 | ## drop rest of the world 27 | # deny all; 28 | 29 | ### Path-specific IP Whitelist 30 | # location /admin { 31 | ## block one workstation 32 | # deny 192.168.1.1; 33 | ## allow anyone in 192.168.1.0/24 34 | # allow 192.168.1.0/24; 35 | ## drop rest of the world 36 | # deny all; 37 | # } 38 | -------------------------------------------------------------------------------- /root/etc/services.d/nginx/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv sh 2 | 3 | /generate_self_signed_cert.sh 4 | 5 | exec nginx 6 | --------------------------------------------------------------------------------