diff --git a/docker/.env b/docker/.env
new file mode 100644
index 00000000..517e3a7c
--- /dev/null
+++ b/docker/.env
@@ -0,0 +1,8 @@
+# Set container timezone
+TZ=America/Edmonton
+
+# Used within the docker-compose.yml template to provide easy configuration for your domain.
+ROOT_DOMAIN=itflow.org
+
+# Generate a random password using `docker run php:8.0-apache openssl rand -base64 32`
+ITFLOW_DB_PASS=thisisnotsecure
\ No newline at end of file
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 00000000..611d98e8
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,75 @@
+FROM ubuntu:22.04
+
+LABEL dockerfile.version="v1.1" dockerfile.release-date="2023-01-03"
+
+# Set up ENVs that will be utilized in compose file.
+ENV TZ Etc/UTC
+
+ENV ITFLOW_NAME ITFlow
+
+ENV ITFLOW_URL demo.itflow.org
+
+ENV ITFLOW_PORT 8080
+
+ENV ITFLOW_REPO github.com/itflow-org/itflow
+
+# apache2 log levels: emerg, alert, crit, error, warn, notice, info, debug
+ENV ITFLOW_LOG_LEVEL warn
+
+ENV ITFLOW_DB_HOST itflow-db
+
+ENV ITFLOW_DB_PASS null
+
+# Set timezone from TZ ENV
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+# PREREQS: php php-intl php-mysqli php-imap php-curl libapache2-mod-php mariadb-server git -y
+# Upgrade, then install prereqs.
+RUN apt-get update && apt-get upgrade -y && apt-get clean
+
+# ITFlow Requirements
+RUN apt-get install -y \
+ git\
+ apache2\
+ php
+
+# Ubuntu quality of life installs
+RUN apt-get install -y \
+ vim\
+ cron\
+ dnsutils\
+ iputils-ping
+
+# Install & enable php extensions
+RUN apt-get install -y \
+ php-intl\
+ php-mysqli\
+ php-curl\
+ php-imap
+
+RUN apt-get install -y \
+ libapache2-mod-php\
+ libapache2-mod-md
+
+# Enable md apache mod
+RUN a2enmod md
+
+# Set the work dir to the git repo.
+WORKDIR /var/www/html
+
+# Entrypoint
+# On every run of the docker file, perform an entrypoint that verifies the container is good to go.
+COPY entrypoint.sh /usr/bin/
+
+RUN chmod +x /usr/bin/entrypoint.sh
+
+# forward request and error logs to docker log collector
+RUN ln -sf /dev/stdout /var/log/apache2/access.log && ln -sf /dev/stderr /var/log/apache2/error.log
+
+ENTRYPOINT [ "entrypoint.sh" ]
+
+# Expose the apache port
+EXPOSE $ITFLOW_PORT
+
+# Start the httpd service and have logs appear in stdout
+CMD [ "apache2ctl", "-D", "FOREGROUND" ]
\ No newline at end of file
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 00000000..04244740
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,53 @@
+# About this Image
+> This is an unofficial image of [ITFlow](https://github.com/itflow-org/itflow) @ https://itflow.org.
+
+> Maintained by [@lued](https://github.com/lued/itflow/tree/docker)
+
+# Usage
+## ITFlow Only (no Reverse Proxy)
+1. Copy [docker-compose.yml](https://raw.githubusercontent.com/lued/itflow/docker/docker/docker-compose.yml) to a directory.
+2. Within docker-compose.yml, adjust the ```environment:``` variables such as ITFLOW_NAME, ITFLOW_URL and ITFLOW_REPO (to your own MSPs fork).
+3. Copy the [.env](https://raw.githubusercontent.com/lued/itflow/docker/docker/.env) file to the same directory.
+> Enter your timezone, root domain and database password within this file. You can avoid this step entirely by adding the information to your docker-compose.yml file directly instead. Or being safe, by using docker secrets.
+4. Run ```docker compose up -d```
+5. Go to your domain. You should be redirected to setup.php. Enter server information correlated to your set up .env and docker-compose.yml files.
+> Defaults: Username: itflow, Password: $ITFLOW_DB_PASS from .env, Database: itflow, Server: itflow-db
+
+## Complete [Traefik](https://doc.traefik.io/traefik/getting-started/quick-start/) Solution (Reverse Proxy)
+1. Copy the traefik [docker-compose.yml](https://raw.githubusercontent.com/lued/itflow/docker/docker/traefik-complete/docker-compose.yml) to a directory.
+2. Within docker-compose.yml, adjust the ```environment:``` variables such as ITFLOW_NAME, ITFLOW_URL and ITFLOW_REPO (to your own MSPs fork).
+3. Copy the [.env](https://raw.githubusercontent.com/lued/itflow/docker/docker/traefik-complete/.env) file to the same directory.
+> Enter your docker path (/srv/docker, ., etc), cloudflare info, timezone, root domain and database password within this file.
+4. Create your A records for your host.
+5. Run ```docker compose up -d```
+6. Verify you are getting certificates through LetsEncrypt. You will have two public URLs, traefik.$ROOT_DOMAIN and $ITFLOW_URL.
+7. Go to your domain. You should be redirected to setup.php. Enter server information correlated to .env and docker-compose.yml
+> Defaults: Username: itflow, Password: $ITFLOW_DB_PASS from .env, Database: itflow, Server: itflow-db
+
+
+
+## Environment Variables
+```
+ENV TZ Etc/UTC
+
+ENV ITFLOW_NAME ITFlow
+
+ENV ITFLOW_REPO github.com/itflow-org/itflow
+
+ENV ITFLOW_URL demo.itflow.org
+
+ENV ITFLOW_PORT 8080
+
+# apache2 log levels: emerg, alert, crit, error, warn, notice, info, debug
+ENV ITFLOW_LOG_LEVEL warn
+
+ENV ITFLOW_DB_HOST itflow-db
+
+ENV ITFLOW_DB_PASS null
+```
+
+### In Beta
+* I highly recommend putting your solution behind [Authelia](https://www.authelia.com/). If requested, I can supply more information on this topic.
+* This project is still in early beta and is considered a **work in progress**. Many changes are being performed and may cause breakage upon updates.
+* Currently, we strongly recommend against storing confidential information in ITFlow; ITFlow has not undergone a third-party security assessment.
+* We are hoping to have a stable 1.0 release early this year.
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 00000000..2c72110c
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,59 @@
+version: "3.9"
+########################### NETWORKS
+
+networks:
+ wan:
+ name: wan
+ driver: bridge
+
+ itflow-db:
+ name: itflow-db
+ external: false
+
+########################### VOLUMES
+
+volumes:
+ itflow-db:
+
+########################### ITFLOW
+services:
+ itflow:
+ hostname: itflow
+ container_name: itflow
+ # Comment out build for docker.io image
+ image: lued/itflow
+ # build: .
+ restart: unless-stopped
+ depends_on:
+ - itflow-db
+ networks:
+ - wan
+ - itflow-db
+ ports:
+ - "80:8080"
+ environment:
+ - TZ=$TZ
+ - ITFLOW_NAME=ITFlow
+ - ITFLOW_URL=it.$ROOT_DOMAIN
+ - ITFLOW_PORT=8080
+ - ITFLOW_REPO=github.com/itflow-org/itflow
+ - ITFLOW_LOG_LEVEL=info
+ - ITFLOW_DB_HOST=itflow-db
+ - ITFLOW_DB_PASS=$ITFLOW_DB_PASS
+ volumes:
+ - ./itflow/:/var/www/html
+
+ itflow-db:
+ hostname: itflow-db
+ container_name: itflow-db
+ image: mariadb:10.6.11
+ restart: always
+ networks:
+ - itflow-db
+ environment:
+ - MARIADB_RANDOM_ROOT_PASSWORD=true
+ - MARIADB_DATABASE=itflow
+ - MARIADB_USER=itflow
+ - MARIADB_PASSWORD=$ITFLOW_DB_PASS
+ volumes:
+ - itflow-db:/var/lib/mysql/
\ No newline at end of file
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
new file mode 100644
index 00000000..72098dee
--- /dev/null
+++ b/docker/entrypoint.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+# Update the apache2 sites-available
+echo "
+ Order allow,deny
+ Allow from all
+ Require all granted
+
+
+ ServerName $ITFLOW_URL
+ DocumentRoot /var/www/html/
+ LogLevel $ITFLOW_LOG_LEVEL
+ ErrorLog /var/log/apache2/error.log
+ CustomLog /var/log/apache2/access.log combined
+" > /etc/apache2/sites-available/000-default.conf
+
+sed -i "s/^Listen.*/Listen $ITFLOW_PORT/g" /etc/apache2/ports.conf
+
+# if itflow is not downloaded, perform the download after the volume mounting process within dockerfile is complete.
+if [[ -f /var/www/html/index.php ]]; then
+ cd /var/www/html
+ git fetch
+else
+ git clone https://$ITFLOW_REPO /var/www/html
+fi
+
+git config --global --add safe.directory /var/www/html
+
+# Verify permissions of itflow git repository
+chown -R www-data:www-data /var/www/html
+
+# This updates the config.php file once initialization through setup.php has completed
+if [[ -f /var/www/html/config.php ]]; then
+ # Company Name
+ sed -i "s/\$config_app_name.*';/\$config_app_name = '$ITFLOW_NAME';/g" /var/www/html/config.php
+
+ # MariaDB Host
+ sed -i "s/\$dbhost.*';/\$dbhost = '$ITFLOW_DB_HOST';/g" /var/www/html/config.php
+
+ # Database Password
+ sed -i "s/\$dbpassword.*';/\$dbpassword = '$ITFLOW_DB_PASS';/g" /var/www/html/config.php
+
+ # Base URL
+ sed -i "s/\$config_base_url.*';/\$config_base_url = '$ITFLOW_URL';/g" /var/www/html/config.php
+
+ find /var/www/html -type d -exec chmod 775 {} \;
+ find /var/www/html -type f -exec chmod 664 {} \;
+ chmod 640 /var/www/html/config.php
+else
+ chmod -R 777 /var/www/html
+fi
+
+# Enable the apache2 sites-available
+service apache2 reload
+service apache2 stop
+
+# Execute the command in the dockerfile's CMD
+exec "$@"
\ No newline at end of file
diff --git a/docker/traefik-complete/.env b/docker/traefik-complete/.env
new file mode 100644
index 00000000..42a19c2b
--- /dev/null
+++ b/docker/traefik-complete/.env
@@ -0,0 +1,18 @@
+# Where you want your itflow git clone files to be stored on your host. For windows, let DOCKERDIR=.
+DOCKERDIR=/srv/containers
+
+# Cloudflare API information for traefik LetsEncrypt deployment
+CLOUDFLARE_API_KEY=
+
+CLOUDFLARE_EMAIL=
+
+# Set container timezone
+TZ=America/Edmonton
+
+# Used within the docker-compose.yml template to provide easy configuration for your domain.
+ROOT_DOMAIN=itflow.org
+
+ITFLOW_URL=demo.$ROOT_DOMAIN
+
+# Generate a random password using `docker run php:8.0-apache openssl rand -base64 32`
+ITFLOW_DB_PASS=thisisnotsecure
\ No newline at end of file
diff --git a/docker/traefik-complete/docker-compose.yml b/docker/traefik-complete/docker-compose.yml
new file mode 100644
index 00000000..29b95a74
--- /dev/null
+++ b/docker/traefik-complete/docker-compose.yml
@@ -0,0 +1,189 @@
+version: "3.9"
+########################### NETWORKS
+
+networks:
+ wan:
+ name: wan
+ driver: bridge
+
+ dockersocket:
+ name: dockersocket
+
+ itflow-db:
+ name: itflow-db
+ external: false
+
+########################### VOLUMES
+
+volumes:
+ traefik-acme:
+ itflow-db:
+
+########################### DOCKER / TRAEFIK
+
+services:
+ traefik: # Reverse Proxy & Router
+ image: traefik
+ hostname: traefik
+ container_name: traefik
+ restart: unless-stopped
+ command:
+ - --global.sendAnonymousUsage=false
+ - --entryPoints.web.address=:80
+ - --entryPoints.websecure.address=:443
+ - --entrypoints.web.http.redirections.entrypoint.to=websecure
+ - --entrypoints.web.http.redirections.entrypoint.scheme=https
+ # Allow these IPs to set the X-Forwarded-* headers - Cloudflare IPs: https://www.cloudflare.com/ips/
+ - --entrypoints.websecure.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/12,172.64.0.0/13,131.0.72.0/22
+ - --api=true
+ - --log=true
+ - --log.level=DEBUG # DEBUG, INFO, WARN, ERROR, FATAL, PANIC
+ - --providers.docker
+ - --providers.docker.endpoint=tcp://docker_proxy:2375 #unix:///var/run/docker.sock
+ - --providers.docker.defaultrule=Host(`{{ index .Labels "com.docker.compose.service" }}`)
+ - --providers.docker.exposedByDefault=false
+ - --providers.docker.network=wan
+ - --providers.docker.swarmMode=false
+ # Test acme resolution through LetsEncrypt's acme-staging-v02 URL to avoid blacklisting your IP.
+ # When ready, uncomment the line below and comment out the "acme-staging-v02" URL and uncomment the "acme-v02" URL.
+ #- --certificatesResolvers.dns-cloudflare.acme.caServer=https://acme-v02.api.letsencrypt.org/directory
+ - --certificatesResolvers.dns-cloudflare.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
+ - --certificatesResolvers.dns-cloudflare.acme.email=$CLOUDFLARE_EMAIL
+ - --certificatesResolvers.dns-cloudflare.acme.storage=/acme/acme.json
+ - --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.provider=cloudflare
+ - --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.resolvers=1.1.1.1:53,1.0.0.1:53
+ - --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.delayBeforeCheck=90
+ - --providers.file.directory=/dynamic-conf # Load dynamic configuration from one or more .toml or .yml files in a directory.
+ - --providers.file.watch=true # Only works on top level files in the rules folder
+ networks:
+ - wan
+ - dockersocket
+ security_opt:
+ - no-new-privileges:true
+ ports:
+ - 80:80
+ - 443:443
+ volumes:
+ - traefik-acme:/acme
+ - ${DOCKERDIR}/traefik:/dynamic-conf
+ environment:
+ - CF_API_EMAIL=$CLOUDFLARE_EMAIL
+ - CF_API_KEY=$CLOUDFLARE_API_KEY
+ - TZ=$TZ
+ labels:
+ - "traefik.enable=true"
+ # Middleware Rules
+ # # Basic Authentication - https://doc.traefik.io/traefik/middlewares/http/basicauth/
+ # - "traefik.http.middlewares.basic-auth.basicAuth.realm=Traefik Basic Authentication"
+ # - "traefik.http.middlewares.basic-auth.basicAuth.users=admin:$$2y$$05$$so1Qmqxf8H6iA19nmqQX1usVZblGrKBM9w3SDEqS1WmEiYUqF3mT2"
+ # # Rate Limit
+ - "traefik.http.middlewares.rate-limit.rateLimit.average=100"
+ - "traefik.http.middlewares.rate-limit.rateLimit.burst=50" # # Secure Headers
+ - "traefik.http.middlewares.secure-headers.headers.framedeny=false"
+ - "traefik.http.middlewares.secure-headers.headers.stsincludesubdomains=true"
+ - "traefik.http.middlewares.secure-headers.headers.stspreload=true"
+ - "traefik.http.middlewares.secure-headers.headers.forceSTSHeader=true"
+ - "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true"
+ - "traefik.http.middlewares.secure-headers.headers.stsseconds=63072000"
+ - "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true"
+ - "traefik.http.middlewares.secure-headers.headers.contenttypenosniff=true"
+ - "traefik.http.middlewares.secure-headers.headers.accesscontrolallowmethods=GET,POST,PUT,OPTIONS"
+ - "traefik.http.middlewares.secure-headers.headers.accesscontrolmaxage=100"
+ - "traefik.http.middlewares.secure-headers.headers.addvaryheader=true"
+ #- "traefik.http.middlewares.secure-headers.headers.contentsecuritypolicy=script-src 'self'"
+ - "traefik.http.middlewares.secure-headers.headers.referrerpolicy=origin-when-cross-origin"
+ #- "traefik.http.middlewares.secure-headers.headers.customResponseHeaders=none,noarchive,nosnippet,notranslate,noimageindex"
+ - "traefik.http.middlewares.secure-headers.headers.hostsProxyHeaders=X-Forwarded-Host"
+ # Middleware Chains
+ - "traefik.http.middlewares.chain-no-auth.chain.middlewares=rate-limit,secure-headers"
+ #- "traefik.http.middlewares.chain-basic-auth.chain.middlewares=rate-limit,secure-headers,basic-auth"
+ # Services - API
+ - "traefik.http.routers.traefik-rtr.service=api@internal"
+ # HTTP Routers
+ - "traefik.http.routers.traefik-rtr.entrypoints=websecure"
+ - "traefik.http.routers.traefik-rtr.rule=Host(`traefik.$ROOT_DOMAIN`)"
+ - "traefik.http.routers.traefik-rtr.tls=true"
+ # Setting TLS to $DOMAIN#
+ - "traefik.http.routers.traefik-rtr.tls.certResolver=dns-cloudflare"
+ - "traefik.http.routers.traefik-rtr.tls.domains[0].main=$ROOT_DOMAIN"
+ - "traefik.http.routers.traefik-rtr.tls.domains[0].sans=*.$ROOT_DOMAIN"
+
+ watchtower:
+ image: containrrr/watchtower
+ hostname: watchtower
+ container_name: watchtower
+ restart: unless-stopped
+ networks:
+ - dockersocket
+ environment:
+ DOCKER_HOST: tcp://docker_proxy:2375
+
+ docker_proxy:
+ image: tecnativa/docker-socket-proxy
+ hostname: docker_proxy
+ container_name: docker_proxy
+ restart: unless-stopped
+ networks:
+ - dockersocket
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ environment:
+ CONTAINERS: 1
+ NETWORKS: 1
+ SERVICES: 1
+ INFO: 1
+ IMAGES: 1
+ VOLUMES: 1
+ POST: 1
+
+########################### ITFLOW
+
+ itflow:
+ hostname: itflow
+ container_name: itflow
+ image: lued/itflow
+ # build: ../.
+ restart: unless-stopped
+ depends_on:
+ - itflow-db
+ networks:
+ - wan
+ - itflow-db
+ environment:
+ - TZ=$TZ
+ - ITFLOW_NAME=ITFlow
+ - ITFLOW_URL=$ITFLOW_URL
+ - ITFLOW_PORT=8080
+ - ITFLOW_REPO=github.com/itflow-org/itflow
+ - ITFLOW_LOG_LEVEL=info
+ - ITFLOW_DB_HOST=itflow-db
+ - ITFLOW_DB_PASS=$ITFLOW_DB_PASS
+ volumes:
+ - ${DOCKERDIR}/itflow/:/var/www/html
+ labels:
+ - "traefik.enable=true"
+ ## HTTP Routers
+ - "traefik.http.routers.itflow-rtr.entrypoints=websecure"
+ - "traefik.http.routers.itflow-rtr.tls=true"
+ - "traefik.http.routers.itflow-rtr.rule=Host(`$ITFLOW_URL`)"
+ ## Middlewares
+ - "traefik.http.routers.itflow-rtr.middlewares=chain-no-auth@docker"
+ ## HTTP Services
+ - "traefik.http.routers.itflow-rtr.service=itflow-svc"
+ - "traefik.http.services.itflow-svc.loadbalancer.server.port=8080"
+
+ itflow-db:
+ hostname: itflow-db
+ container_name: itflow-db
+ image: mariadb:10.6.11
+ restart: always
+ networks:
+ - itflow-db
+ environment:
+ - TZ=$TZ
+ - MARIADB_RANDOM_ROOT_PASSWORD=true
+ - MARIADB_DATABASE=itflow
+ - MARIADB_USER=itflow
+ - MARIADB_PASSWORD=$ITFLOW_DB_PASS
+ volumes:
+ - itflow-db:/var/lib/mysql/
\ No newline at end of file