Giter VIP home page Giter VIP logo

ssl-proxy's Introduction

Simple docker & nginx-based ssl-proxy

Protect any HTTP service with HTTPS!

An Nginx & Docker-based HTTPS/SSL reverse proxy.

Will upgrade to newest nginx

Table of Contents

  1. Features
  2. Example
  3. Getting Started
  4. Secure Docker Registry Example
  5. Secure Rancher Server Example
  6. Secure Rancher Server Example using Docker Compose
  7. Client Verification Example
  8. Arguments / Configuration

Features

  • Up-to-date Nginx & Alpine Linux.
  • Fast HTTP2 TLS-enabled reverse proxy
  • Advanced CORS Support (w/ credentials, auto hostname, smart headers)
  • Automatic WebSockets Support
  • NPN/ALPN Application-Layer Protocol Negotiation test here
  • TLS Forward Secrecy, PFS (aka Perfect Forward Secrecy).
  • Supports Optional Username & Password (stored using bcrypt at 14+ rounds)
    • Alternately an .htpasswd file can be volume mounted. (Multiple named users)
  • Great for securing a Docker Registry, Rancher server, Wordpress, etc

Example

Sample SSL Labs/Qualys SSL & TLS Report:

Here's a sample of what you can expect with default configuration.

image image

Getting Started

Requirements

  1. Generate a HTTPS/SSL certificate using letsencrypt.

To provide secure, proxied access to local HTTP service:

  1. Requires any working HTTP service (for UPSTREAM_TARGET.) (Supports local, in-docker, even remote).
  2. Start an instance of justsml/ssl-proxy:latest as shown below.

Secure Docker Registry Example

# Note: Small scale users can set certificates directly in the registry instance (v2+) 
docker run -d --restart=on-failure:5 \
  --name docker-registry \
  -v /data/registry/registry:/var/lib/registry \
  registry:2.5

# Create an ssl-proxy to point at the registry's port 5000 (via UPSTREAM_TARGET option - see below.)
docker run -d --restart=on-failure:5 \
  --name ssl-proxy \
  -p 5000:5000 \
  -e 'SERVER_NAME=hub.example.com' \
  -e 'UPSTREAM_TARGET=docker-registry:5000' \
  -e 'HTTPS_PORT=5000' \
  -e 'USERNAME=devops' \
  -e 'PASSWORD=secure' \
  -e 'CERT_PUBLIC_PATH=/certs/fullchain.pem' \
  -e 'CERT_PRIVATE_PATH=/certs/privkey.pem' \
  -e "ADD_HEADER='Docker-Distribution-Api-Version' 'registry/2.0' always" \
  -v '/certs:/certs:ro' \
  --link 'docker-registry:docker-registry' \
  justsml/ssl-proxy:latest

# ALT Options
# Create an ssl-proxy to point at the registry's port 5000 (via UPSTREAM_TARGET option - see below.)
docker run -d --restart=on-failure:5 \
  --name ssl-proxy \
  -p 5000:5000 \
  -e 'SERVER_NAME=hub.example.com' \
  -e 'UPSTREAM_TARGET=docker-registry:5000' \
  -e 'EXPIRES_DEFAULT=-1' \
  -e 'HTTPS_PORT=5000' \
  -e 'USERNAME=devops' \
  -e 'PASSWORD=secure' \
  -e 'CERT_PUBLIC_PATH=/certs/fullchain.pem' \
  -e 'CERT_PRIVATE_PATH=/certs/privkey.pem' \
  -e "ADD_HEADER='Docker-Distribution-Api-Version' 'registry/2.0' always" \
  -v '/certs:/certs:ro' \
  --link 'docker-registry:docker-registry' \
  justsml/ssl-proxy:latest

Secure Rancher Server Example

# Update Cached Docker Images
docker pull rancher/server:latest
docker pull justsml/ssl-proxy:latest

# Start Rancher w/ default local port 8080
docker run -d --restart=always \
  --name rancher-server \
  -v /data/rancher/mysql:/var/lib/mysql \
  rancher/server:latest

# Create an ssl-proxy with certs in /certs, (w/o user/pass auth) to point at the local rancher-server's port 8080
docker run -d --restart=always \
  --name rancher-proxy \
  -p 8080:8080 \
  -e 'HTTPS_PORT=8080' \
  -e 'SERVER_NAME=_' \
  -e 'UPSTREAM_TARGET=rancher-server:8080' \
  -e 'CERT_PUBLIC_PATH=/certs/fullchain.pem' \
  -e 'CERT_PRIVATE_PATH=/certs/privkey.pem' \
  -v '/certs:/certs:ro' \
  --link 'rancher-server:rancher-server' \
  justsml/ssl-proxy:latest

Secure Rancher Server Example using Docker Compose

version: '2'
services:
  ssl-proxy:
    image: justsml/ssl-proxy:latest
    environment:
    - HTTPS_PORT=8080
    - SERVER_NAME=rancher.example.com
    - UPSTREAM_TARGET=rancher-server:8080
    - CERT_PUBLIC_PATH=/certs/fullchain.pem
    - CERT_PRIVATE_PATH=/certs/privkey.pem
    volumes:
    - /certs:/certs:ro
    links:
    - 'rancher-server:rancher-server'
    ports: [ '8080:8080' ]
  rancher-server:
    image: rancher/server:latest
    expose: [ '8080' ]
    volumes:
    - /data/rancher/mysql:/var/lib/mysql

Client Verification Example

# Start an nginx server that responds with the incoming request's headers on port 8080
docker run -d --restart=always \
  --name http-server \
  brndnmtthws/nginx-echo-headers

# Create an ssl-proxy with certs in /certs, requiring a client certificate auth, to point at the local http-server's port 8080 and include the client certificate's subject as an http header
docker run -d --restart=always \
  --name verification-proxy \
  -p 443:443 \
  -e 'SERVER_NAME=verification.example.com' \
  -e 'UPSTREAM_TARGET=http-server:8080' \
  -e 'CERT_PUBLIC_PATH=/certs/fullchain.pem' \
  -e 'CERT_PRIVATE_PATH=/certs/privkey.pem' \
  -e 'SSL_VERIFY_CLIENT=on' \
  -e 'CERT_CLIENT_PATH=/certs/clientchain.pem' \
  -e 'ADD_PROXY_HEADER=X-Ssl-Client-Subject $ssl_client_s_dn' \
  -v '/certs:/certs:ro' \
  --link 'http-server:http-server' \
  justsml/ssl-proxy:latest

Arguments

Name Default/Reqd Notes
CERT_AUTO Optional Set to true to automatically request certificate for $SERVER_NAME - caution: don't exceed let's encrypts API limits.
CERT_PUBLIC_PATH Reqd. PEM file Bind-mount certificate files to container path /certs - Or override path w/ this var.
CERT_PRIVATE_PATH Reqd. PEM file Bind-mount certificate files to container path /certs - Or override path w/ this var.
SERVER_NAME Required Primary domain name. Not restricting.
CORS_ORIGIN Optional CORS origin to use for Access-Control-Allow-Origin header. Defaults to SERVER_NAME.
UPSTREAM_TARGET Required HTTP target host:port. Typically an internally routable address. e.g. localhost:9090 or rancher-server:8080
HTTPS_PORT 443/Required Needed for URL rewriting.
ALLOW_RC4 Not set Backwards Compatible Option Required for Java 6 or WinXP/IE8
EXPIRES_DEFAULT Not set Set to apply a default expiration value for nginx location /. Useful for app & caching proxies. (For app use -1 and for caching proxy something like 6h)
USERNAME admin Both PASSWORD and USERNAME must be set in order to use Basic authorization
PASSWORD Both PASSWORD and USERNAME must be set in order to use Basic authorization
PASSWD_PATH /etc/nginx/.htpasswd Alternate auth support (don't combine with USERNAME/PASSWORD) Bind-mount a custom path to /etc/nginx/.htpasswd
SSL_VERIFY_CLIENT Not set Set to verify client certificates (may be on, off, optional, or optional_no_ca). If set and not optional_no_ca, CERT_CLIENT_PATH must be set.
CERT_CLIENT_PATH Not set Needed for client certificate verification. This cert must be PEM-encoded and contain the trusted CA and Intermediate CA certs.
ADD_HEADER Not set Useful for tagging routes in your infrastructure.
ADD_PROXY_HEADER Not set Useful for providing metadata to the upstream server.
SERVER_NAMES_HASH_SIZE 32 Maximum size of server name. Set it to 64/128/... if nginx fails to start with could not build server_names_hash, you should increase server_names_hash_bucket_size error message.
PROXY_HEADER_HOST Optional The host value that will be set in the request header. Defaults to the nginx variable, '$host'. Set this value (e.g., to the nginx variable, '$http_host') if including the port number in the Host header is important.

Contributing / Dev Notes

WORK IN PROGRESS:

  1. HTTPS -> HTTPS proxying support. AKA End-to-end TLS. (skipped due to underwhelming performance and extra complexity in the bash startup script.)
  2. Better CORS support: multi host name
  3. haproxy alt version
# Publish 'latest' version
docker build -t ssl-proxy:latest .
docker tag ssl-proxy:latest justsml/ssl-proxy:latest
docker push justsml/ssl-proxy:latest
# Push a tagged version:
# docker tag ssl-proxy:latest justsml/ssl-proxy:v1.0.1
# docker push justsml/ssl-proxy:v1.0.1

# Remember to docker pull on servers
docker pull justsml/ssl-proxy:latest

# Local testing:
docker build -t ssl-proxy:latest .
docker rm -f TEST-ssl-proxy
docker run --rm \
  --name TEST-ssl-proxy \
  -v ~/certs/xray:/certs \
  -p 5000:5000 \
  -e 'HTTPS_PORT=5000' \
  -e 'USERNAME=devops' \
  -e 'PASSWORD=secure' \
  -e 'SERVER_NAME=hub.example.com' \
  -e 'UPSTREAM_TARGET=www.google.com:80' \
  -e 'CERT_PUBLIC_PATH=/certs/fullchain.pem' \
  -e 'CERT_PRIVATE_PATH=/certs/privkey.pem' \
  ssl-proxy:latest

ssl-proxy's People

Contributors

arel avatar justsml avatar riduidel avatar shawmanz32na avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ssl-proxy's Issues

How to redirect from HTTP to HTTPS?

Hi, when I try to enter to my web app through HTTP appears this:

Screen Shot 2020-07-22 at 19 12 12

How can I configure my docker-compose.yml to redirect HTTP to HTTPS?

My current docker-compose.yml is the following:

version: '3'

services:
  app:
    container_name: app
    depends_on:
      - mongo
    image: myapp-web
    restart: on-failure
    build:
      context: .
      dockerfile: dockerfile
    env_file: docker.env
    networks:
      - farefo-net
  ssl-proxy:
    container_name: ssl-proxy
    image: justsml/ssl-proxy:latest
    environment:
      - HTTPS_PORT=443
      - SERVER_NAME=myapp.com
      - UPSTREAM_TARGET=app:3000
      - CERT_PUBLIC_PATH=/certs/fullchain.pem
      - CERT_PRIVATE_PATH=/certs/privkey.pem
    volumes:
      - /certs:/certs:ro
    ports:
      - 443:443
      - 80:443
    networks:
      - farefo-net
  mongo:
    container_name: mongo
    command:
      - --storageEngine=wiredTiger
    image: mongo:4.2.5-bionic
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: username
      MONGO_INITDB_ROOT_PASSWORD: password
    volumes:
      - $PWD/data:/data/db
      - $PWD/database:/opt/database
    networks:
      - farefo-net
networks:
  farefo-net:

Startup fails due to certbot sudo command usage

When running the compose up -d {service} command to try this out for the first time, this is what I saw:

nginx_1     |  ********** STARTING NGINX HTTPS/AUTH PROXY **********
nginx_1     |
nginx_1     |
nginx_1     | /www/entrypoint.sh: line 21: sudo: command not found

Here is my dumbed-down compose file:

version: "3"

services:

  nginx:
    image: justsml/ssl-proxy:latest
    ports:
      - "443:443"
    restart: unless-stopped
    volumes:
      - ./configs/nginx/certs:/certs
    environment:
      - HTTPS_PORT=443
      - SERVER_NAME=servicename.127.0.0.1.xip.io
      - UPSTREAM_TARGET=upstream.127.0.0.1.xip.io:4433
      - CERT_AUTO=true
      - CERT_PUBLIC_PATH=/certs/fullchain.pem
      - CERT_PRIVATE_PATH=/certs/privkey.pem
    depends_on:
      - servicename

It looks like perhaps sudo just needs to be removed from that script?

could not build server_names_hash, you should increase server_names_hash_bucket_size

I have an application which uses a long domain name. At ssl-proxy startup, I get the following error message

https_offloader_1  | 2019/11/07 10:28:53 [emerg] 13#13: could not build server_names_hash, you should increase server_names_hash_bucket_size: 64
https_offloader_1  | nginx: [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size: 64

And ssl-proxy shuts down.

According to stackoverflow, this may be due to a "bad" configuration.

no route to host when trying to connect to same sever from inside ssl-proxy

Hi there,
I am having the following issue: The proxy works fine to me. I use it to connect to a locally running nextcloud docker, but when I try to connect to the very same server (lets call him myurl.com) i can ping but not connect on https or https.
The parameters with which is start the container:
ssl-proxy:
image: justsml/ssl-proxy:latest
environment:
- HTTPS_PORT=8080
- SERVER_NAME=myurl.com
- UPSTREAM_TARGET=app:80
- CERT_PUBLIC_PATH=/certs/fullchain.pem
- CERT_PRIVATE_PATH=/certs/privkey.pem
- SSLProxyVerify=none
- SSLProxyCheckPeerCN=off
- SSLProxyCheckPeerName=off
- SSLProxyCheckPeerExpire=off
volumes:
- /home/nextcloud/certs:/certs:ro
links:
- app
ports: ['8080:8080']

If I now try to for example curl myurl.com I get no route to host, although there are running services on 80 and many other ports I can connect to from everywhere else. Any suggestions how to fix that would be appreciated. Thank you!

Allow for deciding what the Host request header is set to

Currently, the Host request header is always set to the nginx $host variable, which breaks some use cases.

    ...
    proxy_set_header Host \$host;
    ...

One consequence of using the $host value is that the port number is removed. If serving from a non-standard SSL port, this can cause an issue. In my case, MinIO (Basically, Amazon S3) expects each request to be signed, headers and all. Modifying the host value (by stripping the port number) breaks this. This can be fixed by using $http_host instead of $host (or by setting the host and port number explicitly.)

CORS origin header cannot be customized and overrides the server response headers

An undocumented behavior of ssl-proxy is to set response headers on behalf of the server:

In nginx/entrypoint.sh:

        add_header 'Access-Control-Allow-Origin' $SERVER_NAME always;
        add_header 'Access-Control-Allow-Credentials' \$acac always;
        add_header 'Access-Control-Allow-Methods' ${CORS_METHODS-'GET, POST, PUT, DELETE, HEAD, OPTIONS'} always;
        add_header 'Access-Control-Allow-Headers' ${CORS_HEADERS-'Sec-WebSocket-Extensions,Sec-WebSocket-Key,Sec-WebSocket-Protocol,Sec-WebSocket-Version,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,x-api-action-links,x-api-csrf,x-api-no-challenge,X-Forwarded-For,X-Real-IP'} always;
        add_header 'Access-Control-Max-Age' 864000 always;

Further, the Access-Control-Allow-Origin header is set to the $SERVER_NAME, which does not allow accessing the proxied server from a different domain name. This is the case if you are running an API on a different domain than the web client.

There should be an option to customize the Access-Control-Allow-Origin header. (There should also be an option to use the headers set by the proxied server.) #17 resolves the former case.

Add static file support w/ volume mount

Option No. 1:

Auto-enable if we find a path like /app/public or /var/lib/html?

Option No. 2:

Or for advanced functionality, try something like:

-e STATIC='{RequestPath}={For Matching Extensions}:{Static Files Local Path}'
-e STATIC='/public=js,css,htm,html,jpeg,jpg,png,gif,ico:/app/public'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.