diff --git a/site.yml b/site.yml index 834fff2e11119e99cf63a1e7fc537d0880116b65..073c3d424f2619020e6be0ca717284de3135c27c 100644 --- a/site.yml +++ b/site.yml @@ -227,6 +227,12 @@ basedir: /srv/dockerstats, metrics_port: 9487 } + - { + role: webserver/docker_uffd, tags: [ uffd, docker_services ], + servicename: uffd, + basedir: "/srv/{{ servicename }}", + domain: "uffd.warpzone.ms", + } - { role: common/docker_ldap, tags: [ ldap, docker_services ], servicename: ldap, diff --git a/webserver/docker_uffd/tasks/main.yml b/webserver/docker_uffd/tasks/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..8051170e4f19dd613c732d36a628e47dd31e76f4 --- /dev/null +++ b/webserver/docker_uffd/tasks/main.yml @@ -0,0 +1,41 @@ +--- + +- include_tasks: ../functions/get_secret.yml + with_items: + - { path: "{{ basedir }}/secrets/mysql_admin_pass", length: 24 } + - { path: "{{ basedir }}/secrets/mysql_user_pass", length: 24 } + - { path: "{{ basedir }}/secrets/uffd_admin_pass", length: 24 } + - { path: "{{ basedir }}/secrets/uffd_secret_key", length: 64 } + - { path: "{{ basedir }}/secrets/uffd_mail_pass", length: 12 } + +- name: create folder struct for {{servicename}} + file: + path: "{{ item }}" + state: "directory" + with_items: + - "{{ basedir }}" + - "{{ basedir }}/secrets" + - "{{ basedir }}/db/" + +- name: create config files + template: + src: "{{ item }}" + dest: "{{ basedir }}/{{ item }}" + with_items: + - "Dockerfile" + - "entrypoint.sh" + - "docker-compose.yml" + - "uffd.cfg" + register: config + +- name: stop {{ servicename }} docker + community.docker.docker_compose_v2: + project_src: "{{ basedir }}" + state: absent + when: config.changed + +- name: start {{servicename}} docker + community.docker.docker_compose_v2: + project_src: "{{ basedir }}" + state: present + build: always diff --git a/webserver/docker_uffd/templates/Dockerfile b/webserver/docker_uffd/templates/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3cc374e7417cd3bcbd3bc047cd11d1be540b4f74 --- /dev/null +++ b/webserver/docker_uffd/templates/Dockerfile @@ -0,0 +1,78 @@ +FROM debian:bookworm AS build-stage + +ENV DEBIAN_FRONTEND=noninteractive +ENV PYBUILD_INSTALL_ARGS="--install-lib=/usr/share/uffd/ --install-scripts=/usr/share/uffd/" +ENV PACKAGE_VERSION=v2.3.1.r21 + +RUN set -x && \ + apt update && \ + apt install -y --no-install-recommends \ + lsb-release \ + curl \ + ca-certificates \ + locales-all \ + git \ + python3 \ + python3-venv \ + python3-coverage \ + python3-ldap3 \ + python3-flask \ + python3-flask-sqlalchemy \ + python3-flask-migrate \ + python3-pip \ + python3-qrcode \ + python3-fido2 \ + python3-oauthlib \ + python3-flask-babel \ + python3-argon2 \ + python3-pytest \ + python3-all \ + python3-pip \ + git-buildpackage \ + debhelper \ + build-essential \ + dh-python \ + python3-mysqldb \ + python3-requests-oauthlib \ + python3-git \ + python3-prometheus-client \ + libffi-dev \ + python3-build \ + twine && \ + apt -qq clean + +RUN git clone https://git.cccv.de/uffd/uffd.git +WORKDIR /uffd +# Checkout Git, Current version 31.05.2024 +RUN git checkout 89f1ecdda1149c2d9585135a7186114b645396e1 + +RUN set -x && \ + ./debian/create_changelog.py uffd > debian/changelog && \ + dpkg-buildpackage -us -uc && \ + dpkg-deb -I /*.deb && \ + dpkg-deb -c /*.deb && \ + mv /*.deb /uffd/uffd.deb + +FROM debian:bookworm as app-stage + +COPY --from=build-stage /uffd/uffd.deb /uffd.deb + +RUN set -x && \ + apt update && \ + apt install -y --no-install-recommends /uffd.deb python3-psycopg2 python3-pymysql && \ + rm -rf /var/lib/apt/lists/* && \ + rm /uffd.deb && \ + cat /etc/uffd/uffd.cfg | grep -v "SECRET_KEY=" > /etc/uffd/uffd.cfg.tmp && \ + mv /etc/uffd/uffd.cfg.tmp /etc/uffd/uffd.cfg && \ + mkdir --parents /var/www/uffd && \ + chown root:uffd /var/www/uffd + +COPY entrypoint.sh /entrypoint.sh + +USER uffd +USER root + +EXPOSE 3031/tcp +EXPOSE 9191/tcp + +CMD bash /entrypoint.sh diff --git a/webserver/docker_uffd/templates/Dockerfile-release b/webserver/docker_uffd/templates/Dockerfile-release new file mode 100644 index 0000000000000000000000000000000000000000..feaaa98add6801d43b3a615b3e861541be492ea1 --- /dev/null +++ b/webserver/docker_uffd/templates/Dockerfile-release @@ -0,0 +1,46 @@ +FROM debian:bullseye + +ENV DEBIAN_FRONTEND=noninteractive + +# Install depandencies +RUN set -x && \ + apt update && \ + apt install -y --no-install-recommends \ + ca-certificates \ + curl \ + gnupg2 \ + python3 \ + python3-argon2 \ + python3-cryptography \ + python3-fido2 \ + python3-flask \ + python3-flask-babel \ + python3-flask-sqlalchemy \ + python3-flask-migrate \ + python3-itsdangerous \ + python3-jwt \ + python3-qrcode \ + python3-prometheus-client \ + python3-pymysql \ + python3-oauthlib \ + python3-uaparser \ + wget && \ + rm -rf /var/lib/apt/lists/* + +# Install uffd from CCCV Repo +RUN set -x && \ + echo "deb https://packages.cccv.de/uffd bullseye main" > /etc/apt/sources.list.d/cccv-archive-bullseye.list && \ + curl -sS https://packages.cccv.de/docs/cccv-archive-key.gpg | gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/cccv-archive-key.gpg && \ + apt update && \ + apt install -y --no-install-recommends uffd=2.3.2 && \ + rm -rf /var/lib/apt/lists/* + +COPY entrypoint.sh /entrypoint.sh + +USER uffd +USER root + +EXPOSE 3031/tcp +EXPOSE 9191/tcp + +CMD bash /entrypoint.sh diff --git a/webserver/docker_uffd/templates/docker-compose.yml b/webserver/docker_uffd/templates/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..fd4b853c873b9c10b60f7f764d0a86928405c239 --- /dev/null +++ b/webserver/docker_uffd/templates/docker-compose.yml @@ -0,0 +1,40 @@ +services: + db: + image: mariadb:11.2.2 + restart: always + command: ['mariadbd', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_nopad_bin'] + volumes: + - "{{ basedir }}/db/:/var/lib/mysql" + environment: + MYSQL_ROOT_PASSWORD: "{{ mysql_admin_pass }}" + MYSQL_PASSWORD: "{{ mysql_user_pass }}" + MYSQL_DATABASE: "uffd" + MYSQL_USER: "uffd" + networks: + - default + app: + build: . + image: uffd:v2.3.1.r21 + restart: always + depends_on: + - db + volumes: + - "{{ basedir }}/uffd.cfg/:/etc/uffd/uffd.cfg" + environment: + TZ: "Europe/Berlin" + UFFD_INITIAL_ADMIN_USER: "uffdadmin" + UFFD_INITIAL_ADMIN_PW: "{{ uffd_admin_pass }}" + UFFD_INITIAL_ADMIN_MAIL: "uffdadmin@jabertwo.de" + labels: + - com.centurylinklabs.watchtower.enable=false + - traefik.enable=true + - traefik.http.routers.{{ servicename }}.rule=Host(`{{ domain }}`) + - traefik.http.routers.{{ servicename }}.entrypoints=websecure + - traefik.http.services.{{ servicename }}.loadbalancer.server.port=3031 + networks: + - default + - web + +networks: + web: + external: true diff --git a/webserver/docker_uffd/templates/entrypoint.sh b/webserver/docker_uffd/templates/entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..247cc712cc414088c99778e8ea678999bf0354b9 --- /dev/null +++ b/webserver/docker_uffd/templates/entrypoint.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Modded to Dockerfile +#echo "Copying static files ..." +#cp -r /usr/share/uffd/uffd/static /var/www/uffd + +db_ready="false" +count=0 +while [ $count -lt 32 ] && [ "$db_ready" != "true" ] ;do + if [ -z "$(uffd-admin db current 2>&1 | grep -o "Error")" ] ;then + db_ready="true" + else + echo "Waiting for db to become ready..." + ((duration=2**$count)) + sleep $duration + ((count=$count+1)) + fi +done + +if [ "$db_ready" == "true" ] ;then + echo "Running datbase migrations ..." + uffd-admin db upgrade + + if [ -n "$UFFD_INITIAL_ADMIN_PW" ] && [ "$(uffd-admin user list)" == "" ]; then + echo "Creating groups and roles for initial admin user ..." + if ! uffd-admin group show 'uffd_admin' >> /dev/null 2>&1 ;then + uffd-admin group create 'uffd_admin' --description 'Admin access to uffd' + fi + if ! uffd-admin group show 'uffd_access' >> /dev/null 2>&1 ;then + uffd-admin group create 'uffd_access' --description 'Access to Single-Sign-On and Selfservice' + fi + if ! uffd-admin role show 'uffd_admin' >> /dev/null 2>&1 ;then + uffd-admin role create 'uffd_admin' --add-group 'uffd_admin' --add-group 'uffd_access' + fi + if [ -z "$UFFD_INITIAL_ADMIN_USER" ] ;then + UFFD_INITIAL_ADMIN_USER='uffd_admin' + fi + if [ -z "$UFFD_INITIAL_ADMIN_MAIL" ] ;then + UFFD_INITIAL_ADMIN_MAIL='uffd_admin@localhost' + fi + echo "Creating initial admin user ..." + uffd-admin user create "$UFFD_INITIAL_ADMIN_USER" --password "$UFFD_INITIAL_ADMIN_PW" --mail "$UFFD_INITIAL_ADMIN_MAIL" --displayname 'uffd Admin' --add-role 'uffd_admin' + fi +else + echo "WARNING: Database is not ready yet, skipping migration and initialization" +fi + +echo "Starting server ..." +runuser --preserve-environment -u uffd -- \ + uwsgi --ini /etc/uwsgi/apps-enabled/uffd.ini --http-socket 0.0.0.0:3031 --master --stats 0.0.0.0:9191 diff --git a/webserver/docker_uffd/templates/uffd.cfg b/webserver/docker_uffd/templates/uffd.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6de25a93f469c5b409298f14cac77d15e4f84b8f --- /dev/null +++ b/webserver/docker_uffd/templates/uffd.cfg @@ -0,0 +1,123 @@ + +LANGUAGES={ + # Language identifier (see Accept-Language HTTP header) -> Display Name + "en": "EN", + "de": "DE", +} + +# Uffd Admins Group +ACL_ADMIN_GROUP="uffd_admin" +# Group required to access selfservice functions (view selfservice, change profile/password/roles) +ACL_SELFSERVICE_GROUP="uffd_access" +# Group required to login +ACL_ACCESS_GROUP="uffd_access" +# Members can create invite links for signup +ACL_SIGNUP_GROUP="uffd_signup" + +MAIL_SERVER='mail.test-warpzone.de' +MAIL_PORT=587 +MAIL_USERNAME='noreply-uffd@test-warpzone.de' +MAIL_PASSWORD='{{ uffd_mail_pass }}' +MAIL_USE_STARTTLS=True +MAIL_FROM_ADDRESS='noreply-uffd@test-warpzone.de' + +# Do not enable this on a public service! There is no spam protection implemented at the moment. +SELF_SIGNUP=False + +# Max Lifetime for invites +INVITE_MAX_VALID_DAYS=21 + +# Blocked Loginnames +LOGINNAME_BLOCKLIST=['^admin$', '^root$'] + +#MFA_ICON_URL = 'https://example.com/logo.png' +#MFA_RP_ID = 'example.com' # If unset, hostname from current request is used +MFA_RP_NAME = 'Uffd Test Service' # Service name passed to U2F/FIDO2 authenticators + + +FOOTER_LINKS=[{"url": "https://example.com", "title": "example"}] + +# The default page after login or clicking the top left home button is the self-service +# page. If you would like it to be the services list instead, set this to True. +DEFAULT_PAGE_SERVICES=True + +# Service overview page (disabled if empty) +SERVICES=[ +# # Title is mandatory, all other fields are optional. +# # For permission_levels/groups/infos/links all fields are mandatory aside from required_group. +# { +# 'title': 'Service Title', +# 'subtitle': 'Service Subtitle', +# 'description': 'Short description of the service as plain text', +# 'url': 'https://example.com/', +# 'logo_url': 'https://example.com/logo.png', +# # Basic access group name, service is accessible to everyone if empty +# 'required_group': 'users', +# # Non-basic permission levels, the last matching entry is selected. +# # Users with a matching permission level are considered to have +# # access to the service (as if they have the basic access group). +# 'permission_levels': [ +# {'name': 'Moderator', 'required_group': 'moderators'}, +# {'name': 'Admin', 'required_group': 'uffd_admin'}, +# ], +# # Per default all services are listed publicly (but grayed out for +# # guests/users without access). Confidential services are only visible +# # to users with access rights to the service. +# 'confidential': True, +# # In-service groups, all matching items are visible +# 'groups': [ +# {'name': 'Group "crew_crew"', 'required_group': 'users'}, +# {'name': 'Group "crew_logistik"', 'required_group': 'uffd_admin'}, +# ], +# # Infos are small/medium amounts of information displayed in a modal +# # dialog. All matching items are visible. +# 'infos': [ +# { +# 'title': 'uffd', +# 'button_text': 'Documentation', # Defaults to the title if not set +# 'html': '<p>Some information about the service as html</p>', +# 'required_group': 'users', +# }, +# ], +# # Links to external sites, all matching items are visible +# 'links': [ +# {'title': 'Link to an external site', 'url': '#', 'required_group': 'users'}, +# ] +# }, + + { + 'title': 'Icinga', + 'url': 'https://icinga.test-warpzone.de', + 'logo_url': 'https://icinga.test-warpzone.de/icingaweb2/img/favicon.png' + } + +] + +# A banner text that will be displayed above the services list +SERVICES_BANNER='Available Services' + +# If the banner should be shown to users who are not logged in +SERVICES_BANNER_PUBLIC=False + +# Enable the service overview page for users who are not logged in +SERVICES_PUBLIC=False + +# An optional banner that will be displayed above the login form +LOGIN_BANNER='Always check the URL. Never enter your SSO password on any other site.' + +BRANDING_LOGO_URL='/static/empty.png' +SITE_TITLE='uffd @ test-warpzone.de' + +# Name and contact mail address are displayed to users in a few places (plain text only!) +ORGANISATION_NAME='test-warpzone.de' +ORGANISATION_CONTACT='uffd@test-warpzone.de' + +# Optional text included in account registration mails (plain text only!) +WELCOME_TEXT='See https://docs.example.com/ for further information.' + + +# DO set in production +FLASK_ENV="production" +SQLALCHEMY_DATABASE_URI="mysql+pymysql://uffd:{{ mysql_user_pass }}@db/uffd?charset=utf8mb4" +SECRET_KEY="{{ uffd_secret_key }}" +DEBUG=False