diff --git a/.ansible-lint b/.ansible-lint
new file mode 100644
index 0000000..9e2ef12
--- /dev/null
+++ b/.ansible-lint
@@ -0,0 +1,3 @@
+---
+warn_list:
+ - 'role-name'
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..d38ab0e
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,69 @@
+---
+
+name: "CI"
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - "main"
+ pull_request:
+ branches:
+ - "main"
+
+env:
+ python_version: "3.9"
+
+jobs:
+ test:
+ name: "Molecule"
+ runs-on: "ubuntu-latest"
+ strategy:
+ matrix:
+ distro:
+ - "debian10"
+
+ steps:
+ - name: "Check out the codebase"
+ # see https://github.com/marketplace/actions/checkout
+ uses: "actions/checkout@v2"
+
+ - name: "Set up Python"
+ # see https://github.com/marketplace/actions/setup-python
+ uses: "actions/setup-python@v2"
+ with:
+ python-version: "${{ env.python_version }}"
+
+ - name: "Cache Python packages"
+ # see https://github.com/marketplace/actions/cache
+ uses: "actions/cache@v2"
+ env:
+ cache_name: "pip"
+ with:
+ path: "~/.cache/pip"
+ key: "${{ runner.os }}-${{ env.cache_name }}-${{ hashFiles('**/requirements.txt') }}"
+ restore-keys: |
+ ${{ runner.os }}-${{ env.cache_name }}-
+
+ - name: "Install Python dependencies"
+ run: |
+ python -m pip install wheel
+ python -m pip install --no-deps --requirement requirements.txt
+
+ - name: "Install galaxy roles"
+ run: "ansible-galaxy install --role-file requirements.yml"
+
+ - name: "(Workaround) Remove all identities except of dev from vault_identity_list"
+ # See https://github.com/marketplace/actions/find-and-replace
+ uses: "jacobtomlinson/gha-find-replace@master"
+ with:
+ find: "vault_identity_list.*"
+ replace: "vault_identity_list = dev@.vault-pass.dev"
+ include: "ansible.cfg"
+
+ - name: "Run Molecule tests"
+ env:
+ PY_COLORS: "1"
+ ANSIBLE_FORCE_COLOR: "1"
+ MOLECULE_DISTRO: "${{ matrix.distro }}"
+ run: "molecule test"
diff --git a/.vault-pass.dev b/.vault-pass.dev
new file mode 100644
index 0000000..05ef4d0
--- /dev/null
+++ b/.vault-pass.dev
@@ -0,0 +1 @@
+56/@,v61yHDn.Df4u93yk§´lR@SK4F(N#Mi@A(vhkx))lm$fpsv1k8/°yCLT
\ No newline at end of file
diff --git a/.yamllint b/.yamllint
new file mode 100644
index 0000000..506c7e4
--- /dev/null
+++ b/.yamllint
@@ -0,0 +1,35 @@
+---
+# Based on ansible-lint config
+extends: default
+ignore: |
+ src/molecule/cookiecutter/scenario/driver/delegated/{{cookiecutter.molecule_directory}}
+ src/molecule/cookiecutter/molecule/{{cookiecutter.role_name}}
+ src/molecule/cookiecutter/scenario/verifier/ansible/{{cookiecutter.molecule_directory}}
+rules:
+ braces:
+ max-spaces-inside: 1
+ level: error
+ brackets:
+ max-spaces-inside: 1
+ level: error
+ colons:
+ max-spaces-after: -1
+ level: error
+ commas:
+ max-spaces-after: -1
+ level: error
+ comments: disable
+ comments-indentation: disable
+ document-start: disable
+ empty-lines:
+ max: 3
+ level: error
+ hyphens:
+ level: error
+ indentation: disable
+ key-duplicates: enable
+ line-length: disable
+ new-lines:
+ type: unix
+ trailing-spaces: disable
+ truthy: disable
diff --git a/Vagrantfile b/Vagrantfile
new file mode 100644
index 0000000..32c9780
--- /dev/null
+++ b/Vagrantfile
@@ -0,0 +1,87 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# All Vagrant configuration is done below. The "2" in Vagrant.configure
+# configures the configuration version (we support older styles for
+# backwards compatibility). Please don't change it unless you know what
+# you're doing.
+Vagrant.configure("2") do |config|
+ config.vm.define "node0"
+
+ config.vm.provision "ansible" do |ansible|
+ ansible.playbook = "playbook.yml"
+ ansible.tags = "all" # change to "all, recreate" to recreate git repos
+ ansible.groups = {
+ "dev" => ["node0"],
+ }
+ end
+
+
+ # The most common configuration options are documented and commented below.
+ # For a complete reference, please see the online documentation at
+ # https://docs.vagrantup.com.
+
+ # Every Vagrant development environment requires a box. You can search for
+ # boxes at https://vagrantcloud.com/search.
+ config.vm.box = "generic/debian10"
+
+ # Disable automatic box update checking. If you disable this, then
+ # boxes will only be checked for updates when the user runs
+ # `vagrant box outdated`. This is not recommended.
+ # config.vm.box_check_update = false
+
+ # Create a forwarded port mapping which allows access to a specific port
+ # within the machine from a port on the host machine. In the example below,
+ # accessing "localhost:8080" will access port 80 on the guest machine.
+ # NOTE: This will enable public access to the opened port
+ # config.vm.network "forwarded_port", guest: 80, host: 8080
+ config.vm.network "forwarded_port", guest: 3000, host: 3000
+ config.vm.network "forwarded_port", guest: 9090, host: 9090
+ config.vm.network "forwarded_port", guest: 9091, host: 9091
+ config.vm.network "forwarded_port", guest: 9093, host: 9093
+ config.vm.network "forwarded_port", guest: 8000, host: 8000
+ config.vm.network "forwarded_port", guest: 8443, host: 8443
+
+ # Create a forwarded port mapping which allows access to a specific port
+ # within the machine from a port on the host machine and only allow access
+ # via 127.0.0.1 to disable public access
+ # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
+
+ # Create a private network, which allows host-only access to the machine
+ # using a specific IP.
+ # config.vm.network "private_network", ip: "192.168.33.10"
+
+ # Create a public network, which generally matched to bridged network.
+ # Bridged networks make the machine appear as another physical device on
+ # your network.
+ # config.vm.network "public_network"
+
+ # Share an additional folder to the guest VM. The first argument is
+ # the path on the host to the actual folder. The second argument is
+ # the path on the guest to mount the folder. And the optional third
+ # argument is a set of non-required options.
+ # config.vm.synced_folder "../data", "/vagrant_data"
+
+ # Provider-specific configuration so you can fine-tune various
+ # backing providers for Vagrant. These expose provider-specific options.
+ # Example for VirtualBox:
+ #
+ # config.vm.provider "virtualbox" do |vb|
+ # # Display the VirtualBox GUI when booting the machine
+ # vb.gui = true
+ #
+ # # Customize the amount of memory on the VM:
+ # vb.memory = "1024"
+ # end
+ #
+ # View the documentation for the provider you are using for more
+ # information on available options.
+
+ # Enable provisioning with a shell script. Additional provisioners such as
+ # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
+ # documentation for more information about their specific syntax and use.
+ # config.vm.provision "shell", inline: <<-SHELL
+ # apt-get update
+ # apt-get install -y apache2
+ # SHELL
+end
diff --git a/ansible.cfg b/ansible.cfg
new file mode 100644
index 0000000..5110ea9
--- /dev/null
+++ b/ansible.cfg
@@ -0,0 +1,8 @@
+[defaults]
+vault_identity_list = prod@~/.ansible/vaults/bvrn/.vault-pass.prod, dev@.vault-pass.dev
+INVENTORY = inventory
+deprecation_warnings=False
+interpreter_python = /usr/bin/python3
+stdout_callback = debug
+strategy = free
+forks = 30
diff --git a/files/jitsi-meet/docker-compose.yml b/files/jitsi-meet/docker-compose.yml
new file mode 100644
index 0000000..00239ae
--- /dev/null
+++ b/files/jitsi-meet/docker-compose.yml
@@ -0,0 +1,308 @@
+version: '3'
+
+services:
+ # Frontend
+ web:
+ image: jitsi/web:stable-6826
+ restart: ${RESTART_POLICY}
+ expose:
+ - '80'
+ volumes:
+ - ${CONFIG}/web:/config:Z
+ - ${CONFIG}/web/crontabs:/var/spool/cron/crontabs:Z
+ - ${CONFIG}/transcripts:/usr/share/jitsi-meet/transcripts:Z
+ environment:
+ - AMPLITUDE_ID
+ - ANALYTICS_SCRIPT_URLS
+ - ANALYTICS_WHITELISTED_EVENTS
+ - CALLSTATS_CUSTOM_SCRIPT_URL
+ - CALLSTATS_ID
+ - CALLSTATS_SECRET
+ - CHROME_EXTENSION_BANNER_JSON
+ - CONFCODE_URL
+ - CONFIG_EXTERNAL_CONNECT
+ - DEFAULT_LANGUAGE
+ - DEPLOYMENTINFO_ENVIRONMENT
+ - DEPLOYMENTINFO_ENVIRONMENT_TYPE
+ - DEPLOYMENTINFO_REGION
+ - DEPLOYMENTINFO_SHARD
+ - DEPLOYMENTINFO_USERREGION
+ - DESKTOP_SHARING_FRAMERATE_MIN
+ - DESKTOP_SHARING_FRAMERATE_MAX
+ - DIALIN_NUMBERS_URL
+ - DIALOUT_AUTH_URL
+ - DIALOUT_CODES_URL
+ - DISABLE_AUDIO_LEVELS
+ - DISABLE_DEEP_LINKING
+ - DISABLE_HTTPS
+ - DISABLE_POLLS
+ - DISABLE_REACTIONS
+ - DROPBOX_APPKEY
+ - DROPBOX_REDIRECT_URI
+ - DYNAMIC_BRANDING_URL
+ - ENABLE_AUDIO_PROCESSING
+ - ENABLE_AUTH
+ - ENABLE_BREAKOUT_ROOMS
+ - ENABLE_CALENDAR
+ - ENABLE_COLIBRI_WEBSOCKET
+ - ENABLE_FILE_RECORDING_SERVICE
+ - ENABLE_FILE_RECORDING_SERVICE_SHARING
+ - ENABLE_FLOC
+ - ENABLE_GUESTS
+ - ENABLE_HSTS
+ - ENABLE_HTTP_REDIRECT
+ - ENABLE_IPV6
+ - ENABLE_LETSENCRYPT
+ - ENABLE_LIPSYNC
+ - ENABLE_NO_AUDIO_DETECTION
+ - ENABLE_NOISY_MIC_DETECTION
+ - ENABLE_PREJOIN_PAGE
+ - ENABLE_P2P
+ - ENABLE_WELCOME_PAGE
+ - ENABLE_CLOSE_PAGE
+ - ENABLE_RECORDING
+ - ENABLE_REMB
+ - ENABLE_REQUIRE_DISPLAY_NAME
+ - ENABLE_SIMULCAST
+ - ENABLE_STATS_ID
+ - ENABLE_STEREO
+ - ENABLE_SUBDOMAINS
+ - ENABLE_TALK_WHILE_MUTED
+ - ENABLE_TCC
+ - ENABLE_TRANSCRIPTIONS
+ - ENABLE_XMPP_WEBSOCKET
+ - ETHERPAD_PUBLIC_URL
+ - ETHERPAD_URL_BASE
+ - GOOGLE_ANALYTICS_ID
+ - GOOGLE_API_APP_CLIENT_ID
+ - HIDE_PREMEETING_BUTTONS
+ - INVITE_SERVICE_URL
+ - JICOFO_AUTH_USER
+ - LETSENCRYPT_DOMAIN
+ - LETSENCRYPT_EMAIL
+ - LETSENCRYPT_USE_STAGING
+ - MATOMO_ENDPOINT
+ - MATOMO_SITE_ID
+ - MICROSOFT_API_APP_CLIENT_ID
+ - NGINX_RESOLVER
+ - NGINX_WORKER_PROCESSES
+ - NGINX_WORKER_CONNECTIONS
+ - PEOPLE_SEARCH_URL
+ - PUBLIC_URL
+ - P2P_PREFERRED_CODEC
+ - RESOLUTION
+ - RESOLUTION_MIN
+ - RESOLUTION_WIDTH
+ - RESOLUTION_WIDTH_MIN
+ - START_AUDIO_MUTED
+ - START_AUDIO_ONLY
+ - START_BITRATE
+ - START_SILENT
+ - START_WITH_AUDIO_MUTED
+ - START_VIDEO_MUTED
+ - START_WITH_VIDEO_MUTED
+ - TESTING_CAP_SCREENSHARE_BITRATE
+ - TESTING_OCTO_PROBABILITY
+ - TOKEN_AUTH_URL
+ - TOOLBAR_BUTTONS
+ - TZ
+ - VIDEOQUALITY_BITRATE_H264_LOW
+ - VIDEOQUALITY_BITRATE_H264_STANDARD
+ - VIDEOQUALITY_BITRATE_H264_HIGH
+ - VIDEOQUALITY_BITRATE_VP8_LOW
+ - VIDEOQUALITY_BITRATE_VP8_STANDARD
+ - VIDEOQUALITY_BITRATE_VP8_HIGH
+ - VIDEOQUALITY_BITRATE_VP9_LOW
+ - VIDEOQUALITY_BITRATE_VP9_STANDARD
+ - VIDEOQUALITY_BITRATE_VP9_HIGH
+ - VIDEOQUALITY_ENFORCE_PREFERRED_CODEC
+ - VIDEOQUALITY_PREFERRED_CODEC
+ - XMPP_AUTH_DOMAIN
+ - XMPP_BOSH_URL_BASE
+ - XMPP_DOMAIN
+ - XMPP_GUEST_DOMAIN
+ - XMPP_MUC_DOMAIN
+ - XMPP_RECORDER_DOMAIN
+ - VIRTUAL_HOST
+ - VIRTUAL_PORT
+ - LETSENCRYPT_HOST
+ networks:
+ - meet.jitsi
+ - proxy
+
+ # XMPP server
+ prosody:
+ image: jitsi/prosody:stable-6826
+ restart: ${RESTART_POLICY}
+ expose:
+ - '5222'
+ - '5347'
+ - '5280'
+ volumes:
+ - ${CONFIG}/prosody/config:/config:Z
+ - ${CONFIG}/prosody/prosody-plugins-custom:/prosody-plugins-custom:Z
+ environment:
+ - AUTH_TYPE
+ - DISABLE_POLLS
+ - ENABLE_AUTH
+ - ENABLE_AV_MODERATION
+ - ENABLE_BREAKOUT_ROOMS
+ - ENABLE_GUESTS
+ - ENABLE_LOBBY
+ - ENABLE_XMPP_WEBSOCKET
+ - GLOBAL_CONFIG
+ - GLOBAL_MODULES
+ - JIBRI_RECORDER_USER
+ - JIBRI_RECORDER_PASSWORD
+ - JIBRI_XMPP_USER
+ - JIBRI_XMPP_PASSWORD
+ - JICOFO_AUTH_USER
+ - JICOFO_AUTH_PASSWORD
+ - JICOFO_COMPONENT_SECRET
+ - JIGASI_XMPP_USER
+ - JIGASI_XMPP_PASSWORD
+ - JVB_AUTH_USER
+ - JVB_AUTH_PASSWORD
+ - JWT_APP_ID
+ - JWT_APP_SECRET
+ - JWT_ACCEPTED_ISSUERS
+ - JWT_ACCEPTED_AUDIENCES
+ - JWT_ASAP_KEYSERVER
+ - JWT_ALLOW_EMPTY
+ - JWT_AUTH_TYPE
+ - JWT_TOKEN_AUTH_MODULE
+ - LOG_LEVEL
+ - LDAP_AUTH_METHOD
+ - LDAP_BASE
+ - LDAP_BINDDN
+ - LDAP_BINDPW
+ - LDAP_FILTER
+ - LDAP_VERSION
+ - LDAP_TLS_CIPHERS
+ - LDAP_TLS_CHECK_PEER
+ - LDAP_TLS_CACERT_FILE
+ - LDAP_TLS_CACERT_DIR
+ - LDAP_START_TLS
+ - LDAP_URL
+ - LDAP_USE_TLS
+ - PUBLIC_URL
+ - TURN_CREDENTIALS
+ - TURN_HOST
+ - TURNS_HOST
+ - TURN_PORT
+ - TURNS_PORT
+ - TZ
+ - XMPP_DOMAIN
+ - XMPP_AUTH_DOMAIN
+ - XMPP_GUEST_DOMAIN
+ - XMPP_MUC_DOMAIN
+ - XMPP_INTERNAL_MUC_DOMAIN
+ - XMPP_MODULES
+ - XMPP_MUC_MODULES
+ - XMPP_INTERNAL_MUC_MODULES
+ - XMPP_RECORDER_DOMAIN
+ - XMPP_CROSS_DOMAIN
+ networks:
+ meet.jitsi:
+ aliases:
+ - ${XMPP_SERVER}
+
+ # Focus component
+ jicofo:
+ image: jitsi/jicofo:stable-6826
+ restart: ${RESTART_POLICY}
+ volumes:
+ - ${CONFIG}/jicofo:/config:Z
+ environment:
+ - AUTH_TYPE
+ - BRIDGE_AVG_PARTICIPANT_STRESS
+ - BRIDGE_STRESS_THRESHOLD
+ - ENABLE_AUTH
+ - ENABLE_AUTO_OWNER
+ - ENABLE_CODEC_VP8
+ - ENABLE_CODEC_VP9
+ - ENABLE_CODEC_H264
+ - ENABLE_OCTO
+ - ENABLE_RECORDING
+ - ENABLE_SCTP
+ - ENABLE_AUTO_LOGIN
+ - JICOFO_AUTH_USER
+ - JICOFO_AUTH_PASSWORD
+ - JICOFO_ENABLE_BRIDGE_HEALTH_CHECKS
+ - JICOFO_CONF_INITIAL_PARTICIPANT_WAIT_TIMEOUT
+ - JICOFO_CONF_SINGLE_PARTICIPANT_TIMEOUT
+ - JICOFO_ENABLE_HEALTH_CHECKS
+ - JICOFO_SHORT_ID
+ - JICOFO_RESERVATION_ENABLED
+ - JICOFO_RESERVATION_REST_BASE_URL
+ - JIBRI_BREWERY_MUC
+ - JIBRI_REQUEST_RETRIES
+ - JIBRI_PENDING_TIMEOUT
+ - JIGASI_BREWERY_MUC
+ - JIGASI_SIP_URI
+ - JVB_BREWERY_MUC
+ - MAX_BRIDGE_PARTICIPANTS
+ - OCTO_BRIDGE_SELECTION_STRATEGY
+ - SENTRY_DSN="${JICOFO_SENTRY_DSN:-0}"
+ - SENTRY_ENVIRONMENT
+ - SENTRY_RELEASE
+ - TZ
+ - XMPP_DOMAIN
+ - XMPP_AUTH_DOMAIN
+ - XMPP_INTERNAL_MUC_DOMAIN
+ - XMPP_MUC_DOMAIN
+ - XMPP_RECORDER_DOMAIN
+ - XMPP_SERVER
+ depends_on:
+ - prosody
+ networks:
+ meet.jitsi:
+
+ # Video bridge
+ jvb:
+ image: jitsi/jvb:stable-6826
+ restart: ${RESTART_POLICY}
+ ports:
+ - '${JVB_PORT}:${JVB_PORT}/udp'
+ - '${JVB_TCP_PORT}:${JVB_TCP_PORT}'
+ volumes:
+ - ${CONFIG}/jvb:/config:Z
+ environment:
+ - DOCKER_HOST_ADDRESS
+ - ENABLE_COLIBRI_WEBSOCKET
+ - ENABLE_OCTO
+ - JVB_AUTH_USER
+ - JVB_AUTH_PASSWORD
+ - JVB_BREWERY_MUC
+ - JVB_PORT
+ - JVB_MUC_NICKNAME
+ - JVB_TCP_HARVESTER_DISABLED
+ - JVB_TCP_PORT
+ - JVB_TCP_MAPPED_PORT
+ - JVB_STUN_SERVERS
+ - JVB_OCTO_BIND_ADDRESS
+ - JVB_OCTO_PUBLIC_ADDRESS
+ - JVB_OCTO_BIND_PORT
+ - JVB_OCTO_REGION
+ - JVB_WS_DOMAIN
+ - JVB_WS_SERVER_ID
+ - PUBLIC_URL
+ - SENTRY_DSN="${JVB_SENTRY_DSN:-0}"
+ - SENTRY_ENVIRONMENT
+ - SENTRY_RELEASE
+ - COLIBRI_REST_ENABLED
+ - SHUTDOWN_REST_ENABLED
+ - TZ
+ - XMPP_AUTH_DOMAIN
+ - XMPP_INTERNAL_MUC_DOMAIN
+ - XMPP_SERVER
+ depends_on:
+ - prosody
+ networks:
+ meet.jitsi:
+
+# Custom network so all services can communicate using a FQDN
+networks:
+ meet.jitsi:
+ proxy:
+ external: true
diff --git a/files/jitsi-meet/etherpad.yml b/files/jitsi-meet/etherpad.yml
new file mode 100644
index 0000000..34721c3
--- /dev/null
+++ b/files/jitsi-meet/etherpad.yml
@@ -0,0 +1,17 @@
+version: '3'
+
+services:
+ # Etherpad: real-time collaborative document editing
+ etherpad:
+ image: etherpad/etherpad:1.8.6
+ restart: ${RESTART_POLICY}
+ environment:
+ - TITLE=${ETHERPAD_TITLE}
+ - DEFAULT_PAD_TEXT=${ETHERPAD_DEFAULT_PAD_TEXT}
+ - SKIN_NAME=${ETHERPAD_SKIN_NAME}
+ - SKIN_VARIANTS=${ETHERPAD_SKIN_VARIANTS}
+ - SUPPRESS_ERRORS_IN_PAD_TEXT=${ETHERPAD_SUPPRESS_ERRORS_IN_PAD_TEXT:-false}
+ networks:
+ meet.jitsi:
+ aliases:
+ - etherpad.meet.jitsi
diff --git a/files/keycloak/docker-compose.yml b/files/keycloak/docker-compose.yml
new file mode 100644
index 0000000..f684a8e
--- /dev/null
+++ b/files/keycloak/docker-compose.yml
@@ -0,0 +1,58 @@
+version: '3'
+
+volumes:
+ postgresql_data:
+ driver: local
+
+services:
+ postgresql:
+ image: docker.io/bitnami/postgresql:11
+ environment:
+ - POSTGRESQL_USERNAME=${KEYCLOAK_DATABASE_USER:-bn_keycloak}
+ - POSTGRESQL_PASSWORD=${KEYCLOAK_DATABASE_PASSWORD:?KEYCLOAK_DATABASE_PASSWORD needs to be defined in .env file}
+ - POSTGRESQL_DATABASE=${KEYCLOAK_DATABASE_NAME:-bitnami_keycloak}
+ volumes:
+ - 'postgresql_data:/bitnami/postgresql'
+ networks:
+ - default
+
+ keycloak:
+ image: docker.io/bitnami/keycloak:18.0.1
+ environment:
+ - KEYCLOAK_CREATE_ADMIN_USER
+ - KEYCLOAK_ADMIN_USER
+ - KEYCLOAK_ADMIN_PASSWORD
+ - KEYCLOAK_MANAGEMENT_USER
+ - KEYCLOAK_MANAGEMENT_PASSWORD
+ - KEYCLOAK_DATABASE_HOST
+ - KEYCLOAK_DATABASE_PORT
+ - KEYCLOAK_DATABASE_NAME
+ - KEYCLOAK_DATABASE_USER
+ - KEYCLOAK_DATABASE_PASSWORD
+ - KEYCLOAK_DATABASE_SCHEMA
+ - KEYCLOAK_JDBC_PARAMS
+ - KEYCLOAK_HTTP_PORT
+ - KEYCLOAK_HTTPS_PORT
+ - KEYCLOAK_BIND_ADDRESS
+ - KEYCLOAK_PROXY
+ - KEYCLOAK_PRODUCTION
+ - VIRTUAL_HOST
+ - VIRTUAL_PORT
+ - LETSENCRYPT_HOST
+ - LETSENCRYPT_EMAIL
+ expose:
+ - 8080
+ depends_on:
+ - postgresql
+ volumes:
+ - './themes/bvrn:/opt/bitnami/keycloak/themes/bvrn'
+ networks:
+ - proxy
+ - ldap
+ - default
+
+networks:
+ proxy:
+ external: true
+ ldap:
+ external: true
diff --git a/files/keycloak/themes/bvrn/account/resources/img/bvrn.svg b/files/keycloak/themes/bvrn/account/resources/img/bvrn.svg
new file mode 100644
index 0000000..6ce45b9
--- /dev/null
+++ b/files/keycloak/themes/bvrn/account/resources/img/bvrn.svg
@@ -0,0 +1,75 @@
+
+
+
+
+
diff --git a/files/keycloak/themes/bvrn/account/theme.properties b/files/keycloak/themes/bvrn/account/theme.properties
new file mode 100644
index 0000000..06df1d1
--- /dev/null
+++ b/files/keycloak/themes/bvrn/account/theme.properties
@@ -0,0 +1,20 @@
+# This theme will inherit everything from its parent unless
+# it is overridden in the current theme.
+parent=keycloak.v2
+
+# Look at the styles.css file to see examples of using PatternFly's CSS variables
+# for modifying look and feel.
+#styles=css/styles.css
+
+# This is the logo in upper lefthand corner.
+# It must be a relative path.
+logo=/img/bvrn.svg
+
+# This is the link followed when clicking on the logo.
+# It can be any valid URL, including an external site.
+logoUrl=./
+
+# This is the icon for the account console.
+# It must be a relative path.
+favIcon=/public/favicon.ico
+
diff --git a/files/keycloak/themes/bvrn/login/resources/css/custom.css b/files/keycloak/themes/bvrn/login/resources/css/custom.css
new file mode 100644
index 0000000..5610e83
--- /dev/null
+++ b/files/keycloak/themes/bvrn/login/resources/css/custom.css
@@ -0,0 +1,11 @@
+#kc-header-wrapper {
+ font-size: 29px;
+ text-transform: uppercase;
+ letter-spacing: 3px;
+ line-height: 1.2em;
+ padding: 62px 10px 60px;
+ white-space: normal;
+ background: url("../img/bvrn.svg") no-repeat center top;
+ background-size: contain;
+}
+
diff --git a/files/keycloak/themes/bvrn/login/resources/img/bvrn.svg b/files/keycloak/themes/bvrn/login/resources/img/bvrn.svg
new file mode 100644
index 0000000..2ba27c6
--- /dev/null
+++ b/files/keycloak/themes/bvrn/login/resources/img/bvrn.svg
@@ -0,0 +1,73 @@
+
+
+
+
diff --git a/files/keycloak/themes/bvrn/login/theme.properties b/files/keycloak/themes/bvrn/login/theme.properties
new file mode 100644
index 0000000..490745a
--- /dev/null
+++ b/files/keycloak/themes/bvrn/login/theme.properties
@@ -0,0 +1,5 @@
+parent=keycloak
+import=common/keycloak
+
+styles=css/login.css css/tile.css css/custom.css
+
diff --git a/files/limesurvey/docker-compose.yml b/files/limesurvey/docker-compose.yml
new file mode 100644
index 0000000..dcd81eb
--- /dev/null
+++ b/files/limesurvey/docker-compose.yml
@@ -0,0 +1,43 @@
+version: '3.5'
+
+services:
+
+ limesurvey:
+ image: acspri/limesurvey:5.3.9
+ expose:
+ - 80
+ environment:
+ - LIMESURVEY_DB_PASSWORD
+ - LIMESURVEY_ADMIN_USER
+ - LIMESURVEY_ADMIN_PASSWORD
+ - LIMESURVEY_ADMIN_NAME
+ - LIMESURVEY_ADMIN_EMAIL
+ - VIRTUAL_HOST
+ - VIRTUAL_PORT
+ - LETSENCRYPT_HOST
+ - LETSENCRYPT_EMAIL
+ networks:
+ - default
+ - proxy
+ volumes:
+ - plugins:/var/www/html/plugins
+ - upload:/var/www/html/upload
+ - config:/var/www/html/application/config
+
+ mysql:
+ image: mariadb:10.7.3
+ environment:
+ - MYSQL_ROOT_PASSWORD
+ volumes:
+ - db:/var/lib/mysql
+
+volumes:
+ db:
+ plugins:
+ upload:
+ config:
+
+networks:
+ proxy:
+ external: true
+
diff --git a/files/mattermost/docker-compose.yml b/files/mattermost/docker-compose.yml
new file mode 100644
index 0000000..d2a89d8
--- /dev/null
+++ b/files/mattermost/docker-compose.yml
@@ -0,0 +1,78 @@
+# took from https://github.com/mattermost/docker
+
+# https://docs.docker.com/compose/environment-variables/
+
+version: "2.4"
+
+services:
+ postgres:
+ image: postgres:${POSTGRES_IMAGE_TAG}
+ restart: ${RESTART_POLICY}
+ security_opt:
+ - no-new-privileges:true
+ pids_limit: 100
+ read_only: true
+ tmpfs:
+ - /tmp
+ - /var/run/postgresql
+ volumes:
+ - ${POSTGRES_DATA_PATH}:/var/lib/postgresql/data
+ environment:
+ # timezone inside container
+ - TZ
+
+ # necessary Postgres options/variables
+ - POSTGRES_USER
+ - POSTGRES_PASSWORD
+ - POSTGRES_DB
+
+ mattermost:
+ depends_on:
+ - postgres
+ image: mattermost/${MATTERMOST_IMAGE}:${MATTERMOST_IMAGE_TAG}
+ restart: ${RESTART_POLICY}
+ security_opt:
+ - no-new-privileges:true
+ pids_limit: 200
+ read_only: ${MATTERMOST_CONTAINER_READONLY}
+ tmpfs:
+ - /tmp
+ volumes:
+ - ${MATTERMOST_CONFIG_PATH}:/mattermost/config:rw
+ - ${MATTERMOST_DATA_PATH}:/mattermost/data:rw
+ - ${MATTERMOST_LOGS_PATH}:/mattermost/logs:rw
+ - ${MATTERMOST_PLUGINS_PATH}:/mattermost/plugins:rw
+ - ${MATTERMOST_CLIENT_PLUGINS_PATH}:/mattermost/client/plugins:rw
+ - ${MATTERMOST_BLEVE_INDEXES_PATH}:/mattermost/bleve-indexes:rw
+ # When you want to use SSO with GitLab, you have to add the cert pki chain of GitLab inside Alpine
+ # to avoid Token request failed: certificate signed by unknown authority
+ # (link: https://github.com/mattermost/mattermost-server/issues/13059 and https://github.com/mattermost/docker/issues/34)
+ # - ${GITLAB_PKI_CHAIN_PATH}:/etc/ssl/certs/pki_chain.pem:ro
+ environment:
+ # timezone inside container
+ - TZ
+
+ # necessary Mattermost options/variables (see env.example)
+ - MM_SQLSETTINGS_DRIVERNAME
+ - MM_SQLSETTINGS_DATASOURCE
+
+ # necessary for bleve
+ - MM_BLEVESETTINGS_INDEXDIR
+
+ # additional settings
+ - MM_SERVICESETTINGS_SITEURL
+
+ # nginx-letsencrypt-proxy
+ - VIRTUAL_HOST
+ - VIRTUAL_PORT
+ - LETSENCRYPT_HOST
+ - LETSENCRYPT_EMAIL
+ expose:
+ - 8065
+ networks:
+ - default
+ - proxy
+
+networks:
+ proxy:
+ external: true
diff --git a/files/nextcloud/app/Dockerfile b/files/nextcloud/app/Dockerfile
new file mode 100644
index 0000000..5da2333
--- /dev/null
+++ b/files/nextcloud/app/Dockerfile
@@ -0,0 +1,51 @@
+FROM nextcloud:23.0.3-fpm-alpine
+
+RUN set -ex; \
+ \
+ apk add --no-cache \
+ ffmpeg \
+ imagemagick \
+ procps \
+# samba-client \
+# supervisor \
+ libreoffice \
+ ;
+
+RUN set -ex; \
+ \
+ apk add --no-cache --virtual .build-deps \
+ $PHPIZE_DEPS \
+ imap-dev \
+ krb5-dev \
+ openssl-dev \
+# samba-dev \
+ bzip2-dev \
+ ; \
+ \
+ docker-php-ext-configure imap --with-kerberos --with-imap-ssl; \
+ docker-php-ext-install \
+ bz2 \
+ imap \
+ ; \
+# pecl install smbclient; \
+# docker-php-ext-enable smbclient; \
+ \
+ runDeps="$( \
+ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \
+ | tr ',' '\n' \
+ | sort -u \
+ | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
+ )"; \
+ apk add --virtual .nextcloud-phpext-rundeps $runDeps; \
+ apk del .build-deps
+
+#RUN mkdir -p \
+# /var/log/supervisord \
+# /var/run/supervisord \
+#;
+
+#COPY supervisord.conf /
+
+#ENV NEXTCLOUD_UPDATE=1
+
+#CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf"]
\ No newline at end of file
diff --git a/files/nextcloud/docker-compose.yml b/files/nextcloud/docker-compose.yml
new file mode 100644
index 0000000..23c5fe5
--- /dev/null
+++ b/files/nextcloud/docker-compose.yml
@@ -0,0 +1,73 @@
+version: '3.2'
+
+services:
+ db:
+ image: postgres:13.6-alpine3.15
+ restart: always
+ volumes:
+ - db:/var/lib/postgresql/data
+ environment:
+ - POSTGRES_PASSWORD
+ - POSTGRES_DB
+ - POSTGRES_USER
+
+ redis:
+ image: redis:alpine
+ restart: always
+
+ app:
+ build: ./app
+ restart: always
+ volumes:
+ - nextcloud:/var/www/html
+ environment:
+ - POSTGRES_HOST
+ - REDIS_HOST
+ - POSTGRES_PASSWORD
+ - POSTGRES_DB
+ - POSTGRES_USER
+ depends_on:
+ - db
+ - redis
+
+ web:
+ image: nginx:stable-alpine
+ restart: always
+ volumes:
+ - nextcloud:/var/www/html:ro
+ - type: bind
+ source: ./nginx.conf
+ target: /etc/nginx/nginx.conf
+ read_only: true
+ environment:
+ - VIRTUAL_HOST
+ - VIRTUAL_PORT
+ - LETSENCRYPT_HOST
+ - LETSENCRYPT_EMAIL
+ depends_on:
+ - app
+ networks:
+ - proxy
+ - default
+
+ cron:
+ build: ./app
+ restart: always
+ volumes:
+ - nextcloud:/var/www/html
+ entrypoint: /cron.sh
+ depends_on:
+ - db
+ - redis
+
+volumes:
+ db:
+ nextcloud:
+ certs:
+ acme:
+ vhost.d:
+ html:
+
+networks:
+ proxy:
+ external: true
diff --git a/files/nextcloud/nginx.conf b/files/nextcloud/nginx.conf
new file mode 100644
index 0000000..8fbc162
--- /dev/null
+++ b/files/nextcloud/nginx.conf
@@ -0,0 +1,170 @@
+worker_processes auto;
+
+error_log /var/log/nginx/error.log warn;
+pid /var/run/nginx.pid;
+
+
+events {
+ worker_connections 1024;
+}
+
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ sendfile on;
+ #tcp_nopush on;
+
+ keepalive_timeout 65;
+
+ #gzip on;
+
+ upstream php-handler {
+ server app:9000;
+ }
+
+ server {
+ listen 80;
+
+ # HSTS settings
+ # WARNING: Only add the preload option once you read about
+ # the consequences in https://hstspreload.org/. This option
+ # will add the domain to a hardcoded list that is shipped
+ # in all major browsers and getting removed from this list
+ # could take several months.
+ #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
+
+ # set max upload size
+ client_max_body_size 512M;
+ fastcgi_buffers 64 4K;
+
+ # Enable gzip but do not remove ETag headers
+ gzip on;
+ gzip_vary on;
+ gzip_comp_level 4;
+ gzip_min_length 256;
+ gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
+ gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
+
+ # Pagespeed is not supported by Nextcloud, so if your server is built
+ # with the `ngx_pagespeed` module, uncomment this line to disable it.
+ #pagespeed off;
+
+ # HTTP response headers borrowed from Nextcloud `.htaccess`
+ add_header Referrer-Policy "no-referrer" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-Download-Options "noopen" always;
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-Permitted-Cross-Domain-Policies "none" always;
+ add_header X-Robots-Tag "none" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+
+ # Remove X-Powered-By, which is an information leak
+ fastcgi_hide_header X-Powered-By;
+
+ # Path to the root of your installation
+ root /var/www/html;
+
+ # Specify how to handle directories -- specifying `/index.php$request_uri`
+ # here as the fallback means that Nginx always exhibits the desired behaviour
+ # when a client requests a path that corresponds to a directory that exists
+ # on the server. In particular, if that directory contains an index.php file,
+ # that file is correctly served; if it doesn't, then the request is passed to
+ # the front-end controller. This consistent behaviour means that we don't need
+ # to specify custom rules for certain paths (e.g. images and other assets,
+ # `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
+ # `try_files $uri $uri/ /index.php$request_uri`
+ # always provides the desired behaviour.
+ index index.php index.html /index.php$request_uri;
+
+ # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
+ location = / {
+ if ( $http_user_agent ~ ^DavClnt ) {
+ return 302 /remote.php/webdav/$is_args$args;
+ }
+ }
+
+ location = /robots.txt {
+ allow all;
+ log_not_found off;
+ access_log off;
+ }
+
+ # Make a regex exception for `/.well-known` so that clients can still
+ # access it despite the existence of the regex rule
+ # `location ~ /(\.|autotest|...)` which would otherwise handle requests
+ # for `/.well-known`.
+ location ^~ /.well-known {
+ # The rules in this block are an adaptation of the rules
+ # in `.htaccess` that concern `/.well-known`.
+
+ location = /.well-known/carddav { return 301 /remote.php/dav/; }
+ location = /.well-known/caldav { return 301 /remote.php/dav/; }
+
+ location /.well-known/acme-challenge { try_files $uri $uri/ =404; }
+ location /.well-known/pki-validation { try_files $uri $uri/ =404; }
+
+ # Let Nextcloud's API for `/.well-known` URIs handle all other
+ # requests by passing them to the front-end controller.
+ return 301 /index.php$request_uri;
+ }
+
+ # Rules borrowed from `.htaccess` to hide certain paths from clients
+ location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
+ location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
+
+ # Ensure this block, which passes PHP files to the PHP process, is above the blocks
+ # which handle static assets (as seen below). If this block is not declared first,
+ # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
+ # to the URI, resulting in a HTTP 500 error response.
+ location ~ \.php(?:$|/) {
+ # Required for legacy support
+ rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
+
+ fastcgi_split_path_info ^(.+?\.php)(/.*)$;
+ set $path_info $fastcgi_path_info;
+
+ try_files $fastcgi_script_name =404;
+
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ fastcgi_param PATH_INFO $path_info;
+ #fastcgi_param HTTPS on;
+
+ fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice
+ fastcgi_param front_controller_active true; # Enable pretty urls
+ fastcgi_pass php-handler;
+
+ fastcgi_intercept_errors on;
+ fastcgi_request_buffering off;
+ }
+
+ location ~ \.(?:css|js|svg|gif)$ {
+ try_files $uri /index.php$request_uri;
+ expires 6M; # Cache-Control policy borrowed from `.htaccess`
+ access_log off; # Optional: Don't log access to assets
+ }
+
+ location ~ \.woff2?$ {
+ try_files $uri /index.php$request_uri;
+ expires 7d; # Cache-Control policy borrowed from `.htaccess`
+ access_log off; # Optional: Don't log access to assets
+ }
+
+ # Rule borrowed from `.htaccess`
+ location /remote {
+ return 301 /remote.php$request_uri;
+ }
+
+ location / {
+ try_files $uri $uri/ /index.php$request_uri;
+ }
+ }
+}
diff --git a/files/nginx-letsencrypt-proxy/.nginx.tmpl.backup b/files/nginx-letsencrypt-proxy/.nginx.tmpl.backup
new file mode 100644
index 0000000..51b51a9
--- /dev/null
+++ b/files/nginx-letsencrypt-proxy/.nginx.tmpl.backup
@@ -0,0 +1,402 @@
+{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }}
+
+{{ $external_http_port := coalesce $.Env.HTTP_PORT "80" }}
+{{ $external_https_port := coalesce $.Env.HTTPS_PORT "443" }}
+
+{{ 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 }}
+ {{ if .Network.IP }}
+ server {{ .Network.IP }} down;
+ {{ else }}
+ server 127.0.0.1 down;
+ {{ end }}
+ {{ end }}
+
+{{ end }}
+
+{{ define "ssl_policy" }}
+ {{ if eq .ssl_policy "Mozilla-Modern" }}
+ ssl_protocols TLSv1.3;
+ {{/* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 /*}}
+ {{/* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) /*}}
+ {{/* explicitly set ngnix default value in order to allow single servers to override the global http value */}}
+ ssl_ciphers HIGH:!aNULL:!MD5;
+ ssl_prefer_server_ciphers off;
+ {{ else if eq .ssl_policy "Mozilla-Intermediate" }}
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
+ ssl_prefer_server_ciphers off;
+ {{ else if eq .ssl_policy "Mozilla-Old" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-TLS-1-2-2017-01" }}
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-TLS-1-1-2017-01" }}
+ ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-2016-08" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-2015-05" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-2015-03" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-2015-02" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ 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;
+}
+
+# Apply fix for very long server names
+server_names_hash_bucket_size 128;
+
+# Default dhparam
+{{ if (exists "/etc/nginx/dhparam/dhparam.pem") }}
+ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
+{{ end }}
+
+# Set appropriate X-Forwarded-Ssl header
+map $scheme $proxy_x_forwarded_ssl {
+ default off;
+ https on;
+}
+
+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;
+
+{{/* Get the SSL_POLICY defined by this container, falling back to "Mozilla-Intermediate" */}}
+{{ $ssl_policy := or ($.Env.SSL_POLICY) "Mozilla-Intermediate" }}
+{{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }}
+
+{{ if $.Env.RESOLVERS }}
+resolver {{ $.Env.RESOLVERS }};
+{{ end }}
+
+{{ 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-Ssl $proxy_x_forwarded_ssl;
+proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
+
+# Mitigate httpoxy attack (see README for details)
+proxy_set_header Proxy "";
+{{ end }}
+
+{{ $access_log := (or (and (not $.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }}
+
+{{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }}
+server {
+ server_name _; # This is just an invalid value which will never trigger on a real hostname.
+ listen {{ $external_http_port }};
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_http_port }};
+ {{ end }}
+ {{ $access_log }}
+ 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 {{ $external_https_port }} ssl http2;
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_https_port }} ssl http2;
+ {{ end }}
+ {{ $access_log }}
+ return 503;
+
+ ssl_session_cache shared:SSL:50m;
+ 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" "," }}
+
+{{ $host := trim $host }}
+{{ $is_regexp := hasPrefix "~" $host }}
+{{ $upstream_name := when $is_regexp (sha1 $host) $host }}
+
+# {{ $host }}
+upstream {{ $upstream_name }} {
+
+{{ range $container := $containers }}
+ {{ $addrLen := len $container.Addresses }}
+
+ {{ range $knownNetwork := $CurrentContainer.Networks }}
+ {{ range $containerNetwork := $container.Networks }}
+ {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }}
+ ## Can be connected 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 }}
+ {{ else }}
+ # Cannot connect to network of this container
+ server 127.0.0.1 down;
+ {{ 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 := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }}
+
+{{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
+{{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }}
+
+{{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}}
+{{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) (or $.Env.HTTPS_METHOD "redirect") }}
+
+{{/* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default) */}}
+{{ $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }}
+
+{{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}}
+{{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $.Env.HSTS "max-age=31536000") }}
+
+{{/* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
+{{ $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
+
+
+{{/* 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 $https_method "nohttps") (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 {{ $external_http_port }} {{ $default_server }};
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_http_port }} {{ $default_server }};
+ {{ end }}
+ {{ $access_log }}
+
+ # Do not HTTPS redirect Let'sEncrypt ACME challenge
+ location /.well-known/acme-challenge/ {
+ auth_basic off;
+ allow all;
+ root /usr/share/nginx/html;
+ try_files $uri =404;
+ break;
+ }
+
+ location / {
+ return 301 https://$host$request_uri;
+ }
+}
+{{ end }}
+
+server {
+ server_name {{ $host }};
+ listen {{ $external_https_port }} ssl http2 {{ $default_server }};
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }};
+ {{ end }}
+ {{ $access_log }}
+
+ {{ if eq $network_tag "internal" }}
+ # Only allow traffic from internal clients
+ include /etc/nginx/network_internal.conf;
+ {{ end }}
+
+ {{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }}
+
+ 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) }};
+
+ {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }}
+ ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
+ {{ end }}
+
+ {{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }}
+ ssl_stapling on;
+ ssl_stapling_verify on;
+ ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }};
+ {{ end }}
+
+ {{ if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }}
+ add_header Strict-Transport-Security "{{ trim $hsts }}" always;
+ {{ 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 $upstream_name }};
+ {{ else if eq $proto "fastcgi" }}
+ root {{ trim $vhost_root }};
+ include fastcgi_params;
+ fastcgi_pass {{ trim $upstream_name }};
+ {{ else if eq $proto "grpc" }}
+ grpc_pass {{ trim $proto }}://{{ trim $upstream_name }};
+ {{ else }}
+ proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
+ {{ 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 {{ $external_http_port }} {{ $default_server }};
+ {{ if $enable_ipv6 }}
+ listen [::]:80 {{ $default_server }};
+ {{ end }}
+ {{ $access_log }}
+
+ {{ if eq $network_tag "internal" }}
+ # Only allow traffic from internal clients
+ include /etc/nginx/network_internal.conf;
+ {{ 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 $upstream_name }};
+ {{ else if eq $proto "fastcgi" }}
+ root {{ trim $vhost_root }};
+ include fastcgi_params;
+ fastcgi_pass {{ trim $upstream_name }};
+ {{ else if eq $proto "grpc" }}
+ grpc_pass {{ trim $proto }}://{{ trim $upstream_name }};
+ {{ else }}
+ proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
+ {{ 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 {{ $external_https_port }} ssl http2 {{ $default_server }};
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }};
+ {{ end }}
+ {{ $access_log }}
+ return 500;
+
+ ssl_certificate /etc/nginx/certs/default.crt;
+ ssl_certificate_key /etc/nginx/certs/default.key;
+}
+{{ end }}
+
+{{ end }}
+{{ end }}
diff --git a/files/nginx-letsencrypt-proxy/data/vhost.d/cloud.bvrn.de b/files/nginx-letsencrypt-proxy/data/vhost.d/cloud.bvrn.de
new file mode 100644
index 0000000..03f3982
--- /dev/null
+++ b/files/nginx-letsencrypt-proxy/data/vhost.d/cloud.bvrn.de
@@ -0,0 +1,12 @@
+## Start of configuration add by letsencrypt container
+location ^~ /.well-known/acme-challenge/ {
+ auth_basic off;
+ auth_request off;
+ allow all;
+ root /usr/share/nginx/html;
+ try_files $uri =404;
+ break;
+}
+## End of configuration add by letsencrypt container
+client_max_body_size 512M;
+proxy_set_header X-Forwarded-Proto $scheme;
diff --git a/files/nginx-letsencrypt-proxy/data/vhost.d/default b/files/nginx-letsencrypt-proxy/data/vhost.d/default
new file mode 100644
index 0000000..5e59aa4
--- /dev/null
+++ b/files/nginx-letsencrypt-proxy/data/vhost.d/default
@@ -0,0 +1,10 @@
+## Start of configuration add by letsencrypt container
+location ^~ /.well-known/acme-challenge/ {
+ auth_basic off;
+ auth_request off;
+ allow all;
+ root /usr/share/nginx/html;
+ try_files $uri =404;
+ break;
+}
+## End of configuration add by letsencrypt container
diff --git a/files/nginx-letsencrypt-proxy/docker-compose.yml b/files/nginx-letsencrypt-proxy/docker-compose.yml
new file mode 100644
index 0000000..2283db4
--- /dev/null
+++ b/files/nginx-letsencrypt-proxy/docker-compose.yml
@@ -0,0 +1,77 @@
+# see https://github.com/evertramos/nginx-proxy-automation &
+# https://github.com/nginx-proxy/nginx-proxy
+
+version: '3.5'
+
+services:
+ nginx-proxy-automation-web:
+ image: nginx:${NGINX_IMAGE_VERSION:-stable-alpine}
+ labels:
+ com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true"
+ container_name: ${NGINX_WEB_SEVICE_NAME:-nginx-proxy-automation-web}
+ restart: always
+ ports:
+ - "${IPv4:-0.0.0.0}:${DOCKER_HTTP_:-80}:80"
+ - "${IPv4:-0.0.0.0}:${DOCKER_HTTPS:-443}:443"
+ - "${IPv6:-::0}:${DOCKER_HTTP_:-80}:80"
+ - "${IPv6:-::0}:${DOCKER_HTTPS:-443}:443"
+ environment:
+ SSL_POLICY: ${SSL_POLICY:-Mozilla-Intermediate}
+# DEFAULT_HOST: ${DEFAULT_HOST}
+ ENABLE_IPV6: "true"
+ volumes:
+ - ${NGINX_FILES_PATH:-./data}/conf.d:/etc/nginx/conf.d
+ - ${NGINX_FILES_PATH:-./data}/vhost.d:/etc/nginx/vhost.d
+ - ${NGINX_FILES_PATH:-./data}/html:/usr/share/nginx/html
+ - ${NGINX_FILES_PATH:-./data}/certs:/etc/nginx/certs:ro
+ - ${NGINX_FILES_PATH:-./data}/htpasswd:/etc/nginx/htpasswd:ro
+ logging:
+ driver: ${NGINX_WEB_LOG_DRIVER:-json-file}
+ options:
+ max-size: ${NGINX_WEB_LOG_MAX_SIZE:-4m}
+ max-file: ${NGINX_WEB_LOG_MAX_FILE:-10}
+
+ nginx-proxy-automation-gen:
+ image: nginxproxy/docker-gen:${DOCKER_GEN_IMAGE_VERSION:-0.7.7}
+ command: -notify-sighup ${NGINX_WEB_SEVICE_NAME:-nginx-proxy-automation-web} -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
+ container_name: ${DOCKER_GEN_SEVICE_NAME:-nginx-proxy-automation-gen}
+ restart: always
+ volumes:
+ - ${NGINX_FILES_PATH:-./data}/conf.d:/etc/nginx/conf.d
+ - ${NGINX_FILES_PATH:-./data}/vhost.d:/etc/nginx/vhost.d
+ - ${NGINX_FILES_PATH:-./data}/html:/usr/share/nginx/html
+ - ${NGINX_FILES_PATH:-./data}/certs:/etc/nginx/certs:ro
+ - ${NGINX_FILES_PATH:-./data}/htpasswd:/etc/nginx/htpasswd:ro
+ - /var/run/docker.sock:/tmp/docker.sock:ro
+ - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro
+ logging:
+ driver: ${NGINX_GEN_LOG_DRIVER:-json-file}
+ options:
+ max-size: ${NGINX_GEN_LOG_MAX_SIZE:-2m}
+ max-file: ${NGINX_GEN_LOG_MAX_FILE:-10}
+
+ nginx-proxy-automation-letsencrypt:
+ image: nginxproxy/acme-companion:${NGINX_PROXY_COMPANION_IMAGE_VERSION:-2.1}
+ container_name: ${LETS_ENCRYPT_SEVICE_NAME:-nginx-proxy-automation-letsencrypt}
+ restart: always
+ volumes:
+ - ${NGINX_FILES_PATH:-./data}/conf.d:/etc/nginx/conf.d
+ - ${NGINX_FILES_PATH:-./data}/vhost.d:/etc/nginx/vhost.d
+ - ${NGINX_FILES_PATH:-./data}/html:/usr/share/nginx/html
+ - ${NGINX_FILES_PATH:-./data}/certs:/etc/nginx/certs:rw
+ - ${NGINX_FILES_PATH:-./data}/acme.sh:/etc/acme.sh
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ environment:
+ NGINX_DOCKER_GEN_CONTAINER: ${DOCKER_GEN_SEVICE_NAME:-nginx-proxy-automation-gen}
+ NGINX_PROXY_CONTAINER: ${NGINX_WEB_SEVICE_NAME:-nginx-proxy-automation-web}
+ DEFAULT_EMAIL: ${DEFAULT_EMAIL:-mail@yourdomain.tld}
+ logging:
+ driver: ${NGINX_LETSENCRYPT_LOG_DRIVER:-json-file}
+ options:
+ max-size: ${NGINX_LETSENCRYPT_LOG_MAX_SIZE:-2m}
+ max-file: ${NGINX_LETSENCRYPT_LOG_MAX_FILE:-10}
+
+networks:
+ default:
+ external: true
+ name: ${NETWORK:-proxy}
diff --git a/files/nginx-letsencrypt-proxy/nginx.tmpl b/files/nginx-letsencrypt-proxy/nginx.tmpl
new file mode 100644
index 0000000..b23dc6d
--- /dev/null
+++ b/files/nginx-letsencrypt-proxy/nginx.tmpl
@@ -0,0 +1,485 @@
+{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }}
+
+{{ $nginx_proxy_version := coalesce $.Env.NGINX_PROXY_VERSION "" }}
+{{ $external_http_port := coalesce $.Env.HTTP_PORT "80" }}
+{{ $external_https_port := coalesce $.Env.HTTPS_PORT "443" }}
+{{ $debug_all := $.Env.DEBUG }}
+{{ $sha1_upstream_name := parseBool (coalesce $.Env.SHA1_UPSTREAM_NAME "false") }}
+{{ $default_root_response := coalesce $.Env.DEFAULT_ROOT "404" }}
+
+{{ define "ssl_policy" }}
+ {{ if eq .ssl_policy "Mozilla-Modern" }}
+ ssl_protocols TLSv1.3;
+ {{/* nginx currently lacks ability to choose ciphers in TLS 1.3 in configuration, see https://trac.nginx.org/nginx/ticket/1529 /*}}
+ {{/* a possible workaround can be modify /etc/ssl/openssl.cnf to change it globally (see https://trac.nginx.org/nginx/ticket/1529#comment:12 ) /*}}
+ {{/* explicitly set ngnix default value in order to allow single servers to override the global http value */}}
+ ssl_ciphers HIGH:!aNULL:!MD5;
+ ssl_prefer_server_ciphers off;
+ {{ else if eq .ssl_policy "Mozilla-Intermediate" }}
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
+ ssl_prefer_server_ciphers off;
+ {{ else if eq .ssl_policy "Mozilla-Old" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-TLS-1-2-2017-01" }}
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-TLS-1-1-2017-01" }}
+ ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-2016-08" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-2015-05" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-2015-03" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ else if eq .ssl_policy "AWS-2015-02" }}
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
+ ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA';
+ ssl_prefer_server_ciphers on;
+ {{ end }}
+{{ end }}
+
+{{ define "location" }}
+location {{ .Path }} {
+ {{ if eq .NetworkTag "internal" }}
+ # Only allow traffic from internal clients
+ include /etc/nginx/network_internal.conf;
+ {{ end }}
+
+ {{ if eq .Proto "uwsgi" }}
+ include uwsgi_params;
+ uwsgi_pass {{ trim .Proto }}://{{ trim .Upstream }};
+ {{ else if eq .Proto "fastcgi" }}
+ root {{ trim .VhostRoot }};
+ include fastcgi_params;
+ fastcgi_pass {{ trim .Upstream }};
+ {{ else if eq .Proto "grpc" }}
+ grpc_pass {{ trim .Proto }}://{{ trim .Upstream }};
+ {{ else }}
+ proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}{{ trim .Dest }};
+ {{ 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_%s_location" .Host (sha1 .Path) )) }}
+ include {{ printf "/etc/nginx/vhost.d/%s_%s_location" .Host (sha1 .Path) }};
+ {{ else 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 }}
+
+{{ define "upstream" }}
+ {{ $networks := .Networks }}
+ {{ $debug_all := .Debug }}
+ upstream {{ .Upstream }} {
+ {{ $server_found := "false" }}
+ {{ range $container := .Containers }}
+ {{ $debug := (eq (coalesce $container.Env.DEBUG $debug_all "false") "true") }}
+ {{/* If only 1 port exposed, use that as a default, else 80 */}}
+ {{ $defaultPort := (when (eq (len $container.Addresses) 1) (first $container.Addresses) (dict "Port" "80")).Port }}
+ {{ $port := (coalesce $container.Env.VIRTUAL_PORT $defaultPort) }}
+ {{ $address := where $container.Addresses "Port" $port | first }}
+ {{ if $debug }}
+ # Exposed ports: {{ $container.Addresses }}
+ # Default virtual port: {{ $defaultPort }}
+ # VIRTUAL_PORT: {{ $container.Env.VIRTUAL_PORT }}
+ {{ if not $address }}
+ # /!\ Virtual port not exposed
+ {{ end }}
+ {{ end }}
+ {{ range $knownNetwork := $networks }}
+ {{ range $containerNetwork := $container.Networks }}
+ {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }}
+ ## Can be connected with "{{ $containerNetwork.Name }}" network
+ {{ 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 }}
+ {{ $server_found = "true" }}
+ # {{ $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 $containerNetwork }}
+ {{ $server_found = "true" }}
+ # {{ $container.Name }}
+ server {{ $containerNetwork.IP }}:{{ $address.Port }};
+ {{ end }}
+ {{ else if $containerNetwork }}
+ # {{ $container.Name }}
+ {{ if $containerNetwork.IP }}
+ {{ $server_found = "true" }}
+ server {{ $containerNetwork.IP }}:{{ $port }};
+ {{ else }}
+ # /!\ No IP for this network!
+ {{ end }}
+ {{ end }}
+ {{ else }}
+ # Cannot connect to network '{{ $containerNetwork.Name }}' of this container
+ {{ end }}
+ {{ end }}
+ {{ end }}
+ {{ end }}
+ {{/* nginx-proxy/nginx-proxy#1105 */}}
+ {{ if (eq $server_found "false") }}
+ # Fallback entry
+ server 127.0.0.1 down;
+ {{ end }}
+ }
+{{ end }}
+
+{{ if ne $nginx_proxy_version "" }}
+# nginx-proxy version : {{ $nginx_proxy_version }}
+{{ 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;
+}
+
+map $http_x_forwarded_host $proxy_x_forwarded_host {
+ default $http_x_forwarded_host;
+ '' $http_host;
+}
+
+# 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;
+}
+
+# Apply fix for very long server names
+server_names_hash_bucket_size 128;
+
+# Default dhparam
+{{ if (exists "/etc/nginx/dhparam/dhparam.pem") }}
+ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
+{{ end }}
+
+# Set appropriate X-Forwarded-Ssl header based on $proxy_x_forwarded_proto
+map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl {
+ default off;
+ https on;
+}
+
+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" '
+ '"$upstream_addr"';
+
+access_log off;
+
+{{/* Get the SSL_POLICY defined by this container, falling back to "Mozilla-Intermediate" */}}
+{{ $ssl_policy := or ($.Env.SSL_POLICY) "Mozilla-Intermediate" }}
+{{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }}
+error_log /dev/stderr;
+
+{{ if $.Env.RESOLVERS }}
+resolver {{ $.Env.RESOLVERS }};
+{{ end }}
+
+{{ 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-Host $proxy_x_forwarded_host;
+proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
+proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
+proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
+proxy_set_header X-Original-URI $request_uri;
+
+# Mitigate httpoxy attack (see README for details)
+proxy_set_header Proxy "";
+{{ end }}
+
+{{ $access_log := (or (and (not $.Env.DISABLE_ACCESS_LOGS) "access_log /var/log/nginx/access.log vhost;") "") }}
+
+{{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }}
+server {
+ server_name _; # This is just an invalid value which will never trigger on a real hostname.
+ server_tokens off;
+ listen {{ $external_http_port }};
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_http_port }};
+ {{ end }}
+ {{ $access_log }}
+ 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.
+ server_tokens off;
+ listen {{ $external_https_port }} ssl http2;
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_https_port }} ssl http2;
+ {{ end }}
+ {{ $access_log }}
+ return 503;
+
+ ssl_session_cache shared:SSL:50m;
+ 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" "," }}
+
+{{ $host := trim $host }}
+{{ $is_regexp := hasPrefix "~" $host }}
+{{ $upstream_name := when (or $is_regexp $sha1_upstream_name) (sha1 $host) $host }}
+
+{{ $paths := groupBy $containers "Env.VIRTUAL_PATH" }}
+{{ $nPaths := len $paths }}
+
+{{ if eq $nPaths 0 }}
+ # {{ $host }}
+ {{ template "upstream" (dict "Upstream" $upstream_name "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }}
+{{ else }}
+ {{ range $path, $containers := $paths }}
+ {{ $sum := sha1 $path }}
+ {{ $upstream := printf "%s-%s" $upstream_name $sum }}
+ # {{ $host }}{{ $path }}
+ {{ template "upstream" (dict "Upstream" $upstream "Containers" $containers "Networks" $CurrentContainer.Networks "Debug" $debug_all) }}
+ {{ end }}
+{{ end }}
+
+{{ $default_host := or ($.Env.DEFAULT_HOST) "" }}
+{{ $default_server := index (dict $host "" $default_host "default_server") $host }}
+
+{{/* Get the SERVER_TOKENS defined by containers w/ the same vhost, falling back to "" */}}
+{{ $server_tokens := trim (or (first (groupByKeys $containers "Env.SERVER_TOKENS")) "") }}
+
+
+{{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}}
+{{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) (or $.Env.HTTPS_METHOD "redirect") }}
+
+{{/* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to empty string (use default) */}}
+{{ $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "" }}
+
+{{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}}
+{{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) (or $.Env.HSTS "max-age=31536000") }}
+
+{{/* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}}
+{{ $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }}
+
+
+{{/* 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 $https_method "nohttps") (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 }};
+ {{ if $server_tokens }}
+ server_tokens {{ $server_tokens }};
+ {{ end }}
+ listen {{ $external_http_port }} {{ $default_server }};
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_http_port }} {{ $default_server }};
+ {{ end }}
+ {{ $access_log }}
+
+ # Do not HTTPS redirect Let'sEncrypt ACME challenge
+ location ^~ /.well-known/acme-challenge/ {
+ auth_basic off;
+ auth_request off;
+ allow all;
+ root /usr/share/nginx/html;
+ try_files $uri =404;
+ break;
+ }
+
+ location / {
+ {{ if eq $external_https_port "443" }}
+ return 301 https://$host$request_uri;
+ {{ else }}
+ return 301 https://$host:{{ $external_https_port }}$request_uri;
+ {{ end }}
+ }
+}
+{{ end }}
+
+server {
+ server_name {{ $host }};
+ {{ if $server_tokens }}
+ server_tokens {{ $server_tokens }};
+ {{ end }}
+ listen {{ $external_https_port }} ssl http2 {{ $default_server }};
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }};
+ {{ end }}
+ {{ $access_log }}
+
+ {{ template "ssl_policy" (dict "ssl_policy" $ssl_policy) }}
+
+ 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) }};
+
+ {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }}
+ ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }};
+ {{ end }}
+
+ {{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }}
+ ssl_stapling on;
+ ssl_stapling_verify on;
+ ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }};
+ {{ end }}
+
+ {{ if (not (or (eq $https_method "noredirect") (eq $hsts "off"))) }}
+ add_header Strict-Transport-Security "{{ trim $hsts }}" always;
+ {{ 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 }}
+
+ {{ if eq $nPaths 0 }}
+ {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}}
+ {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }}
+
+ {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
+ {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }}
+ {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }}
+ {{ else }}
+ {{ range $path, $container := $paths }}
+ {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}}
+ {{ $proto := trim (or (first (groupByKeys $container "Env.VIRTUAL_PROTO")) "http") }}
+
+ {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
+ {{ $network_tag := or (first (groupByKeys $container "Env.NETWORK_ACCESS")) "external" }}
+ {{ $sum := sha1 $path }}
+ {{ $upstream := printf "%s-%s" $upstream_name $sum }}
+ {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }}
+ {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }}
+ {{ end }}
+ {{ if (not (contains $paths "/")) }}
+ location / {
+ return {{ $default_root_response }};
+ }
+ {{ end }}
+ {{ end }}
+}
+
+{{ end }}
+
+{{ if or (not $is_https) (eq $https_method "noredirect") }}
+
+server {
+ server_name {{ $host }};
+ {{ if $server_tokens }}
+ server_tokens {{ $server_tokens }};
+ {{ end }}
+ listen {{ $external_http_port }} {{ $default_server }};
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_http_port }} {{ $default_server }};
+ {{ end }}
+ {{ $access_log }}
+
+ {{ 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 }}
+
+ {{ if eq $nPaths 0 }}
+ {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}}
+ {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }}
+
+ {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
+ {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }}
+ {{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }}
+ {{ else }}
+ {{ range $path, $container := $paths }}
+ {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}}
+ {{ $proto := trim (or (first (groupByKeys $container "Env.VIRTUAL_PROTO")) "http") }}
+
+ {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
+ {{ $network_tag := or (first (groupByKeys $container "Env.NETWORK_ACCESS")) "external" }}
+ {{ $sum := sha1 $path }}
+ {{ $upstream := printf "%s-%s" $upstream_name $sum }}
+ {{ $dest := (or (first (groupByKeys $container "Env.VIRTUAL_DEST")) "") }}
+ {{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }}
+ {{ end }}
+ {{ if (not (contains $paths "/")) }}
+ location / {
+ return {{ $default_root_response }};
+ }
+ {{ end }}
+ {{ end }}
+}
+
+{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
+server {
+ server_name {{ $host }};
+ {{ if $server_tokens }}
+ server_tokens {{ $server_tokens }};
+ {{ end }}
+ listen {{ $external_https_port }} ssl http2 {{ $default_server }};
+ {{ if $enable_ipv6 }}
+ listen [::]:{{ $external_https_port }} ssl http2 {{ $default_server }};
+ {{ end }}
+ {{ $access_log }}
+ return 500;
+
+ ssl_certificate /etc/nginx/certs/default.crt;
+ ssl_certificate_key /etc/nginx/certs/default.key;
+}
+{{ end }}
+
+{{ end }}
+{{ end }}
diff --git a/group_vars/all.yaml b/group_vars/all.yaml
new file mode 100644
index 0000000..24065c7
--- /dev/null
+++ b/group_vars/all.yaml
@@ -0,0 +1,14 @@
+---
+jicofo_auth_password: "{{ vault_jicofo_auth_password }}"
+jvb_auth_password: "{{ vault_jvb_auth_password }}"
+jigasi_xmpp_password: "{{ vault_jigasi_xmpp_password }}"
+jibri_recorder_password: "{{ vault_jibri_recorder_password }}"
+jibri_xmpp_password: "{{ vault_jibri_xmpp_password }}"
+limesurvey_db_password: "{{ vault_limesurvey_db_password }}"
+limesurvey_admin_password: "{{ vault_limesurvey_admin_password }}"
+nextcloud_postgres_password: "{{ vault_nextcloud_postgres_password }}"
+openldap_admin_password: "{{ vault_openldap_admin_password }}"
+keycloak_admin_password: "{{ vault_keycloak_admin_password }}"
+keycloak_management_password: "{{ vault_keycloak_management_password }}"
+keycloak_database_password: "{{ vault_keycloak_database_password }}"
+mattermost_db_password: "{{ vault_mattermost_db_password }}"
diff --git a/group_vars/dev/dev_vault b/group_vars/dev/dev_vault
new file mode 100644
index 0000000..6a87a36
--- /dev/null
+++ b/group_vars/dev/dev_vault
@@ -0,0 +1,54 @@
+$ANSIBLE_VAULT;1.2;AES256;dev
+37323431646431323564663435393661363866313131306632316462663362663436356332353030
+6465663539323161343234313864323530383730643030300a653239386665386465363365353362
+37333437373538336439663530326134626136376264616164353733373836326333346664333266
+6366636666663437360a343830663633666232366237336134323965343934313865363135613233
+63666162613436646533323937306439623331636333373666376339333431663136656166636236
+33646431656665663434336630626133333563313739313131306666643639343533626336666133
+64326365333431316634396438353662666237333733636461663465316237623263373466356334
+33306234363266323238346630393534306534383461306430633632356239333436376335656135
+34363136353330343630616531653464366164343263353164663934363266656431626339653361
+64356231633634633537663964646134373035613536376463623434643736326234633766323531
+36343932303966663961626462383862636137346261326139653835396130353163383235363831
+63343838613539323363653166373362643664646138383834393030393461313836363265373866
+34653965643563323934663736356164353863363037303334393238616266623630323064356438
+33383965373732636462643434373535316339323462323264313865336231393463663630363163
+34643731643662333732353061326235363662363739653536373330356135633236623966343937
+63613135636466363131363938633936333236393964336536366334626137363436303065616666
+30346662646438316633363565383063643461363261363731333330653066303363613831613130
+37396235313234616639356534303438316264626164313161343532323635326666646335383437
+31633762383431393037383334343962343338373263666330666233353363663664333438346331
+65343161316436613436613236623764363863393234323065373064393964336431623132383666
+61323764613935333934383431303439636461326139346535656335316534363132366135636461
+36613630633030346235363136386335626336333761306330653735626536383136653562333962
+62626431643665323864386337633030356238356661666165373436353234653364633361346235
+62343833643239386361643866616563383764343364366131626230356234316434363533333862
+39323631333562373037643638353235613836623766373665643033313436333964643961613830
+65616134646439396534353536646365336137333965636365396230346632653135313034306366
+37366236343838363635323164316262343930666130633032643033616366353335303538316136
+63353936663337373930613831386364623138313937333365633336363262316434656162343363
+38633037653661313461373337313036656163616334656238343134343435646463666638373731
+64373466326363633663393066353962383530303633333236643866613432616132326139373735
+36666339303766656636393262643861316533633662366163393833633662313066313162346366
+36633634636561353735306665666564366261633962393237626230633339323339633166323030
+36333661313737316564343734663666373431353962396161343664623061373263636566376430
+37633438646465666164303034323931636431653532343538646432386639373066653734326337
+37373863386336313133343862373330376431663431363364313534626662326139313930616165
+30303830633437366334383733326664646539366161613561663638396362326539333430306265
+34633832393730633839663434323038303731363664383866356331633037313764303131373530
+34353135383038653065626136393835373235653436303564383165303439346639346239376533
+39326436363365623937323035623131363533383762306664313933393263626562356338626466
+61336161383932323234616539376262303034353661643963363861383063336461653036396161
+30393561326535643964353965336537323939383934376461613737383637396161656631333931
+35663130323132623961386237356333356134346232306439363434613163653466353432376666
+35313931643639656635313161333061353838326463363435386663396632363365393064333333
+66626239643635306138643133643337306634316639313561343637373035323333396530646263
+65646166616266376163313233613834666631333534316534333033393635613630326666383533
+61643639613533376539313539336533393634343833326637343636313236346130363865663362
+35386134653438363063326434366263303163376237393761303663306664303066373663636162
+39616131663664613164656438373437383233633834303961336232663333323438636231643033
+66313837623933663365643238353463643662386537376538313232323564393862313433613631
+62353062396463323438383465366665303261303133646362373835633463393930333264343835
+31306463616232346466326335323138616130636462363432313930346162633365373663313061
+61333834393537393338343161373463373365666137363466623966386436623236336433616566
+61366266313664616237643939663632393965313438303836613164613239333933
\ No newline at end of file
diff --git a/group_vars/prod/prod_vault b/group_vars/prod/prod_vault
new file mode 100644
index 0000000..a4fe5df
--- /dev/null
+++ b/group_vars/prod/prod_vault
@@ -0,0 +1,56 @@
+$ANSIBLE_VAULT;1.2;AES256;prod
+31613537353864633838393437303639383430653561633562366134313837633262323437653330
+6661386236653430336134383831393032396561376439650a633865666265643232393765613231
+63626232323336323566356666396664336562356635316166613933396636363161656161383234
+3135326665653766610a616539613738393336653435626262653733356637363739303431323637
+31343337373065633135623965336230613330383066383462613037643466663966633337326532
+34303466643037343430363937346334623337366531636638313361616132393732353632376461
+34643866313662373561386364396337336331356630336665666638323639646563643539663162
+38633832353566646162363131353330646561636634613136316535623964336534323433326538
+63303166373130393961313238643264383938626461303336646137623862356163323232346331
+62313037636332316239393566383337376639373631333835643037323361626138313438326334
+35313036373431363566346433316662626433386435643834653536333535313738313535306137
+34616136633564336438303431396462643437366161666538663637663366333139633664373137
+30393661333432616330613335636635346534633064663162373361323236393863356566663538
+31313838343863626438653338633038383133643333303333333062316462383738646335386161
+62373230336332323339326666636331393765643032333566333536373939623934326565383230
+63333830373466666138393463643633393966396639363261313235663338313233326161326430
+35343563333466393765343435643430353162653535333935343561343437373135613835343432
+61313562383761373762373932366634343938373033616466646634663435313062313339656664
+34613032383530643463633538306339333734363032306637326161626465333234326633613264
+32383332323035333365643763386434386462363630633165636433663934353332386331616330
+66313736303536623663656530623266316539346239313037626463336364623437393436656432
+37366636396539616362663731396564336630383764333037646561383135386334336338396134
+37336335636366373564383262383162643463613532303536633234306633613436386562616164
+37323531396362613166386161323131356466336539653162366230636433393538366434313133
+61656236356363656130653430383264393030313661303438303564356662663538316533376661
+36643537363431363539616231333964316632616265373065363439323732616234633761306239
+33303336613561353333373866353830353237613336643735346532396435646538313732373536
+65643735623938343465363333346532626438613831336433356364616361643436653534666237
+32666337303234323432356235396665613333393666396133346366343464303635633733666162
+66343134303438643236366664623964343337373137303332303662613835383939306533386363
+61373734616261343235386633393736336538653538313334303264643561353263353436333261
+63646665633930393534363932303938623637393464643661393538643433353234306564373861
+31343063373639613737363538626262653365356261306535386639646630303133326166326536
+34636230363836666634633734323234643037356363386364326466366331313266646334386364
+35393664336265653166623637373436663738623434376461646263336264383536303933323233
+32383064393762346262313661643865366566643362623631313634303939323934633334323530
+35303064653039623432376430356464373163666433393631343464316335363537666461333264
+32343863383039626563646337343439376331303937386535396432313938666130653065366433
+65306261646130633465633539353365356163613733383665336139663561636565343333303536
+63363034313934636539393732633863353066663664623439316537393264356363313132643662
+64633462333638343664356337613565333531616330343930663934373062653163316235343532
+64653661313837323732306561666438386236396164666237663065613766313533626337363061
+61623862363365626463323839306331636463636533396664363935353263356661633935373366
+38313333396134653936393335623333663231346638343733343331343863646664303430383862
+64643763303338363536663630313036663861376662623765663265323733353337393730626262
+61393361383963373431646438613631623031316264636562383565633431646135663665343531
+32343932393739646234663033613036656264633264393761396661613232656639346432323163
+38313239623361613439343064393633336363663333313237313161376234353666616236396135
+38373464383361366463636333373766326339306265396265656139343236366238393237616363
+66386363326530633137316261636236333934666531393733616261386330653539613364383064
+31653962383039613531356135643034636431666632336338393066613862323536643130626532
+33333032316634366263353864373361616339633232336332356333333135633466326232393262
+39323162363163353036653938396631393430363365633138313630633739396438323761393137
+63656330306637373535643165383635393663313238363333363661613435353636653239336163
+383063383432646566366264623862636465
\ No newline at end of file
diff --git a/host_vars/server01.bvrn.de.yml b/host_vars/server01.bvrn.de.yml
new file mode 100644
index 0000000..34fc74f
--- /dev/null
+++ b/host_vars/server01.bvrn.de.yml
@@ -0,0 +1,3 @@
+---
+
+ansible_host: "81.169.136.237"
diff --git a/inventory b/inventory
new file mode 100644
index 0000000..5dddea3
--- /dev/null
+++ b/inventory
@@ -0,0 +1,2 @@
+[prod]
+server01.bvrn.de
diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml
new file mode 100644
index 0000000..529ab9d
--- /dev/null
+++ b/molecule/default/converge.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: all
+
+- import_playbook: ../../playbook.yml
diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml
new file mode 100644
index 0000000..88eeae1
--- /dev/null
+++ b/molecule/default/molecule.yml
@@ -0,0 +1,25 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+lint: |
+ set -e
+ yamllint .
+ ansible-lint
+platforms:
+ - name: instance
+ image: geerlingguy/docker-${MOLECULE_DISTRO:-debian10}-ansible:latest
+ command: ""
+ volumes:
+ - /sys/fs/cgroup:/sys/fs/cgroup:ro
+ privileged: true
+ pre_build_image: true
+ groups:
+ - dev
+provisioner:
+ name: ansible
+ options:
+ vault-id: .vault-pass.dev
+verifier:
+ name: ansible
diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml
new file mode 100644
index 0000000..79044cd
--- /dev/null
+++ b/molecule/default/verify.yml
@@ -0,0 +1,10 @@
+---
+# This is an example playbook to execute Ansible tests.
+
+- name: Verify
+ hosts: all
+ gather_facts: false
+ tasks:
+ - name: Example assertion
+ assert:
+ that: true
diff --git a/playbook.yml b/playbook.yml
new file mode 100644
index 0000000..049702f
--- /dev/null
+++ b/playbook.yml
@@ -0,0 +1,243 @@
+---
+
+- hosts: "all"
+ become: true
+
+ vars:
+ docker_install_compose: true
+ pip_install_packages:
+ - name:
+ - "docker"
+ - "docker-compose"
+
+ roles:
+ - "kwoodson.yedit"
+ - name: "Set up basic stuff"
+ role: "basic"
+
+ - name: "Install Nginx Proxy"
+ role: "docker_compose_app"
+ docker_compose_app_directories_create:
+ - "/opt/docker-compose/nginx-letsencrypt-proxy"
+ - "/opt/docker-compose/nginx-letsencrypt-proxy/data/acme.sh"
+ - "/opt/docker-compose/nginx-letsencrypt-proxy/data/certs"
+ - "/opt/docker-compose/nginx-letsencrypt-proxy/data/conf.d"
+ - "/opt/docker-compose/nginx-letsencrypt-proxy/data/html"
+ - "/opt/docker-compose/nginx-letsencrypt-proxy/data/htpasswd"
+ - "/opt/docker-compose/nginx-letsencrypt-proxy/data/vhost.d"
+ docker_compose_app_appname: "nginx-letsencrypt-proxy"
+ tags:
+ - nginx
+
+ - name: "Install Openldap"
+ role: "docker_compose_app"
+ docker_compose_app_directories_create:
+ - "/opt/docker-compose/openldap"
+ docker_compose_app_appname: "openldap"
+ tags:
+ - openldap
+
+ - name: "Install Keycloak"
+ role: "docker_compose_app"
+ docker_compose_app_directories_create:
+ - "/opt/docker-compose/keycloak"
+ docker_compose_app_appname: "keycloak"
+ tags:
+ - keycloak
+
+ - name: "Install Jitsi"
+ role: "docker_compose_app"
+ docker_compose_app_directories_create:
+ - "/opt/docker-compose/jitsi-meet"
+ - "/etc/jitsi/jibri"
+ - "/etc/jitsi/jicofo"
+ - "/etc/jitsi/jigasi"
+ - "/etc/jitsi/jvb"
+ - "/etc/jitsi/prosody/config"
+ - "/etc/jitsi/prosody/prosody-plugins-custom"
+ - "/etc/jitsi/transcripts"
+ - "/etc/jitsi/web/letsencrypt"
+ docker_compose_files:
+ - "docker-compose.yml"
+ - "etherpad.yml"
+ docker_compose_app_appname: "jitsi-meet"
+ docker_compose_app_github_repo: "jitsi/docker-jitsi-meet"
+ tags:
+ - jitsi
+
+# - role: "docker_compose_app"
+# docker_compose_app_repo_url: "https://github.com/stefanprodan/dockprom.git"
+# docker_compose_app_appname: "dockprom"
+# docker_compose_copy_env_file: false
+## docker_compose_deploy_default: false
+
+ - name: "Install Limesurvey"
+ role: "docker_compose_app"
+ docker_compose_app_directories_create:
+ - "/opt/docker-compose/limesurvey"
+ docker_compose_app_appname: "limesurvey"
+ tags:
+ - limesurvey
+
+ - name: "Install Nextcloud"
+ role: "docker_compose_app"
+ docker_compose_app_directories_create:
+ - "/opt/docker-compose/nextcloud"
+ docker_compose_app_appname: "nextcloud"
+ tags:
+ - nextcloud
+
+ - name: "Install Mattermost"
+ role: "docker_compose_app"
+ docker_compose_app_directories_create:
+ - "/opt/docker-compose/mattermost"
+ - "/opt/docker-compose/mattermost/volumes/app/mattermost/config"
+ - "/opt/docker-compose/mattermost/volumes/app/mattermost/data"
+ - "/opt/docker-compose/mattermost/volumes/app/mattermost/logs"
+ - "/opt/docker-compose/mattermost/volumes/app/mattermost/plugins"
+ - "/opt/docker-compose/mattermost/volumes/app/mattermost/client/plugins"
+ - "/opt/docker-compose/mattermost/volumes/app/mattermost/bleve-indexes"
+ docker_compose_app_appname: "mattermost"
+ tags:
+ - mattermost
+
+ tasks:
+# - name: "Modify dockprom docker-compose.yml file"
+# yedit:
+# src: "/opt/docker-compose/dockprom/docker-compose.yml"
+# key: "{{ item.key }}"
+# value: "{{ item.value }}"
+# state: "{{ item.state }}"
+# loop:
+# - key: "services.grafana.environment"
+# state: "absent"
+# value: ~
+# - key: "services.grafana.ports"
+# state: "present"
+# value: |
+# - "3000:3000"
+#
+# - name: "Deploy dockprom Docker Compose"
+# # Environment variable 'CI' is always set to true on GitHub Action runners.
+# # Some compose examples may require specific things which are not fulfilled on shared runners, but are
+# # fulfilled on our dedicated infrastructure, e.g. access to /dev/shm or /dev/snd.
+# when:
+# - "not ({{ lookup('env', 'CI') | default(false) }})"
+# docker_compose:
+# project_src: "/opt/docker-compose/dockprom"
+# files:
+# - "docker-compose.yml"
+# services:
+# - "prometheus"
+# - "alertmanager"
+# - "nodeexporter"
+# - "grafana"
+# state: "present"
+#
+# - name: "Copy nginx.tmpl"
+# ansible.builtin.copy:
+# src: "templates/nginx-letsencrypt-proxy/nginx.tmpl"
+# dest: "/opt/docker-compose/nginx-letsencrypt-proxy/nginx.tmpl"
+#
+# - name: "Copy conf.d files"
+# ansible.builtin.copy:
+# src: "templates/{{ item }}"
+# dest: "/opt/docker-compose/nginx-letsencrypt-proxy/data/{{ item }}"
+# with_fileglob:
+# - "conf.d/*"
+
+ - name: "Create proxy network"
+ community.docker.docker_network:
+ name: "proxy"
+
+ - name: "Deploy nginx-letsencrypt-proxy Docker Compose"
+ when:
+ - "not ({{ lookup('env', 'CI') | default(false) }})"
+ docker_compose:
+ project_src: "/opt/docker-compose/nginx-letsencrypt-proxy"
+ files:
+ - "docker-compose.yml"
+ state: "present"
+ tags:
+ - nginx
+
+ - name: "Deploy jitsi-meet Docker Compose"
+ when:
+ - "not ({{ lookup('env', 'CI') | default(false) }})"
+ docker_compose:
+ project_src: "/opt/docker-compose/jitsi-meet"
+ files:
+ - "docker-compose.yml"
+ - "etherpad.yml"
+ state: "present"
+ tags:
+ - jitsi
+
+ - name: "Deploy limesurvey Docker Compose"
+ when:
+ - "not ({{ lookup('env', 'CI') | default(false) }})"
+ docker_compose:
+ project_src: "/opt/docker-compose/limesurvey"
+ files:
+ - "docker-compose.yml"
+ state: "present"
+ tags:
+ - limesurvey
+
+ - name: "Create ldap network"
+ community.docker.docker_network:
+ name: "ldap"
+
+ - name: "Deploy openldap Docker Compose"
+ when:
+ - "not ({{ lookup('env', 'CI') | default(false) }})"
+ docker_compose:
+ project_src: "/opt/docker-compose/openldap"
+ files:
+ - "docker-compose.yml"
+ state: "present"
+ tags:
+ - openldap
+
+ - name: "Deploy keycloak Docker Compose"
+ when:
+ - "not ({{ lookup('env', 'CI') | default(false) }})"
+ docker_compose:
+ project_src: "/opt/docker-compose/keycloak"
+ files:
+ - "docker-compose.yml"
+ state: "present"
+ tags:
+ - keycloak
+
+ - name: "Deploy nextcloud Docker Compose"
+ when:
+ - "not ({{ lookup('env', 'CI') | default(false) }})"
+ docker_compose:
+ project_src: "/opt/docker-compose/nextcloud"
+ files:
+ - "docker-compose.yml"
+ state: "present"
+ tags:
+ - nexcloud
+
+ - name: "Chown mattermost volume dirs"
+ ansible.builtin.file:
+ path: /opt/docker-compose/mattermost/volumes/app/mattermost
+ state: directory
+ recurse: yes
+ owner: 2000
+ group: 2000
+ tags:
+ - mattermost
+
+ - name: "Deploy mattermost Docker Compose"
+ when:
+ - "not ({{ lookup('env', 'CI') | default(false) }})"
+ docker_compose:
+ project_src: "/opt/docker-compose/mattermost"
+ files:
+ - "docker-compose.yml"
+ state: "present"
+ tags:
+ - mattermost
diff --git a/requirements.in b/requirements.in
new file mode 100644
index 0000000..fa64eba
--- /dev/null
+++ b/requirements.in
@@ -0,0 +1,6 @@
+ansible
+molecule
+molecule-docker
+docker
+yamllint
+ansible-lint
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..4d98d21
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,157 @@
+#
+# This file is autogenerated by pip-compile with python 3.9
+# To update, run:
+#
+# pip-compile
+#
+ansible==4.5.0
+ # via -r requirements.in
+ansible-compat==0.5.0
+ # via
+ # molecule
+ # molecule-docker
+ansible-core==2.11.5
+ # via ansible
+ansible-lint==5.1.3
+ # via -r requirements.in
+arrow==1.1.1
+ # via jinja2-time
+bcrypt==3.2.0
+ # via paramiko
+binaryornot==0.4.4
+ # via cookiecutter
+bracex==2.1.1
+ # via wcmatch
+cerberus==1.3.2
+ # via molecule
+certifi==2021.5.30
+ # via requests
+cffi==1.14.6
+ # via
+ # bcrypt
+ # cryptography
+ # pynacl
+chardet==4.0.0
+ # via binaryornot
+charset-normalizer==2.0.6
+ # via requests
+click==8.0.1
+ # via
+ # click-help-colors
+ # cookiecutter
+ # molecule
+click-help-colors==0.9.1
+ # via molecule
+colorama==0.4.4
+ # via rich
+commonmark==0.9.1
+ # via rich
+cookiecutter==1.7.3
+ # via molecule
+cryptography==3.4.8
+ # via
+ # ansible-core
+ # paramiko
+distro==1.6.0
+ # via selinux
+docker==5.0.2
+ # via
+ # -r requirements.in
+ # molecule-docker
+enrich==1.2.6
+ # via
+ # ansible-lint
+ # molecule
+idna==3.2
+ # via requests
+jinja2==3.0.1
+ # via
+ # ansible-core
+ # cookiecutter
+ # jinja2-time
+ # molecule
+jinja2-time==0.2.0
+ # via cookiecutter
+markupsafe==2.0.1
+ # via jinja2
+molecule==3.5.1
+ # via
+ # -r requirements.in
+ # molecule-docker
+molecule-docker==1.0.2
+ # via -r requirements.in
+packaging==21.0
+ # via
+ # ansible-core
+ # ansible-lint
+ # molecule
+paramiko==2.7.2
+ # via molecule
+pathspec==0.9.0
+ # via yamllint
+pluggy==1.0.0
+ # via molecule
+poyo==0.5.0
+ # via cookiecutter
+pycparser==2.20
+ # via cffi
+pygments==2.10.0
+ # via rich
+pynacl==1.4.0
+ # via paramiko
+pyparsing==2.4.7
+ # via packaging
+python-dateutil==2.8.2
+ # via arrow
+python-slugify==5.0.2
+ # via cookiecutter
+pyyaml==5.4.1
+ # via
+ # ansible-compat
+ # ansible-core
+ # ansible-lint
+ # molecule
+ # yamllint
+requests==2.26.0
+ # via
+ # cookiecutter
+ # docker
+ # molecule-docker
+resolvelib==0.5.4
+ # via ansible-core
+rich==10.10.0
+ # via
+ # ansible-lint
+ # enrich
+ # molecule
+ruamel.yaml==0.17.16
+ # via ansible-lint
+ruamel.yaml.clib==0.2.6
+ # via ruamel.yaml
+selinux==0.2.1
+ # via
+ # molecule
+ # molecule-docker
+six==1.16.0
+ # via
+ # bcrypt
+ # cookiecutter
+ # pynacl
+ # python-dateutil
+subprocess-tee==0.3.4
+ # via molecule
+tenacity==8.0.1
+ # via ansible-lint
+text-unidecode==1.3
+ # via python-slugify
+urllib3==1.26.6
+ # via requests
+wcmatch==8.2
+ # via ansible-lint
+websocket-client==1.2.1
+ # via docker
+yamllint==1.26.3
+ # via -r requirements.in
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools
diff --git a/requirements.yml b/requirements.yml
new file mode 100644
index 0000000..63d45f1
--- /dev/null
+++ b/requirements.yml
@@ -0,0 +1,5 @@
+---
+roles:
+ - name: geerlingguy.pip
+ - name: geerlingguy.docker
+ - name: kwoodson.yedit
diff --git a/roles/basic/meta/main.yml b/roles/basic/meta/main.yml
new file mode 100644
index 0000000..3a3cbd9
--- /dev/null
+++ b/roles/basic/meta/main.yml
@@ -0,0 +1,17 @@
+---
+
+dependencies: []
+galaxy_info:
+ role_name: "basic"
+ author: "Jan Otto"
+ description: "Basic setup"
+ license: "MIT"
+ min_ansible_version: 2.4
+ platforms:
+ - name: "Ubuntu"
+ versions:
+ - "all"
+ - name: "Debian"
+ versions:
+ - "all"
+ galaxy_tags: []
diff --git a/roles/basic/tasks/main.yml b/roles/basic/tasks/main.yml
new file mode 100644
index 0000000..9656ebe
--- /dev/null
+++ b/roles/basic/tasks/main.yml
@@ -0,0 +1,47 @@
+---
+
+- name: "Set timezone to Europe/Berlin"
+ timezone:
+ name: "Europe/Berlin"
+
+- name: "Update apt cache"
+ apt:
+ update_cache: true
+ cache_valid_time: 3600
+ when:
+ - ansible_os_family == 'Debian'
+
+- name: "Install packages"
+ package:
+ name:
+ - "neovim"
+ - "tmux"
+ - "ufw"
+ state: "present"
+
+- name: "Allow OpenSSH port"
+ community.general.ufw:
+ state: "enabled"
+ rule: allow
+ name: OpenSSH
+
+- name: "Allow some some TCP ports"
+ community.general.ufw:
+ state: "enabled"
+ rule: allow
+ port: "{{ item }}"
+ proto: "tcp"
+ loop:
+ - '80'
+ - '443'
+
+- name: "Deny everything and enable UFW"
+ community.general.ufw:
+ state: "enabled"
+ policy: "deny"
+
+- name: "Enable service ufw"
+ ansible.builtin.service:
+ name: "ufw"
+ enabled: yes
+ state: "started"
diff --git a/roles/docker_compose_app/defaults/main.yml b/roles/docker_compose_app/defaults/main.yml
new file mode 100644
index 0000000..82ed89f
--- /dev/null
+++ b/roles/docker_compose_app/defaults/main.yml
@@ -0,0 +1,18 @@
+---
+docker_compose_app_directory: "/opt/docker-compose"
+docker_compose_app_directories_create: []
+docker_compose_app_appname: "dockercomposeapp"
+docker_compose_files:
+ - "docker-compose.yml"
+docker_compose_copy_files: true
+
+# app could be cloned via git, downloaded as release or copied as template
+# docker_compose_app_repo_url and docker_compose_app_github_repo are mutually exclusive
+
+# git clone config
+docker_compose_app_repo_url: None
+
+# release download config
+docker_compose_app_github_repo: None
+
+docker_compose_app_version_ref: "HEAD"
diff --git a/roles/docker_compose_app/meta/main.yml b/roles/docker_compose_app/meta/main.yml
new file mode 100644
index 0000000..d3ff518
--- /dev/null
+++ b/roles/docker_compose_app/meta/main.yml
@@ -0,0 +1,19 @@
+---
+
+dependencies:
+ - role: "geerlingguy.pip"
+ - role: "geerlingguy.docker"
+galaxy_info:
+ role_name: "docker_compose_app"
+ author: "Jan Otto"
+ description: "Docker compose app"
+ license: "license (MIT)"
+ min_ansible_version: 2.4
+ platforms:
+ - name: "Ubuntu"
+ versions:
+ - "all"
+ - name: "Debian"
+ versions:
+ - "all"
+ galaxy_tags: []
diff --git a/roles/docker_compose_app/tasks/git_clone.yml b/roles/docker_compose_app/tasks/git_clone.yml
new file mode 100644
index 0000000..e6dc7a8
--- /dev/null
+++ b/roles/docker_compose_app/tasks/git_clone.yml
@@ -0,0 +1,8 @@
+---
+
+- name: "Clone Git repository"
+ ansible.builtin.git:
+ repo: "{{ docker_compose_app_repo_url }}"
+ dest: "{{ docker_compose_app_directory }}/{{ docker_compose_app_appname }}"
+ version: "{{ docker_compose_app_version_ref }}"
+ force: true
diff --git a/roles/docker_compose_app/tasks/git_download.yml b/roles/docker_compose_app/tasks/git_download.yml
new file mode 100644
index 0000000..04694d9
--- /dev/null
+++ b/roles/docker_compose_app/tasks/git_download.yml
@@ -0,0 +1,28 @@
+---
+
+- name: "Fetch docker app release string from GitHub"
+ ansible.builtin.uri:
+ url: "https://api.github.com/repos/{{ docker_compose_app_github_repo }}/releases/latest"
+ return_content: true
+ register: "docker_compose_app__release"
+
+- name: "Create temporary download directory"
+ changed_when: False
+ ansible.builtin.tempfile:
+ state: "directory"
+ suffix: "{{ docker_compose_app_github_repo | replace('/', '-') }}"
+ register: "docker_compose_app_temp"
+
+- name: Download docker app release from GitHub.
+ changed_when: False
+ ansible.builtin.get_url:
+ url: "{{ docker_compose_app__release.json.tarball_url }}"
+ dest: "{{ docker_compose_app_temp.path }}/docker-compose-app_release.tar.gz"
+
+- name: Extract docker app release.
+ ansible.builtin.unarchive:
+ src: "{{ docker_compose_app_temp.path }}/docker-compose-app_release.tar.gz"
+ dest: "{{ docker_compose_app_directory }}/{{ docker_compose_app_appname }}"
+ remote_src: true
+ extra_opts:
+ - "--strip-components=1"
diff --git a/roles/docker_compose_app/tasks/main.yml b/roles/docker_compose_app/tasks/main.yml
new file mode 100644
index 0000000..7a763ed
--- /dev/null
+++ b/roles/docker_compose_app/tasks/main.yml
@@ -0,0 +1,70 @@
+---
+
+- name: "Remove directory if recreate tag is specified"
+ ansible.builtin.file:
+ path: "{{ docker_compose_app_directory }}/{{ docker_compose_app_appname }}"
+ state: absent
+ tags:
+ - "never"
+ - "recreate"
+
+- name: "Create directories"
+ ansible.builtin.file:
+ path: "{{ item }}"
+ state: "directory"
+ mode: 0o755
+ loop: "{{ docker_compose_app_directories_create | union([docker_compose_app_directory]) | unique }}"
+
+- name: "Check the stat of the app directory"
+ ansible.builtin.stat:
+ path: "{{ docker_compose_app_directory }}/{{ docker_compose_app_appname }}"
+ register: repo_directory
+
+###
+# Docker App
+###
+- name: "Clone App from GitHub"
+ vars:
+ git_repo_uri_regex: "^(?:https://)[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#\\[\\]@!\\$&'\\(\\)\\*\\+,;=.]+(?:\\.git)$"
+ when:
+ - not repo_directory.stat.exists
+ - docker_compose_app_repo_url | regex_search(git_repo_uri_regex)
+ ansible.builtin.include_tasks: "git_clone.yml"
+
+- name: "Download App from GitHub release"
+ vars:
+ git_repo_name_regex: '^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$'
+ when:
+ - docker_compose_app_github_repo | regex_search(git_repo_name_regex)
+ ansible.builtin.include_tasks: "git_download.yml"
+
+###
+# App
+###
+- name: "Copy files"
+ when:
+ - docker_compose_copy_files
+ ansible.builtin.copy:
+ src: "files/{{ docker_compose_app_appname }}/"
+ dest: "{{ docker_compose_app_directory }}/{{ docker_compose_app_appname }}/"
+
+- name: "Copy Docker Compose .env file"
+ when:
+ - docker_compose_copy_env_file | default(true)
+ ansible.builtin.template:
+ src: "templates/{{ docker_compose_app_appname }}/.env.j2"
+ dest: "{{ docker_compose_app_directory }}/{{ docker_compose_app_appname }}/.env"
+ mode: 0o400
+
+#- name: "Deploy Docker Compose"
+# # Environment variable 'CI' is always set to true on GitHub Action runners.
+# # Some compose examples may require specific things which are not fulfilled on shared runners, but are
+# # fulfilled on our dedicated infrastructure, e.g. access to /dev/shm or /dev/snd.
+# when:
+# - docker_compose_deploy_default | default(true)
+# - "not ({{ lookup('env', 'CI') | default(false) }})"
+# community.docker.docker_compose:
+# project_src: "{{ docker_compose_app_directory }}/{{ docker_compose_app_appname }}"
+# files:
+# - "docker-compose.yml"
+# state: "present"
diff --git a/templates/jitsi-meet/.env.j2 b/templates/jitsi-meet/.env.j2
new file mode 100644
index 0000000..56cf662
--- /dev/null
+++ b/templates/jitsi-meet/.env.j2
@@ -0,0 +1,391 @@
+# shellcheck disable=SC2034
+
+# Security
+#
+# Set these to strong passwords to avoid intruders from impersonating a service account
+# The service(s) won't start unless these are specified
+# Running ./gen-passwords.sh will update .env with strong passwords
+# You may skip the Jigasi and Jibri passwords if you are not using those
+# DO NOT reuse passwords
+
+
+# XMPP password for Jicofo client connections
+JICOFO_AUTH_PASSWORD={{ jicofo_auth_password }}
+
+# XMPP password for JVB client connections
+JVB_AUTH_PASSWORD={{ jvb_auth_password }}
+
+# XMPP password for Jigasi MUC client connections
+JIGASI_XMPP_PASSWORD={{ jigasi_xmpp_password }}
+
+# XMPP recorder password for Jibri client connections
+JIBRI_RECORDER_PASSWORD={{ jibri_recorder_password }}
+
+# XMPP password for Jibri client connections
+JIBRI_XMPP_PASSWORD={{ jibri_xmpp_password }}
+
+
+#
+# Basic configuration options
+#
+
+# Directory where all configuration will be stored
+CONFIG=/etc/jitsi
+
+# Exposed HTTP port
+HTTP_PORT=8000
+
+# Exposed HTTPS port
+HTTPS_PORT=8443
+
+# System time zone
+TZ=UTC
+
+# Public URL for the web service (required)
+PUBLIC_URL=https://meet.bvrn.de
+
+# IP address of the Docker host
+# See the "Running behind NAT or on a LAN environment" section in the Handbook:
+# https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker#running-behind-nat-or-on-a-lan-environment
+#DOCKER_HOST_ADDRESS=192.168.1.1
+
+# Control whether the lobby feature should be enabled or not
+#ENABLE_LOBBY=1
+
+# Show a prejoin page before entering a conference
+#ENABLE_PREJOIN_PAGE=0
+
+# Enable the welcome page
+#ENABLE_WELCOME_PAGE=1
+
+# Enable the close page
+#ENABLE_CLOSE_PAGE=0
+
+# Disable measuring of audio levels
+#DISABLE_AUDIO_LEVELS=0
+
+# Enable noisy mic detection
+#ENABLE_NOISY_MIC_DETECTION=1
+
+#
+# Let's Encrypt configuration
+#
+
+# Enable Let's Encrypt certificate generation
+ENABLE_LETSENCRYPT=0
+
+# Domain for which to generate the certificate
+#LETSENCRYPT_DOMAIN=meet.example.com
+
+# E-Mail for receiving important account notifications (mandatory)
+#LETSENCRYPT_EMAIL=alice@atlanta.net
+
+# Use the staging server (for avoiding rate limits while testing)
+#LETSENCRYPT_USE_STAGING=1
+
+
+#
+# Etherpad integration (for document sharing)
+#
+
+# Set etherpad-lite URL in docker local network (uncomment to enable)
+ETHERPAD_URL_BASE=http://etherpad.meet.jitsi:9001
+
+# Set etherpad-lite public URL (uncomment to enable)
+#ETHERPAD_PUBLIC_URL=https://etherpad.my.domain
+
+# Name your etherpad instance!
+ETHERPAD_TITLE=Video Chat
+
+# The default text of a pad
+ETHERPAD_DEFAULT_PAD_TEXT=Welcome to Web Chat!
+
+# Name of the skin for etherpad
+ETHERPAD_SKIN_NAME=colibris
+
+# Skin variants for etherpad
+ETHERPAD_SKIN_VARIANTS=super-dark-toolbar super-dark-editor dark-background full-width-editor
+
+# Supress errors in pad text
+ETHERPAD_SUPPRESS_ERRORS_IN_PAD_TEXT=true
+
+
+#
+# Basic Jigasi configuration options (needed for SIP gateway support)
+#
+
+# SIP URI for incoming / outgoing calls
+#JIGASI_SIP_URI=test@sip2sip.info
+
+# Password for the specified SIP account as a clear text
+#JIGASI_SIP_PASSWORD=passw0rd
+
+# SIP server (use the SIP account domain if in doubt)
+#JIGASI_SIP_SERVER=sip2sip.info
+
+# SIP server port
+#JIGASI_SIP_PORT=5060
+
+# SIP server transport
+#JIGASI_SIP_TRANSPORT=UDP
+
+#
+# Authentication configuration (see handbook for details)
+#
+
+# Enable authentication
+#ENABLE_AUTH=1
+
+# Enable guest access
+#ENABLE_GUESTS=1
+
+# Select authentication type: internal, jwt or ldap
+#AUTH_TYPE=internal
+
+# JWT authentication
+#
+
+# Application identifier
+#JWT_APP_ID=my_jitsi_app_id
+
+# Application secret known only to your token generator
+#JWT_APP_SECRET=my_jitsi_app_secret
+
+# (Optional) Set asap_accepted_issuers as a comma separated list
+#JWT_ACCEPTED_ISSUERS=my_web_client,my_app_client
+
+# (Optional) Set asap_accepted_audiences as a comma separated list
+#JWT_ACCEPTED_AUDIENCES=my_server1,my_server2
+
+
+# LDAP authentication (for more information see the Cyrus SASL saslauthd.conf man page)
+#
+
+# LDAP url for connection
+#LDAP_URL=ldaps://ldap.domain.com/
+
+# LDAP base DN. Can be empty
+#LDAP_BASE=DC=example,DC=domain,DC=com
+
+# LDAP user DN. Do not specify this parameter for the anonymous bind
+#LDAP_BINDDN=CN=binduser,OU=users,DC=example,DC=domain,DC=com
+
+# LDAP user password. Do not specify this parameter for the anonymous bind
+#LDAP_BINDPW=LdapUserPassw0rd
+
+# LDAP filter. Tokens example:
+# %1-9 - if the input key is user@mail.domain.com, then %1 is com, %2 is domain and %3 is mail
+# %s - %s is replaced by the complete service string
+# %r - %r is replaced by the complete realm string
+#LDAP_FILTER=(sAMAccountName=%u)
+
+# LDAP authentication method
+#LDAP_AUTH_METHOD=bind
+
+# LDAP version
+#LDAP_VERSION=3
+
+# LDAP TLS using
+#LDAP_USE_TLS=1
+
+# List of SSL/TLS ciphers to allow
+#LDAP_TLS_CIPHERS=SECURE256:SECURE128:!AES-128-CBC:!ARCFOUR-128:!CAMELLIA-128-CBC:!3DES-CBC:!CAMELLIA-128-CBC
+
+# Require and verify server certificate
+#LDAP_TLS_CHECK_PEER=1
+
+# Path to CA cert file. Used when server certificate verify is enabled
+#LDAP_TLS_CACERT_FILE=/etc/ssl/certs/ca-certificates.crt
+
+# Path to CA certs directory. Used when server certificate verify is enabled
+#LDAP_TLS_CACERT_DIR=/etc/ssl/certs
+
+# Wether to use starttls, implies LDAPv3 and requires ldap:// instead of ldaps://
+# LDAP_START_TLS=1
+
+
+#
+# Advanced configuration options (you generally don't need to change these)
+#
+
+# Internal XMPP domain
+XMPP_DOMAIN=meet.jitsi
+
+# Internal XMPP server
+XMPP_SERVER=xmpp.meet.jitsi
+
+# Internal XMPP server URL
+XMPP_BOSH_URL_BASE=http://xmpp.meet.jitsi:5280
+
+# Internal XMPP domain for authenticated services
+XMPP_AUTH_DOMAIN=auth.meet.jitsi
+
+# XMPP domain for the MUC
+XMPP_MUC_DOMAIN=muc.meet.jitsi
+
+# XMPP domain for the internal MUC used for jibri, jigasi and jvb pools
+XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
+
+# XMPP domain for unauthenticated users
+XMPP_GUEST_DOMAIN=guest.meet.jitsi
+
+# Comma separated list of domains for cross domain policy or "true" to allow all
+# The PUBLIC_URL is always allowed
+#XMPP_CROSS_DOMAIN=true
+
+# Custom Prosody modules for XMPP_DOMAIN (comma separated)
+XMPP_MODULES=
+
+# Custom Prosody modules for MUC component (comma separated)
+XMPP_MUC_MODULES=
+
+# Custom Prosody modules for internal MUC component (comma separated)
+XMPP_INTERNAL_MUC_MODULES=
+
+# MUC for the JVB pool
+JVB_BREWERY_MUC=jvbbrewery
+
+# XMPP user for JVB client connections
+JVB_AUTH_USER=jvb
+
+# STUN servers used to discover the server's public IP
+JVB_STUN_SERVERS=meet-jit-si-turnrelay.jitsi.net:443
+
+# Media port for the Jitsi Videobridge
+JVB_PORT=10000
+
+# TCP Fallback for Jitsi Videobridge for when UDP isn't available
+JVB_TCP_HARVESTER_DISABLED=true
+JVB_TCP_PORT=4443
+JVB_TCP_MAPPED_PORT=4443
+
+# A comma separated list of APIs to enable when the JVB is started [default: none]
+# See https://github.com/jitsi/jitsi-videobridge/blob/master/doc/rest.md for more information
+#JVB_ENABLE_APIS=rest,colibri
+
+# XMPP user for Jicofo client connections.
+# NOTE: this option doesn't currently work due to a bug
+JICOFO_AUTH_USER=focus
+
+# Base URL of Jicofo's reservation REST API
+#JICOFO_RESERVATION_REST_BASE_URL=http://reservation.example.com
+
+# Enable Jicofo's health check REST API (http://:8888/about/health)
+#JICOFO_ENABLE_HEALTH_CHECKS=true
+
+# XMPP user for Jigasi MUC client connections
+JIGASI_XMPP_USER=jigasi
+
+# MUC name for the Jigasi pool
+JIGASI_BREWERY_MUC=jigasibrewery
+
+# Minimum port for media used by Jigasi
+JIGASI_PORT_MIN=20000
+
+# Maximum port for media used by Jigasi
+JIGASI_PORT_MAX=20050
+
+# Enable SDES srtp
+#JIGASI_ENABLE_SDES_SRTP=1
+
+# Keepalive method
+#JIGASI_SIP_KEEP_ALIVE_METHOD=OPTIONS
+
+# Health-check extension
+#JIGASI_HEALTH_CHECK_SIP_URI=keepalive
+
+# Health-check interval
+#JIGASI_HEALTH_CHECK_INTERVAL=300000
+#
+# Enable Jigasi transcription
+#ENABLE_TRANSCRIPTIONS=1
+
+# Jigasi will record audio when transcriber is on [default: false]
+#JIGASI_TRANSCRIBER_RECORD_AUDIO=true
+
+# Jigasi will send transcribed text to the chat when transcriber is on [default: false]
+#JIGASI_TRANSCRIBER_SEND_TXT=true
+
+# Jigasi will post an url to the chat with transcription file [default: false]
+#JIGASI_TRANSCRIBER_ADVERTISE_URL=true
+
+# Credentials for connect to Cloud Google API from Jigasi
+# Please read https://cloud.google.com/text-to-speech/docs/quickstart-protocol
+# section "Before you begin" paragraph 1 to 5
+# Copy the values from the json to the related env vars
+#GC_PROJECT_ID=
+#GC_PRIVATE_KEY_ID=
+#GC_PRIVATE_KEY=
+#GC_CLIENT_EMAIL=
+#GC_CLIENT_ID=
+#GC_CLIENT_CERT_URL=
+
+# Enable recording
+#ENABLE_RECORDING=1
+
+# XMPP domain for the jibri recorder
+XMPP_RECORDER_DOMAIN=recorder.meet.jitsi
+
+# XMPP recorder user for Jibri client connections
+JIBRI_RECORDER_USER=recorder
+
+# Directory for recordings inside Jibri container
+JIBRI_RECORDING_DIR=/config/recordings
+
+# The finalizing script. Will run after recording is complete
+#JIBRI_FINALIZE_RECORDING_SCRIPT_PATH=/config/finalize.sh
+
+# XMPP user for Jibri client connections
+JIBRI_XMPP_USER=jibri
+
+# MUC name for the Jibri pool
+JIBRI_BREWERY_MUC=jibribrewery
+
+# MUC connection timeout
+JIBRI_PENDING_TIMEOUT=90
+
+# When jibri gets a request to start a service for a room, the room
+# jid will look like: roomName@optional.prefixes.subdomain.xmpp_domain
+# We'll build the url for the call by transforming that into:
+# https://xmpp_domain/subdomain/roomName
+# So if there are any prefixes in the jid (like jitsi meet, which
+# has its participants join a muc at conference.xmpp_domain) then
+# list that prefix here so it can be stripped out to generate
+# the call url correctly
+JIBRI_STRIP_DOMAIN_JID=muc
+
+# Directory for logs inside Jibri container
+JIBRI_LOGS_DIR=/config/logs
+
+# Disable HTTPS: handle TLS connections outside of this setup
+#DISABLE_HTTPS=1
+
+# Enable FLoC
+# Opt-In to Federated Learning of Cohorts tracking
+#ENABLE_FLOC=0
+
+# Redirect HTTP traffic to HTTPS
+# Necessary for Let's Encrypt, relies on standard HTTPS port (443)
+#ENABLE_HTTP_REDIRECT=1
+
+# Send a `strict-transport-security` header to force browsers to use
+# a secure and trusted connection. Recommended for production use.
+# Defaults to 1 (send the header).
+# ENABLE_HSTS=1
+
+# Enable IPv6
+# Provides means to disable IPv6 in environments that don't support it (get with the times, people!)
+#ENABLE_IPV6=1
+
+# Container restart policy
+# Defaults to unless-stopped
+RESTART_POLICY=unless-stopped
+
+# Authenticate using external service or just focus external auth window if there is one already.
+# TOKEN_AUTH_URL=https://auth.meet.example.com/{room}
+
+# nginx-proxy settings
+VIRTUAL_HOST="meet.bvrn.de"
+VIRTUAL_PORT=80
+LETSENCRYPT_HOST="meet.bvrn.de"
+LETSENCRYPT_EMAIL="j.otto@bvrn.de"
diff --git a/templates/keycloak/.env.j2 b/templates/keycloak/.env.j2
new file mode 100644
index 0000000..9a474bf
--- /dev/null
+++ b/templates/keycloak/.env.j2
@@ -0,0 +1,23 @@
+KEYCLOAK_CREATE_ADMIN_USER=true
+KEYCLOAK_ADMIN_USER=admin
+KEYCLOAK_ADMIN_PASSWORD={{ keycloak_admin_password }}
+KEYCLOAK_MANAGEMENT_USER=manager
+KEYCLOAK_MANAGEMENT_PASSWORD={{ keycloak_management_password }}
+KEYCLOAK_DATABASE_HOST=postgresql
+KEYCLOAK_DATABASE_PORT=5432
+KEYCLOAK_DATABASE_NAME=bitnami_keycloak
+KEYCLOAK_DATABASE_USER=bn_keycloak
+KEYCLOAK_DATABASE_PASSWORD={{ keycloak_database_password }}
+KEYCLOAK_DATABASE_SCHEMA=public
+KEYCLOAK_JDBC_PARAMS
+KEYCLOAK_HTTP_PORT=8080
+KEYCLOAK_HTTPS_PORT=""
+KEYCLOAK_BIND_ADDRESS
+KEYCLOAK_PROXY="edge"
+KEYCLOAK_PRODUCTION=true
+
+VIRTUAL_HOST="accounts.bvrn.de"
+VIRTUAL_PORT=8080
+LETSENCRYPT_HOST="accounts.bvrn.de"
+LETSENCRYPT_EMAIL="j.otto@bvrn.de"
+
diff --git a/templates/limesurvey/.env.j2 b/templates/limesurvey/.env.j2
new file mode 100644
index 0000000..8158d98
--- /dev/null
+++ b/templates/limesurvey/.env.j2
@@ -0,0 +1,13 @@
+LIMESURVEY_DB_PASSWORD={{ limesurvey_db_password }}
+LIMESURVEY_ADMIN_USER=admin
+LIMESURVEY_ADMIN_PASSWORD={{ limesurvey_admin_password }}
+LIMESURVEY_ADMIN_NAME=Lime Administrator
+LIMESURVEY_ADMIN_EMAIL=admin@bvrn.de
+MYSQL_ROOT_PASSWORD={{ limesurvey_db_password }}
+VIRTUAL_HOST="survey.bvrn.de"
+VIRTUVIRTUAL_HOST="survey.bvrn.de"
+VIRTUAL_PORT=80
+LETSENCRYPT_HOST="survey.bvrn.de"
+LETSENCRYPT_EMAIL="j.otto@bvrn.de"AL_PORT=80
+LETSENCRYPT_HOST="survey.bvrn.de"
+LETSENCRYPT_EMAIL="j.otto@bvrn.de"
\ No newline at end of file
diff --git a/templates/mattermost/.env.j2 b/templates/mattermost/.env.j2
new file mode 100644
index 0000000..a38e045
--- /dev/null
+++ b/templates/mattermost/.env.j2
@@ -0,0 +1,91 @@
+# Domain of service
+DOMAIN=chat.bvrn.de
+
+# Container settings
+## Timezone inside the containers. The value needs to be in the form 'Europe/Berlin'.
+## A list of these tz database names can be looked up at Wikipedia
+## https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+TZ=Europe/Berlin
+RESTART_POLICY=unless-stopped
+
+# Postgres settings
+## Documentation for this image and available settings can be found on hub.docker.com
+## https://hub.docker.com/_/postgres
+## Please keep in mind this will create a superuser and it's recommended to use a less privileged
+## user to connect to the database.
+## A guide on how to change the database user to a nonsuperuser can be found in docs/creation-of-nonsuperuser.md
+POSTGRES_IMAGE_TAG=13-alpine
+POSTGRES_DATA_PATH=./volumes/db/var/lib/postgresql/data
+
+POSTGRES_USER=mmuser
+POSTGRES_PASSWORD={{ mattermost_db_password }}
+POSTGRES_DB=mattermost
+
+# Nginx
+## The nginx container will use a configuration found at the NGINX_MATTERMOST_CONFIG. The config aims
+## to be secure and uses a catch-all server vhost which will work out-of-the-box. For additional settings
+## or changes ones can edit it or provide another config. Important note: inside the container, nginx sources
+## every config file inside */etc/nginx/conf.d* ending with a *.conf* file extension.
+
+## Inside the container the uid and gid is 101. The folder owner can be set with
+## `sudo chown -R 101:101 ./nginx` if needed.
+NGINX_IMAGE_TAG=alpine
+
+## The folder containing server blocks and any additional config to nginx.conf
+NGINX_CONFIG_PATH=./nginx/conf.d
+NGINX_DHPARAMS_FILE=./nginx/dhparams4096.pem
+
+CERT_PATH=./volumes/web/cert/cert.pem
+KEY_PATH=./volumes/web/cert/key-no-password.pem
+#GITLAB_PKI_CHAIN_PATH=/pki_chain.pem
+#CERT_PATH=./certs/etc/letsencrypt/live/${DOMAIN}/fullchain.pem
+#KEY_PATH=./certs/etc/letsencrypt/live/${DOMAIN}/privkey.pem
+
+## Exposed ports to the host. Inside the container 80 and 443 will be used
+HTTPS_PORT=443
+HTTP_PORT=80
+
+# Mattermost settings
+## Inside the container the uid and gid is 2000. The folder owner can be set with
+## `sudo chown -R 2000:2000 ./volumes/app/mattermost`.
+MATTERMOST_CONFIG_PATH=./volumes/app/mattermost/config
+MATTERMOST_DATA_PATH=./volumes/app/mattermost/data
+MATTERMOST_LOGS_PATH=./volumes/app/mattermost/logs
+MATTERMOST_PLUGINS_PATH=./volumes/app/mattermost/plugins
+MATTERMOST_CLIENT_PLUGINS_PATH=./volumes/app/mattermost/client/plugins
+MATTERMOST_BLEVE_INDEXES_PATH=./volumes/app/mattermost/bleve-indexes
+
+## Bleve index (inside the container)
+MM_BLEVESETTINGS_INDEXDIR=/mattermost/bleve-indexes
+
+## This will be 'mattermost-enterprise-edition' or 'mattermost-team-edition' based on the version of Mattermost you're installing.
+MATTERMOST_IMAGE=mattermost-enterprise-edition
+MATTERMOST_IMAGE_TAG=7.1
+
+## Make Mattermost container readonly. This interferes with the regeneration of root.html inside the container. Only use
+## it if you know what you're doing.
+## See https://github.com/mattermost/docker/issues/18
+MATTERMOST_CONTAINER_READONLY=false
+
+## The app port is only relevant for using Mattermost without the nginx container as reverse proxy. This is not meant
+## to be used with the internal HTTP server exposed but rather in case one wants to host several services on one host
+## or for using it behind another existing reverse proxy.
+APP_PORT=8065
+
+## Configuration settings for Mattermost. Documentation on the variables and the settings itself can be found at
+## https://docs.mattermost.com/administration/config-settings.html
+## Keep in mind that variables set here will take precedence over the same setting in config.json. This includes
+## the system console as well and settings set with env variables will be greyed out.
+
+## Below one can find necessary settings to spin up the Mattermost container
+MM_SQLSETTINGS_DRIVERNAME=postgres
+MM_SQLSETTINGS_DATASOURCE=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable&connect_timeout=10
+
+## Example settings (any additional setting added here also needs to be introduced in the docker-compose.yml)
+MM_SERVICESETTINGS_SITEURL=https://${DOMAIN}
+
+## nginx-letsencrypt-proxy settings
+VIRTUAL_HOST="chat.bvrn.de"
+VIRTUAL_PORT=8065
+LETSENCRYPT_HOST="chat.bvrn.de"
+LETSENCRYPT_EMAIL="j.otto@bvrn.de"
diff --git a/templates/nextcloud/.env.j2 b/templates/nextcloud/.env.j2
new file mode 100644
index 0000000..7d42c80
--- /dev/null
+++ b/templates/nextcloud/.env.j2
@@ -0,0 +1,9 @@
+POSTGRES_PASSWORD={{ nextcloud_postgres_password }}
+POSTGRES_DB=nextcloud
+POSTGRES_USER=nextcloud
+POSTGRES_HOST=db
+REDIS_HOST=redis
+VIRTUAL_HOST="cloud.bvrn.de"
+VIRTUAL_PORT=80
+LETSENCRYPT_HOST="cloud.bvrn.de"
+LETSENCRYPT_EMAIL="j.otto@bvrn.de"
diff --git a/templates/nginx-letsencrypt-proxy/.env.j2 b/templates/nginx-letsencrypt-proxy/.env.j2
new file mode 100644
index 0000000..92f5240
--- /dev/null
+++ b/templates/nginx-letsencrypt-proxy/.env.j2
@@ -0,0 +1,136 @@
+#-----------------------------------------------------------------------
+#
+# https://github.com/evertramos/nginx-proxy-automation
+#
+# NGINX Proxy automation
+#
+# This is the .env file to set up nginx-proxy in your environment
+#
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+#
+# Script requirements
+#
+
+# PID file the running script
+PID_FILE=.fresh_start.pid
+
+# Allow run commands with sudo if needed
+ALLOW_RUN_WITH_SUDO=false
+
+#-----------------------------------------------------------------------
+#
+# NGINX-Proxy containers/service
+#
+# The variables below is used to set the containers name for the nginx-proxy
+# and the image version for each service, please note that if you use our script
+# we will replace the service name in docker-compose file with the container name
+#
+NGINX_WEB_SEVICE_NAME=proxy-web
+NGINX_IMAGE_VERSION=stable-alpine
+
+DOCKER_GEN_SEVICE_NAME=docker-gen
+DOCKER_GEN_IMAGE_VERSION=latest
+
+LETS_ENCRYPT_SEVICE_NAME=letsencrypt-companion
+NGINX_PROXY_COMPANION_IMAGE_VERSION=2.0
+
+#-----------------------------------------------------------------------
+#
+# IP address of the external interface
+#
+# The IP address below is used to bind your local services to the internet
+# please make sure you use the correct address otherwise your proxy will not
+# work properly, '0.0.0.0' will work, but we recommend to update this variable
+#
+IPv4=0.0.0.0
+IPv6=::0
+
+#-----------------------------------------------------------------------
+#
+# Default network name
+#
+# The network name set below is used by the proxy to forward internet requests
+# to the correct containers in your environment, so please make sure to add this
+# network in all docker containers, otherwise it will break the proxy redirection
+#
+NETWORK=proxy
+
+#-----------------------------------------------------------------------
+#
+# Data path for the nginx-proxy files
+#
+# The variable below will be used to place all files used by the nginx-proxy
+# please consider including this folder to your backup services, once all config
+# files, settings and certificates will be placed here in case you need to recover
+#
+NGINX_FILES_PATH=./data
+
+#-----------------------------------------------------------------------
+#
+# Docker logging settings
+#
+# Logs! Very important, right? But if you do not clean it up, it might causes you
+# issues on disk space over time, so keep in mind to set this log options making sure
+# you will have the least to audit, any further information on that please check the docs
+#
+# https://docs.docker.com/config/containers/logging/configure/
+#
+NGINX_WEB_LOG_DRIVER=json-file
+NGINX_WEB_LOG_MAX_SIZE=4m
+NGINX_WEB_LOG_MAX_FILE=10
+
+NGINX_GEN_LOG_DRIVER=json-file
+NGINX_GEN_LOG_MAX_SIZE=2m
+NGINX_GEN_LOG_MAX_FILE=10
+
+NGINX_LETSENCRYPT_LOG_DRIVER=json-file
+NGINX_LETSENCRYPT_LOG_MAX_SIZE=2m
+NGINX_LETSENCRYPT_LOG_MAX_FILE=10
+
+#-----------------------------------------------------------------------
+#
+# Docker ports that should be binded by the proxy
+#
+# This option were added by a contributor long ago, so might be a very specific case
+# where you might need to change http and https port number, keep in mind that changes
+# on that setting might will break the auto renewing Let's Encrypt certificate services
+#
+DOCKER_HTTP_=80
+DOCKER_HTTPS=443
+
+#-----------------------------------------------------------------------
+#
+# SSL policy (defaults to Mozilla-Intermediate)
+#
+# This also was added by a contributor which sets the default cipher configuration
+# to the nginx-proxy container, which has the 'Mozilla-Intermediate' as default value
+# plase make sure you take a good look at options in the url below before messing around
+#
+# https://github.com/nginx-proxy/nginx-proxy#how-ssl-support-works
+#
+#SSL_POLICY=Mozilla-Modern
+
+#-----------------------------------------------------------------------
+#
+# Let's Encrypt default email
+#
+# You might want to inform a default email to Let's Encrypt certificate once it is
+# a required parameter in order to issue the new certificate. This information will be
+# replaced by the LETSENCRYPT_EMAIL environment varibale present in your docker container
+#
+DEFAULT_EMAIL=j.otto@bvrn.de
+
+#-----------------------------------------------------------------------
+#
+# Default host
+#
+# Nginx-proxy will then redirect all requests to a container where you have set
+# "VIRTUAL HOST" set to "DEFAULT HOST", if they don't match any (other) container
+# You might want to check the link below for more information:
+# https://github.com/nginx-proxy/nginx-proxy#default-host
+#
+DEFAULT_HOST=
+
+USE_NGINX_CONF_FILES=true
diff --git a/templates/openldap/.env.j2 b/templates/openldap/.env.j2
new file mode 100644
index 0000000..50484d5
--- /dev/null
+++ b/templates/openldap/.env.j2
@@ -0,0 +1,4 @@
+LDAP_PORT_NUMBER=1389
+LDAP_ROOT=dc=bvrn,dc=de
+LDAP_ADMIN_USERNAME=admin
+LDAP_ADMIN_PASSWORD={{ openldap_admin_password }}
diff --git a/templates/openldap/docker-compose.yml.j2 b/templates/openldap/docker-compose.yml.j2
new file mode 100644
index 0000000..93303dd
--- /dev/null
+++ b/templates/openldap/docker-compose.yml.j2
@@ -0,0 +1,26 @@
+version: '3.5'
+
+services:
+ openldap:
+ image: docker.io/bitnami/openldap:2.5
+ expose:
+ - 1389
+ ports:
+ - '127.0.0.1:1389:1389'
+ environment:
+ - BITNAMI_DEBUG=false
+ - LDAP_ROOT
+ - LDAP_ADMIN_USERNAME
+ - LDAP_ADMIN_PASSWORD
+ volumes:
+ - 'openldap_data:/bitnami/openldap'
+ - './ldifs:/ldifs'
+
+volumes:
+ openldap_data:
+ driver: local
+
+networks:
+ default:
+ external: true
+ name: ${NETWORK:-ldap}