Browse Source

added load balancer images... tcp-proxy for connections, http-proxy for SSL certs

unpolished copy
Harlan Iverson 7 years ago
parent
commit
402b020ce1

+ 5 - 0
http-proxy-docker/build.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+source env-proxy.sh
+
+docker build -t $IMAGE image

+ 6 - 0
http-proxy-docker/env-proxy.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+
+export IMAGE=nginx-proxy-arm
+export ENV=prod
+
+export NAME=http-proxy-$ENV

+ 4 - 0
http-proxy-docker/image/Dockerfile

@@ -0,0 +1,4 @@
+FROM braingamer/nginx-proxy-arm
+
+ADD nginx.tmpl /app/nginx.tmpl
+ADD cloudflare.crt /etc/nginx/certs/cloudflare.crt

+ 35 - 0
http-proxy-docker/image/cloudflare.crt

@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGBjCCA/CgAwIBAgIIV5G6lVbCLmEwCwYJKoZIhvcNAQENMIGQMQswCQYDVQQG
+EwJVUzEZMBcGA1UEChMQQ2xvdWRGbGFyZSwgSW5jLjEUMBIGA1UECxMLT3JpZ2lu
+IFB1bGwxFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3Ju
+aWExIzAhBgNVBAMTGm9yaWdpbi1wdWxsLmNsb3VkZmxhcmUubmV0MB4XDTE1MDEx
+MzAyNDc1M1oXDTIwMDExMjAyNTI1M1owgZAxCzAJBgNVBAYTAlVTMRkwFwYDVQQK
+ExBDbG91ZEZsYXJlLCBJbmMuMRQwEgYDVQQLEwtPcmlnaW4gUHVsbDEWMBQGA1UE
+BxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEjMCEGA1UEAxMa
+b3JpZ2luLXB1bGwuY2xvdWRmbGFyZS5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQDdsts6I2H5dGyn4adACQRXlfo0KmwsN7B5rxD8C5qgy6spyONr
+WV0ecvdeGQfWa8Gy/yuTuOnsXfy7oyZ1dm93c3Mea7YkM7KNMc5Y6m520E9tHooc
+f1qxeDpGSsnWc7HWibFgD7qZQx+T+yfNqt63vPI0HYBOYao6hWd3JQhu5caAcIS2
+ms5tzSSZVH83ZPe6Lkb5xRgLl3eXEFcfI2DjnlOtLFqpjHuEB3Tr6agfdWyaGEEi
+lRY1IB3k6TfLTaSiX2/SyJ96bp92wvTSjR7USjDV9ypf7AD6u6vwJZ3bwNisNw5L
+ptph0FBnc1R6nDoHmvQRoyytoe0rl/d801i9Nru/fXa+l5K2nf1koR3IX440Z2i9
++Z4iVA69NmCbT4MVjm7K3zlOtwfI7i1KYVv+ATo4ycgBuZfY9f/2lBhIv7BHuZal
+b9D+/EK8aMUfjDF4icEGm+RQfExv2nOpkR4BfQppF/dLmkYfjgtO1403X0ihkT6T
+PYQdmYS6Jf53/KpqC3aA+R7zg2birtvprinlR14MNvwOsDOzsK4p8WYsgZOR4Qr2
+gAx+z2aVOs/87+TVOR0r14irQsxbg7uP2X4t+EXx13glHxwG+CnzUVycDLMVGvuG
+aUgF9hukZxlOZnrl6VOf1fg0Caf3uvV8smOkVw6DMsGhBZSJVwao0UQNqQIDAQAB
+o2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4E
+FgQUQ1lLK2mLgOERM2pXzVc42p59xeswHwYDVR0jBBgwFoAUQ1lLK2mLgOERM2pX
+zVc42p59xeswCwYJKoZIhvcNAQENA4ICAQDKDQM1qPRVP/4Gltz0D6OU6xezFBKr
+LWtDoA1qW2F7pkiYawCP9MrDPDJsHy7dx+xw3bBZxOsK5PA/T7p1dqpEl6i8F692
+g//EuYOifLYw3ySPe3LRNhvPl/1f6Sn862VhPvLa8aQAAwR9e/CZvlY3fj+6G5ik
+3it7fikmKUsVnugNOkjmwI3hZqXfJNc7AtHDFw0mEOV0dSeAPTo95N9cxBbm9PKv
+qAEmTEXp2trQ/RjJ/AomJyfA1BQjsD0j++DI3a9/BbDwWmr1lJciKxiNKaa0BRLB
+dKMrYQD+PkPNCgEuojT+paLKRrMyFUzHSG1doYm46NE9/WARTh3sFUp1B7HZSBqA
+kHleoB/vQ/mDuW9C3/8Jk2uRUdZxR+LoNZItuOjU8oTy6zpN1+GgSj7bHjiy9rfA
+F+ehdrz+IOh80WIiqs763PGoaYUyzxLvVowLWNoxVVoc9G+PqFKqD988XlipHVB6
+Bz+1CD4D/bWrs3cC9+kk/jFmrrAymZlkFX8tDb5aXASSLJjUjcptci9SKqtI2h0J
+wUGkD7+bQAr+7vr8/R+CBmNMe7csE8NeEX6lVMF7Dh0a1YKQa6hUN18bBuYgTMuT
+QzMmZpRpIBB321ZBlcnlxiTJvWxvbCPHKHj20VwwAz7LONF59s84ZsOqfoBv8gKM
+s0s5dsq5zpLeaw==
+-----END CERTIFICATE-----

+ 248 - 0
http-proxy-docker/image/nginx.tmpl

@@ -0,0 +1,248 @@
+{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }}
+
+{{ define "upstream" }}
+	{{ if .Address }}
+		{{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}}
+		{{ if and .Container.Node.ID .Address.HostPort }}
+			# {{ .Container.Node.Name }}/{{ .Container.Name }}
+			server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }};
+		{{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}}
+		{{ else if .Network }}
+			# {{ .Container.Name }}
+			server {{ .Network.IP }}:{{ .Address.Port }};
+		{{ end }}
+	{{ else if .Network }}
+		# {{ .Container.Name }}
+		server {{ .Network.IP }} down;
+	{{ end }}
+{{ end }}
+
+# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
+# scheme used to connect to this server
+map $http_x_forwarded_proto $proxy_x_forwarded_proto {
+  default $http_x_forwarded_proto;
+  ''      $scheme;
+}
+
+# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
+# server port the client connected to
+map $http_x_forwarded_port $proxy_x_forwarded_port {
+  default $http_x_forwarded_port;
+  ''      $server_port;
+}
+
+# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
+# Connection header that may have been passed to this server
+map $http_upgrade $proxy_connection {
+  default upgrade;
+  '' close;
+}
+
+gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
+
+log_format vhost '$host $remote_addr - $remote_user [$time_local] '
+                 '"$request" $status $body_bytes_sent '
+                 '"$http_referer" "$http_user_agent"';
+
+access_log off;
+
+{{ if (exists "/etc/nginx/proxy.conf") }}
+include /etc/nginx/proxy.conf;
+{{ else }}
+# HTTP 1.1 support
+proxy_http_version 1.1;
+proxy_buffering off;
+proxy_set_header Host $http_host;
+proxy_set_header Upgrade $http_upgrade;
+proxy_set_header Connection $proxy_connection;
+proxy_set_header X-Real-IP $remote_addr;
+proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
+
+# Mitigate httpoxy attack (see README for details)
+proxy_set_header Proxy "";
+{{ end }}
+
+server {
+	server_name _; # This is just an invalid value which will never trigger on a real hostname.
+	listen 80;
+	access_log /var/log/nginx/access.log vhost;
+	return 503;
+}
+
+{{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
+server {
+	server_name _; # This is just an invalid value which will never trigger on a real hostname.
+	listen 443 ssl http2;
+	access_log /var/log/nginx/access.log vhost;
+	return 503;
+
+	ssl_session_tickets off;
+	ssl_certificate /etc/nginx/certs/default.crt;
+	ssl_certificate_key /etc/nginx/certs/default.key;
+}
+{{ end }}
+
+{{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }}
+
+upstream {{ $host }} {
+{{ range $container := $containers }}
+	{{ $addrLen := len $container.Addresses }}
+
+	{{ range $knownNetwork := $CurrentContainer.Networks }}
+		{{ range $containerNetwork := $container.Networks }}
+			{{ if eq $knownNetwork.Name $containerNetwork.Name }}
+				## Can be connect with "{{ $containerNetwork.Name }}" network
+
+				{{/* If only 1 port exposed, use that */}}
+				{{ if eq $addrLen 1 }}
+					{{ $address := index $container.Addresses 0 }}
+					{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
+				{{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}}
+				{{ else }}
+					{{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }}
+					{{ $address := where $container.Addresses "Port" $port | first }}
+					{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
+				{{ end }}
+			{{ end }}
+		{{ end }}
+	{{ end }}
+{{ end }}
+}
+
+{{ $default_host := or ($.Env.DEFAULT_HOST) "" }}
+{{ $default_server := index (dict $host "" $default_host "default_server") $host }}
+
+{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}}
+{{ $proto := or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http" }}
+
+{{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}}
+{{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }}
+
+{{/* Get the first cert name defined by containers w/ the same vhost */}}
+{{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }}
+
+{{/* Get the best matching cert  by name for the vhost. */}}
+{{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}}
+
+{{/* vhostCert is actually a filename so remove any suffixes since they are added later */}}
+{{ $vhostCert := trimSuffix ".crt" $vhostCert }}
+{{ $vhostCert := trimSuffix ".key" $vhostCert }}
+
+{{/* Use the cert specified on the container or fallback to the best vhost match */}}
+{{ $cert := (coalesce $certName $vhostCert) }}
+
+{{ $is_https := (and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }}
+
+{{ if $is_https }}
+
+{{ if eq $https_method "redirect" }}
+server {
+	server_name {{ $host }};
+	listen 80 {{ $default_server }};
+	access_log /var/log/nginx/access.log vhost;
+	return 301 https://$host$request_uri;
+}
+{{ end }}
+
+server {
+	server_name {{ $host }};
+	listen 443 ssl http2 {{ $default_server }};
+	access_log /var/log/nginx/access.log vhost;
+
+	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+	ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
+
+	ssl_prefer_server_ciphers on;
+	ssl_session_timeout 5m;
+	ssl_session_cache shared:SSL:50m;
+	ssl_session_tickets off;
+
+	ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }};
+	ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }};
+
+        ssl_verify_client on;
+        ssl_client_certificate /etc/nginx/certs/cloudflare.crt;
+
+	{{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }}
+	ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
+	{{ end }}
+
+	{{ if (ne $https_method "noredirect") }}
+	add_header Strict-Transport-Security "max-age=31536000";
+	{{ end }}
+
+	{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
+	include {{ printf "/etc/nginx/vhost.d/%s" $host }};
+	{{ else if (exists "/etc/nginx/vhost.d/default") }}
+	include /etc/nginx/vhost.d/default;
+	{{ end }}
+
+	location / {
+		{{ if eq $proto "uwsgi" }}
+		include uwsgi_params;
+		uwsgi_pass {{ trim $proto }}://{{ trim $host }};
+		{{ else }}
+		proxy_pass {{ trim $proto }}://{{ trim $host }};
+		{{ end }}
+		{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
+		auth_basic	"Restricted {{ $host }}";
+		auth_basic_user_file	{{ (printf "/etc/nginx/htpasswd/%s" $host) }};
+		{{ end }}
+                {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
+                include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
+                {{ else if (exists "/etc/nginx/vhost.d/default_location") }}
+                include /etc/nginx/vhost.d/default_location;
+                {{ end }}
+	}
+}
+
+{{ end }}
+
+{{ if or (not $is_https) (eq $https_method "noredirect") }}
+
+server {
+	server_name {{ $host }};
+	listen 80 {{ $default_server }};
+	access_log /var/log/nginx/access.log vhost;
+
+	{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
+	include {{ printf "/etc/nginx/vhost.d/%s" $host }};
+	{{ else if (exists "/etc/nginx/vhost.d/default") }}
+	include /etc/nginx/vhost.d/default;
+	{{ end }}
+
+	location / {
+		{{ if eq $proto "uwsgi" }}
+		include uwsgi_params;
+		uwsgi_pass {{ trim $proto }}://{{ trim $host }};
+		{{ else }}
+		proxy_pass {{ trim $proto }}://{{ trim $host }};
+		{{ end }}
+		{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
+		auth_basic	"Restricted {{ $host }}";
+		auth_basic_user_file	{{ (printf "/etc/nginx/htpasswd/%s" $host) }};
+		{{ end }}
+                {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
+                include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
+                {{ else if (exists "/etc/nginx/vhost.d/default_location") }}
+                include /etc/nginx/vhost.d/default_location;
+                {{ end }}
+	}
+}
+
+{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
+server {
+	server_name {{ $host }};
+	listen 443 ssl http2 {{ $default_server }};
+	access_log /var/log/nginx/access.log vhost;
+	return 500;
+
+	ssl_certificate /etc/nginx/certs/default.crt;
+	ssl_certificate_key /etc/nginx/certs/default.key;
+}
+{{ end }}
+
+{{ end }}
+{{ end }}

+ 18 - 0
http-proxy-docker/start-proxy-dev.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+
+source env-proxy.sh
+
+docker run \
+  -d \
+  --name $NAME-dev \
+  --restart always \
+  -h $NAME-dev \
+  -p 680:80 \
+  -p 643:443 \
+  -v /var/run/docker.sock:/tmp/docker.sock:ro \
+  -v $HOME/harlanji.com/certs:/etc/nginx/certs:ro  \
+  $IMAGE
+
+
+
+#  -v /etc/nginx/nginx.conf:$HOME/harlanji.com/rtmp-proxy/nginx_ispoogedaily.conf:ro \

+ 18 - 0
http-proxy-docker/start-proxy.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+
+source env-proxy.sh
+
+docker run \
+  -d \
+  --name $NAME \
+  --restart always \
+  -h $NAME \
+  -p 80:80 \
+  -p 443:443 \
+  -v /var/run/docker.sock:/tmp/docker.sock:ro \
+  -v $HOME/harlanji.com/certs:/etc/nginx/certs:ro  \
+  $IMAGE
+
+
+
+#  -v /etc/nginx/nginx.conf:$HOME/harlanji.com/rtmp-proxy/nginx_ispoogedaily.conf:ro \

+ 5 - 0
http-proxy-docker/stop-proxy.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+source env-proxy.sh
+
+docker rm -f $NAME

+ 1 - 0
tcp-proxy-docker/build.sh

@@ -0,0 +1 @@
+docker build -t tcp-proxy:2 image

+ 8 - 0
tcp-proxy-docker/image/Dockerfile

@@ -0,0 +1,8 @@
+FROM hypriot/rpi-haproxy
+
+COPY haproxy.cfg /etc/haproxy/haproxy.cfg
+
+ENV BACKEND_HOST \
+    BACKEND_PORT \
+    HOST \
+    PORT

+ 20 - 0
tcp-proxy-docker/image/haproxy.cfg

@@ -0,0 +1,20 @@
+frontend frontend
+        bind 0.0.0.0:80
+        mode tcp
+        log global
+        option tcplog
+        timeout client 3600s
+        backlog 4096
+        maxconn 50000
+        default_backend backend
+
+backend backend
+        mode tcp
+        option log-health-checks
+        option redispatch
+        option tcplog
+        balance roundrobin
+        server backend_host $BACKEND_HOST:$BACKEND_PORT
+        timeout connect 2s
+        timeout queue 5s
+        timeout server 3600s

+ 17 - 0
tcp-proxy-docker/ispooge-proxy-dev.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# starts an HTTP proxy that will be detected by the nginx
+# http/https proxy service.
+#
+# v1 - 2018-01-01
+
+HOSTNAME=v2.ispooge.com
+IP=192.168.1.11
+PORT=33080
+
+DOMAINS=$HOSTNAME
+IMAGE=tcp-proxy:2
+
+NAME="$HOSTNAME-proxy"
+
+docker run -d --restart always --name $NAME -e "BACKEND_HOST=$IP" -e "BACKEND_PORT=$PORT" -e "VIRTUAL_HOST=$DOMAINS" -h $HOSTNAME $IMAGE

+ 17 - 0
tcp-proxy-docker/ispooge-proxy.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# starts an HTTP proxy that will be detected by the nginx
+# http/https proxy service.
+#
+# v1 - 2018-01-01
+
+HOSTNAME=ispooge.com
+IP=192.168.1.8
+PORT=33080
+
+DOMAINS=$HOSTNAME
+IMAGE=tcp-proxy:2
+
+NAME="$HOSTNAME-proxy"
+
+docker run -d --restart always --name $NAME -e "BACKEND_HOST=$IP" -e "BACKEND_PORT=$PORT" -e "VIRTUAL_HOST=$DOMAINS" -h $HOSTNAME $IMAGE

+ 15 - 0
tcp-proxy-docker/ispooge-v1-proxy.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# starts an HTTP proxy that will be detected by the nginx
+# http/https proxy service.
+#
+# v1 - 2018-01-01
+
+HOSTNAME=ispooge.com
+IP=192.168.1.8
+PORT=80
+
+DOMAINS=$HOSTNAME,www.$HOSTNAME,broken.$HOSTNAME
+IMAGE=tcp-proxy
+
+docker run -d --restart always --name "$HOSTNAME-proxy" -e "BACKEND_HOST=$IP" -e "BACKEND_PORT=$PORT" -e "VIRTUAL_HOST=$DOMAINS" -h $HOSTNAME $IMAGE

+ 15 - 0
tcp-proxy-docker/tinydatacenter-proxy.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# starts an HTTP proxy that will be detected by the nginx
+# http/https proxy service.
+#
+# v1 - 2018-01-01
+
+HOSTNAME=tinydatacenter.com
+IP=192.168.1.7
+PORT=80
+
+DOMAINS=$HOSTNAME,www.$HOSTNAME
+IMAGE=tcp-proxy
+
+docker run -d --restart always --name "$HOSTNAME-proxy" -e "BACKEND_HOST=$IP" -e "BACKEND_PORT=$PORT" -e "VIRTUAL_HOST=$DOMAINS" -h $HOSTNAME $IMAGE

+ 20 - 0
tcp-proxy-docker/tinydatacenter-v2-proxy-dev.sh

@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# starts an HTTP proxy that will be detected by the nginx
+# http/https proxy service.
+#
+# v1 - 2018-01-01
+
+HOSTNAME=broken.tinydatacenter.com
+IP=192.168.1.7
+PORT=23000
+
+DOMAINS=$HOSTNAME
+IMAGE=tcp-proxy:2
+
+docker run -d --restart always --name "$HOSTNAME-proxy" \
+  -h $HOSTNAME \
+  -e "VIRTUAL_HOST=$DOMAINS" \
+  -e "BACKEND_HOST=$IP" \
+  -e "BACKEND_PORT=$PORT" \
+  $IMAGE

+ 20 - 0
tcp-proxy-docker/tinydatacenter-v2-proxy.sh

@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# starts an HTTP proxy that will be detected by the nginx
+# http/https proxy service.
+#
+# v1 - 2018-01-01
+
+HOSTNAME=v2.tinydatacenter.com
+IP=192.168.1.7
+PORT=23080
+
+DOMAINS=$HOSTNAME,new.tinydatacenter.com
+IMAGE=tcp-proxy:2
+
+docker run -d --restart always --name "$HOSTNAME-proxy" \
+  -h $HOSTNAME \
+  -e "VIRTUAL_HOST=$DOMAINS" \
+  -e "BACKEND_HOST=$IP" \
+  -e "BACKEND_PORT=$PORT" \
+  $IMAGE