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 @@ + + + +image/svg+xml + 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 @@ + + + +image/svg+xml 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}