Skip to content
This repository has been archived by the owner on Sep 2, 2021. It is now read-only.

Commit

Permalink
Merge pull request #2 from CausticLab/dev
Browse files Browse the repository at this point in the history
MVP v0.1.x
  • Loading branch information
Munsio authored Dec 6, 2017
2 parents 8da3904 + 100cefe commit 4740cc3
Show file tree
Hide file tree
Showing 14 changed files with 663 additions and 12 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/tmp
docker-compose.yml
38 changes: 27 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
FROM alpine:3.5
FROM alpine:3.6

RUN apk add --no-cache ca-certificates

ENV RANCHER_GEN_RELEASE v0.2.0 \
RGON_EXEC_RELEASE v1.0.0 \
ACMETOOL_RELEASE v0.0.59
RUN apk add --no-cache ca-certificates openssl

ENV RANCHER_GEN_RELEASE=v0.6.0 \
RGON_EXEC_RELEASE=v1.1.1 \
ACMETOOL_RELEASE=v0.0.59

# Consider revising: https://www.ctl.io/developers/blog/post/dockerfile-add-vs-copy/#which-to-use
ADD https://github.com/causticlab/go-rancher-gen/releases/download/${RANCHER_GEN_RELEASE}/rancher-gen-linux-amd64.tar.gz /tmp/rancher-gen.tar.gz
ADD https://github.com/causticlab/rgon-exec/releases/download/${RGON_EXEC_RELEASE}/rgon-exec-linux-amd64.tar.gz /tmp/rgon-exec.tar.gz
ADD https://github.com/hlandau/acme/releases/download/${ACMETOOL_RELEASE}/acmetool-${ACMETOOL_RELEASE}-linux_amd64.tar.gz /tmp/acmetool.tar.gz

RUN tar -zxvf /tmp/*.tar.gz -C /usr/local/bin \
&& chmod +x /usr/local/bin/rancher-gen \
&& chmod +x /usr/local/bin/rgon-exec \
&& chmod +x /usr/local/bin/acmetool
RUN ls /tmp/*.tar.gz | xargs -i tar zxf {} -C /usr/local/bin

RUN mv /usr/local/bin/acmetool-${ACMETOOL_RELEASE}-linux_amd64/bin/acmetool /usr/local/bin/acmetool \
&& mv /usr/local/bin/rgon-exec-linux-amd64 /usr/local/bin/rgon-exec

COPY app/acmetool/hooks/01copyCertsToNginx.sh /usr/lib/acme/hooks/
COPY app/cron/daily/acmetool-cron /etc/periodic/daily/
COPY examples/rancher-gen/ /app/rancher-gen/default/
COPY examples/acmetool/ /app/acme/conf/
COPY app/entrypoint.sh /app/

RUN chmod +x /usr/local/bin/rancher-gen \
&& chmod +x /usr/local/bin/rgon-exec \
&& chmod +x /usr/local/bin/acmetool \
&& chown root:root /usr/local/bin/* \
&& chmod +x /app/* \
&& chown root:root /usr/lib/acme/hooks/* \
&& chmod +x /usr/lib/acme/hooks/* \
&& chmod +x /etc/periodic/daily/* \
&& rm /tmp/*.tar.gz

ENTRYPOINT ["/usr/local/bin/rancher-gen"]
ENTRYPOINT ["/bin/sh", "/app/entrypoint.sh" ]
CMD ["rancher-gen", "--config", "/etc/rancher-gen/default/rancher-gen.cfg"]
39 changes: 39 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
PROJECT := rgon-proxy
PLATFORM := linux
ARCH := amd64
DOCKER_IMAGE := causticlab/$(PROJECT)

VERSION := $(shell cat VERSION)
GITSHA := $(shell git rev-parse --short HEAD)

all: help

help:
@echo "make image - build release image"
@echo "make run - start the docker container"
@echo "make release - tag with version and trigger CI release build"
@echo "make dockerhub - build and push image to Docker Hub"
@echo "make version - show app version"

image:
docker build -t $(DOCKER_IMAGE):$(VERSION) -f Dockerfile .

run:
docker run -td --name $(PROJECT) $(DOCKER_IMAGE):$(VERSION)

bash:
docker exec -it $(PROJECT) bash

clean-docker:
docker stop $(PROJECT) && docker rm $(PROJECT)

release:
git tag `cat VERSION`
git push origin master --tags

dockerhub: image
@echo "Pushing $(DOCKER_IMAGE):$(VERSION)"
docker push $(DOCKER_IMAGE):$(VERSION)

version:
@echo $(VERSION) $(GITSHA)
113 changes: 112 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,115 @@
# rgon-proxy
the base image of the rancher nginx letsencrypt proxy

**RGON-proxy** is currently in an development/alpha state please use the `dev` branch for further information.
## Usage

Add labels to containers to indicate domain name and port (if non-80):

- `rgon.domain=mydomain.com`
- `rgon.port=80`
- `rgon.ssl='true'`
- `rgon.redirect=https`
- `rgon.stats=1.1.1.1`

## Update Notice
RGON-Proxy is currently in an development/alpha state cause of this it might be possible that default config files will change rapidly - To provide always an latest use of those config files please rename the config folder on your server before the update an merge changes by hand. We are currently on an discussion how to solve this problem in the future

## Features

### Let’s Encrypt support
Let’s Encrypt support powered by [hlandau/acme](https://github.com/hlandau/acme).

### Automatic https redirect
If the `rgon.redirect` label is defined with `https` rgon automatically redirects the specified domains to https.

### Diffie-Hellman Key generation
On each start we check if there is allready an key present and if not we generate one.

### Multiple Domain support
It is possible to define multiple domains in the `rgon.domain` label seperated by an `,`.
If the `rgon.ssl` label is also present we generate an SNI Certificate for all this domains.

### Custom/Default nginx vhost & location config
You can specify an default vhost or default location config file under `%YourPath%/vhost.d/default[_location]`.
Or define an configuration for each domain with `%domain%[_location]`. Please notice that if you use multiple domains the first one is the identifier.

### HTTP Basic Auth
Add an file with the domain name of the `rgon.domain` label under %YourPath%/htpasswd and your site is protected with an Basic Auth dialog.

### [Experimental] Vertical scalability
We were able to run an nginx instance on each host using the scheduler commands of rancher but it is currently only possible for simple http requests because we need a centralized secure store for certificates before we can continiue this feature.

## Usage

Rancher Labels can be used to specify various modes of operation.

### Label: `rgon.domain`

One or many domains can be defined and comma-separated.

**Domain Label Examples:**

```
rgon.domain=my-domain.com
rgon.domain=sub1.my-domain.com,sub2.my-domain.com
```

### Label: `rgon.redirect`

Optional redirect to the HTTPS port. Only valid when label value equals `https`, is ignored otherwise.

**Redirect Label Examples:**

```sh
# Does nothing, leaves traffic routed to rgon.port label
rgon.redirect=

# Same as empty value
rgon.redirect=http

# Reroute traffic to :443
rgon.redirect=https

```

### Label: `rgon.stats`

The [Nginx Status module](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) can be enabled by adding a `rgon.stats` label. This label must specify the IP address of machines that are allowed to read these stats, or `all` for open access. Multiple IP addresses can be comma-separated.

**Status Label Examples:**

```
rgon.stats=1.1.1.1
rgon.stats=1.1.1.1,2.2.2.2
rgon.stats=all
```
Use `http://server-ip/nginx_status` to access these stats - note it only work with the ip-address were the nginx is running on


## Upcoming Features

- Vertical scalability with SSL support
- Easier generation of an basic auth file
- Possibility to use multiple ports for use with subdomains [frontend:80/api:8080/monitoring:7071]
- Use of custom SSL-Certificates


## Lifecycle

1. entrypoint.sh: Run on start
- Check for writable directories
- Check for `dhparam.pem`, generate if missing
- Check for `nginx.conf`, remove if present
- Single-run Rancher-Gen to build `nginx.conf`
- Reload nginx
- Init Rancher-Gen watcher
1. Rancher-Gen: watch for metadata changes
- acmetool: Generate certificates if needed
- Update `nginx.conf`
- Reload nginx
1. Repeat step 2

## Credits

- [janeczku/go-rancher-gen](https://github.com/janeczku/go-rancher-gen) - service to poll rancher-metadata api
- [hlandau/acme](https://github.com/hlandau/acme) - service to generate and reissue letsencrypt certificates
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v0.1.1
16 changes: 16 additions & 0 deletions app/acmetool/hooks/01copyCertsToNginx.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh
#
# Executed by acmetool when a certificate is updated
# Ensures that /etc/nginx/certs/ is present, then
# overwrite-copies all live SSL certs to that directory.
# Triggers a single-run of rancher-gen to reload Nginx containers.

case "$1" in
"live-updated" )
echo "[HOOK] 01copyCertsToNginx: Processing live-updated hook"
mkdir -p /etc/nginx/certs
cp -RLf /var/lib/acme/live/* /etc/nginx/certs/
;;
*)
exit 42
esac
3 changes: 3 additions & 0 deletions app/cron/daily/acmetool-cron
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
echo "running acmetool renewal"
/usr/local/bin/acmetool --batch reconcile
107 changes: 107 additions & 0 deletions app/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/bin/sh

if [[ -z "$CATTLE_URL" ]]; then
echo "Error: can't get my CATTLE_URL !" >&2
exit 1
fi

if [[ -z "$CATTLE_ACCESS_KEY" ]]; then
echo "Error: can't get my CATTLE_ACCESS_KEY !" >&2
exit 1
fi

if [[ -z "$CATTLE_SECRET_KEY" ]]; then
echo "Error: can't get my CATTLE_SECRET_KEY !" >&2
exit 1
fi

function acmetool_init {
if [[ -z "$ACME_EMAIL" ]]; then
echo "Warning: can't get my ACME_EMAIL !" >&2
else
sed -i "s~hostmaster@example.com~$ACME_EMAIL~g" /var/lib/acme/conf/responses
fi

if [[ -z "$ACME_API" ]]; then
echo "Warning: can't get my ACME_API !" >&2
else
sed -i "s~https://acme-staging.api.letsencrypt.org/directory~$ACME_API~g" /var/lib/acme/conf/responses
fi

/usr/local/bin/acmetool quickstart
}

function copy_config_files {
if [[ ! -d /etc/rancher-gen/default ]]; then
mkdir -p /etc/rancher-gen/default
cp /app/rancher-gen/default/* /etc/rancher-gen/default/
fi

if [[ ! -f /var/lib/acme/conf/responses ]]; then
mkdir -p /var/lib/acme/conf
cp /app/acme/conf/responses /var/lib/acme/conf/
fi

if [[ ! -f /var/lib/acme/conf/target ]]; then
cp /app/acme/conf/target /var/lib/acme/conf/
fi
}

function check_writable_directory {
local dir="$1"
if [[ ! -d "$dir" ]]; then
echo "Error: can't access to '$dir' directory !" >&2
echo "Check that '$dir' directory is declared has a writable volume." >&2
exit 1
fi
touch $dir/.check_writable 2>/dev/null
if [[ $? -ne 0 ]]; then
echo "Error: can't write to the '$dir' directory !" >&2
echo "Check that '$dir' directory is export as a writable volume." >&2
exit 1
fi
rm -f $dir/.check_writable
}

function check_dh_group {
if [[ ! -f /etc/nginx/certs/dhparam.pem ]]; then
echo "Creating Diffie-Hellman group (can take several minutes...)"
openssl dhparam -out /etc/nginx/certs/.dhparam.pem.tmp 2048
mv /etc/nginx/certs/.dhparam.pem.tmp /etc/nginx/certs/dhparam.pem || exit 1
fi
}

function check_nginx_conf {
if [[ ! -f /etc/nginx/certs/default/default.csr ]]; then
openssl genrsa -des3 -passout pass:x -out /etc/nginx/certs/default/default.pass.key 2048
openssl rsa -passin pass:x -in /etc/nginx/certs/default/default.pass.key \
-out /etc/nginx/certs/default/default.key
rm /etc/nginx/certs/default/default.pass.key
openssl req -new -key /etc/nginx/certs/default/default.key -out /etc/nginx/certs/default/default.csr \
-days 1000 -subj "/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=example.com"
openssl x509 -req -days 365 -in /etc/nginx/certs/default/default.csr \
-signkey /etc/nginx/certs/default/default.key -out /etc/nginx/certs/default/default.crt
fi

if [[ -f /etc/nginx/conf.d/nginx.conf ]]; then
rm -f /etc/nginx/conf.d/nginx.conf
fi
}

function rancher_gen_firstrun {
if [[ -f /etc/rancher-gen/default/rancher-gen-firstrun.cfg ]]; then
echo "[ENTRYPOINT]: Running Rancher-Gen first-run"
/usr/local/bin/rancher-gen --config /etc/rancher-gen/default/rancher-gen-firstrun.cfg
echo "[ENTRYPOINT]: Rancher-Gen first-run complete"
fi
}

copy_config_files
check_writable_directory '/etc/nginx/certs'
check_writable_directory '/etc/nginx/vhost.d'
check_dh_group
check_nginx_conf
acmetool_init
rancher_gen_firstrun

exec "$@"
52 changes: 52 additions & 0 deletions docker-compose.yml.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
version: '2'

services:
nginx:
image: "nginx"
tty: true
stdin_open: true
ports:
- 80:80/tcp
- 443:443/tcp
volumes:
- ${DIR}/htpasswd:/etc/nginx/htpasswd
- ${DIR}/conf:/etc/nginx/conf.d
- ${DIR}/vhost:/etc/nginx/vhost.d
- ${DIR}/certs:/etc/nginx/certs
labels:
io.rancher.sidekicks: rgon-proxy
io.rancher.scheduler.global: 'false' # true for multi-host
rgon-proxy: nginx
logging:
driver: "json-file"
options:
max-size: 10m
max-file: 5
rgon-proxy:
image: "causticlab/rgon-proxy:dev" # :latest for production
tty: true
stdin_open: true
links:
- nginx
volumes_from:
- nginx
ports:
- 402:402/tcp
volumes:
- ${DIR}/rancher-gen:/etc/rancher-gen
- ${DIR}/acmetool:/var/lib/acme
links:
- nginx
labels:
io.rancher.scheduler.global: 'false'
io.rancher.container.create_agent: 'true'
io.rancher.container.agent.role: 'environment'
rgon-proxy: rancher-gen
rgon-acme: 'true'
logging:
driver: "json-file"
options:
max-size: 10m
max-file: 5
environment:
RGEN_CONFIG_ONETIME: '/etc/rancher-gen/default/rancher-gen-onetime.cfg'
Loading

0 comments on commit 4740cc3

Please sign in to comment.