├── .dockerignore ├── .github ├── dependabot.yml └── workflows │ ├── auto-merge-dependabot.yml │ └── docker.yml ├── .gitignore ├── Caddyfile.example ├── Dockerfile ├── RT_SiteConfig.pm.example ├── Readme.md ├── bash_functions.sh ├── certs └── .keep ├── cron └── .keep ├── cron_entrypoint.sh ├── crontab.example ├── dev.sh ├── docker-compose.dev.yml ├── docker-compose.yml ├── getmail └── .keep ├── getmailrc.example ├── gpg └── .keep ├── logs_prod.sh ├── msmtp.conf.example ├── msmtp └── .keep ├── prod.sh ├── restart_prod.sh ├── shredder └── .keep └── smime └── .keep /.dockerignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | *.md 3 | *.pem 4 | *.key 5 | *.yml 6 | *.example 7 | *.secret 8 | *.env 9 | .github/ 10 | .git/ 11 | certs/ 12 | cron/ 13 | getmail/ 14 | gpg/ 15 | msmtp/ 16 | shredder/ 17 | smime/ 18 | .env 19 | .keep 20 | **/.keep 21 | *.pgpass 22 | *.json 23 | !cron_entrypoint.sh 24 | crontab 25 | RT_SiteConfig.pm 26 | Caddyfile 27 | Dockerfile 28 | docker-compose.yml 29 | docker-compose.override.yml 30 | docker-compose.dev.yml 31 | .dockerignore 32 | .gitignore 33 | *.patch -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | 13 | - package-ecosystem: docker 14 | directory: "/" 15 | schedule: 16 | interval: "daily" 17 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge-dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Auto-merge dependabot updates 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | 7 | permissions: 8 | pull-requests: write 9 | contents: write 10 | 11 | jobs: 12 | 13 | dependabot-merge: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | if: ${{ github.actor == 'dependabot[bot]' }} 18 | 19 | steps: 20 | - name: Dependabot metadata 21 | id: metadata 22 | uses: dependabot/fetch-metadata@v2.4.0 23 | with: 24 | github-token: "${{ secrets.GITHUB_TOKEN }}" 25 | 26 | - name: Enable auto-merge for Dependabot PRs 27 | # Only if version bump is not a major version change 28 | if: ${{steps.metadata.outputs.update-type != 'version-update:semver-major'}} 29 | run: gh pr merge --auto --merge "$PR_URL" 30 | env: 31 | PR_URL: ${{github.event.pull_request.html_url}} 32 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 33 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker Images 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | schedule: 10 | - cron: "0 0 * * *" 11 | 12 | env: 13 | DO_PUSH: ${{ github.ref == 'refs/heads/main' }} # do not push on PRs 14 | 15 | jobs: 16 | images: 17 | timeout-minutes: 80 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: checkout sources 22 | uses: actions/checkout@v4 23 | 24 | - name: Set up QEMU 25 | uses: docker/setup-qemu-action@v3 26 | 27 | - name: Set up Docker Buildx 28 | uses: docker/setup-buildx-action@v3 29 | id: buildx 30 | with: 31 | install: true 32 | 33 | - name: Docker rt5 meta 34 | id: metart5 35 | uses: docker/metadata-action@v5 36 | with: 37 | images: firefart/requesttracker 38 | tags: | 39 | type=raw,value=5 40 | type=raw,value=5.0.8 41 | type=schedule,pattern={{date 'YYYYMMDD'}},prefix=nightly-5- 42 | 43 | - name: Docker rt6 meta 44 | id: metart6 45 | uses: docker/metadata-action@v5 46 | with: 47 | images: firefart/requesttracker 48 | tags: | 49 | type=raw,value=latest 50 | type=raw,value=6 51 | type=raw,value=6.0.0 52 | type=schedule,pattern={{date 'YYYYMMDD'}},prefix=nightly- 53 | 54 | - name: Login to Docker Hub 55 | uses: docker/login-action@v3.4.0 56 | if: env.DO_PUSH == 'true' 57 | with: 58 | username: ${{ secrets.DOCKERHUB_USERNAME }} 59 | password: ${{ secrets.DOCKERHUB_TOKEN }} 60 | 61 | - name: Build and push RT5 62 | uses: docker/build-push-action@v6 63 | with: 64 | build-args: | 65 | RT_VERSION=5.0.8 66 | context: . 67 | file: Dockerfile 68 | push: ${{ env.DO_PUSH }} 69 | tags: ${{ steps.metart5.outputs.tags }} 70 | labels: ${{ steps.metart5.outputs.labels }} 71 | 72 | - name: Build and push RT6 73 | uses: docker/build-push-action@v6 74 | with: 75 | context: . 76 | file: Dockerfile 77 | push: ${{ env.DO_PUSH }} 78 | tags: ${{ steps.metart6.outputs.tags }} 79 | labels: ${{ steps.metart6.outputs.labels }} 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | !dev.sh 3 | !prod.sh 4 | RT_SiteConfig.pm 5 | *.pem 6 | *.key 7 | TODO 8 | !dev.sh 9 | !prod.sh 10 | !logs_prod.sh 11 | !bash_functions.sh 12 | !restart_prod.sh 13 | !cron_entrypoint.sh 14 | Caddyfile 15 | crontab 16 | /certs/* 17 | !/certs/.keep 18 | msmtp/* 19 | !msmtp/.keep 20 | getmail/* 21 | !getmail/.keep 22 | gpg/* 23 | !gpg/.keep 24 | shredder/*.sql 25 | *.secret 26 | *.env 27 | .env 28 | *.pgpass 29 | *.json 30 | docker-compose.override.yml 31 | *.patch -------------------------------------------------------------------------------- /Caddyfile.example: -------------------------------------------------------------------------------- 1 | { 2 | # debug 3 | admin off 4 | auto_https off 5 | } 6 | 7 | # healthchecks 8 | :1337 { 9 | respond "OK" 200 10 | } 11 | 12 | # mailgate 13 | :8080 { 14 | log 15 | reverse_proxy rt:9000 { 16 | transport fastcgi 17 | } 18 | } 19 | 20 | # request tracker 21 | :443 { 22 | log 23 | tls /certs/pub.pem /certs/priv.pem 24 | 25 | # Block access to the unauth mail gateway endpoint 26 | # we have a seperate mailgate server for that 27 | @blocked path /REST/1.0/NoAuth/mail-gateway 28 | respond @blocked "Nope" 403 29 | 30 | reverse_proxy rt:9000 { 31 | transport fastcgi 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM debian:12-slim AS msmtp-builder 4 | 5 | ENV MSMTP_VERSION="1.8.30" 6 | ENV MSMTP_GPG_PUBLIC_KEY="2F61B4828BBA779AECB3F32703A2A4AB1E32FD34" 7 | 8 | # Install required packages 9 | RUN DEBIAN_FRONTEND=noninteractive apt-get update \ 10 | && apt-get -q -y install --no-install-recommends \ 11 | wget ca-certificates libgnutls28-dev xz-utils \ 12 | gpg dirmngr gpg-agent libgsasl-dev libsecret-1-dev \ 13 | build-essential automake libtool gettext texinfo pkg-config 14 | 15 | RUN wget -O /msmtp.tar.xz -nv https://marlam.de/msmtp/releases/msmtp-${MSMTP_VERSION}.tar.xz \ 16 | && wget -O /msmtp.tar.xz.sig -nv https://marlam.de/msmtp/releases/msmtp-${MSMTP_VERSION}.tar.xz.sig \ 17 | && gpg --keyserver hkps://keyserver.ubuntu.com --keyserver-options timeout=10 --recv-keys ${MSMTP_GPG_PUBLIC_KEY} \ 18 | && gpg --verify /msmtp.tar.xz.sig /msmtp.tar.xz \ 19 | && tar -xf /msmtp.tar.xz \ 20 | && cd /msmtp-${MSMTP_VERSION} \ 21 | && ./configure --sysconfdir=/etc \ 22 | && make \ 23 | && make install 24 | 25 | ############################################################################# 26 | 27 | FROM perl:5.40.2 AS builder 28 | 29 | ARG RT_VERSION="6.0.0" 30 | ARG RTIR_VERSION="5.0.8" 31 | 32 | ENV RT="${RT_VERSION}" 33 | ENV RTIR="${RTIR_VERSION}" 34 | ENV RT_GPG_PUBLIC_KEY="C49B372F2BF84A19011660270DF0A283FEAC80B2" 35 | 36 | ARG ADDITIONAL_CPANM_ARGS="" 37 | 38 | # use cpanm for dependencies 39 | ENV RT_FIX_DEPS_CMD="cpanm -v --no-man-pages ${ADDITIONAL_CPANM_ARGS}" 40 | # cpan non interactive mode 41 | ENV PERL_MM_USE_DEFAULT=1 42 | 43 | # Create RT user 44 | RUN groupadd -g 1000 rt && useradd -u 1000 -g 1000 -Ms /bin/bash -d /opt/rt rt 45 | 46 | # Install required packages 47 | RUN DEBIAN_FRONTEND=noninteractive apt-get update \ 48 | && apt-get -q -y install --no-install-recommends \ 49 | ca-certificates wget gnupg graphviz libssl3 zlib1g \ 50 | gpg dirmngr gpg-agent \ 51 | libgd3 libexpat1 libpq5 w3m elinks links html2text lynx openssl libgd-dev 52 | 53 | # Download and extract RT 54 | RUN mkdir -p /src \ 55 | # import RT signing key 56 | && gpg --keyserver hkps://keyserver.ubuntu.com --keyserver-options timeout=10 --recv-keys ${RT_GPG_PUBLIC_KEY} \ 57 | # download and extract RT 58 | && wget -O /src/rt.tar.gz -nv https://download.bestpractical.com/pub/rt/release/rt-${RT}.tar.gz \ 59 | && wget -O /src/rt.tar.gz.asc -nv https://download.bestpractical.com/pub/rt/release/rt-${RT}.tar.gz.asc \ 60 | && gpg --verify /src/rt.tar.gz.asc /src/rt.tar.gz \ 61 | && mkdir -p /src/rt \ 62 | && tar --strip-components=1 -C /src/rt -xzf /src/rt.tar.gz \ 63 | # download and extract RTIR 64 | && wget -O /src/rtir.tar.gz -nv https://download.bestpractical.com/pub/rt/release/RT-IR-${RTIR}.tar.gz \ 65 | && wget -O /src/rtir.tar.gz.asc -nv https://download.bestpractical.com/pub/rt/release/RT-IR-${RTIR}.tar.gz.asc \ 66 | && gpg --verify /src/rtir.tar.gz.asc /src/rtir.tar.gz \ 67 | && mkdir -p /src/rtir \ 68 | && tar --strip-components=1 -C /src/rtir -xzf /src/rtir.tar.gz 69 | 70 | # Configure RT 71 | RUN cd /src/rt \ 72 | # configure with all plugins and with the newly created user 73 | && ./configure --prefix=/opt/rt --with-db-type=Pg --enable-gpg --enable-gd --enable-graphviz --enable-smime --enable-externalauth --with-web-user=rt --with-web-group=rt --with-rt-group=rt --with-bin-owner=rt --with-libs-owner=rt 74 | 75 | # install https support for cpanm 76 | # also disable tests on net http as the live tests often fail 77 | RUN cpanm -v --no-man-pages -n install Net::HTTP \ 78 | && cpanm -v --no-man-pages install LWP::Protocol::https \ 79 | # Install Sever::Starter without tests 80 | # as they constanly fail with timeouts and thus break 81 | # the build 82 | # Also install CSS::Inliner so users can use $EmailDashboardInlineCSS 83 | && cpanm -v --no-man-pages -n install Server::Starter CSS::Inliner 84 | 85 | # Install dependencies 86 | RUN make -C /src/rt fixdeps \ 87 | && make -C /src/rt testdeps \ 88 | && make -C /src/rt install 89 | 90 | ENV PERL5LIB=/opt/rt/lib/ 91 | 92 | # install extensions and additional tools 93 | RUN true \ 94 | # https://metacpan.org/dist/RT-Extension-MergeUsers 95 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::MergeUsers \ 96 | # https://metacpan.org/dist/RT-Extension-TerminalTheme 97 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::TerminalTheme \ 98 | # https://metacpan.org/dist/RT-Extension-Announce 99 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::Announce \ 100 | # https://metacpan.org/dist/RT-Extension-Assets-Import-CSV 101 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::Assets::Import::CSV \ 102 | # https://metacpan.org/dist/RT-Extension-Import-CSV 103 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::Import::CSV \ 104 | # https://metacpan.org/dist/RT-Extension-Announce 105 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::Announce \ 106 | # https://metacpan.org/dist/RT-Extension-CommandByMail 107 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::CommandByMail \ 108 | # https://metacpan.org/dist/RT-Extension-ExtractCustomFieldValues 109 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::ExtractCustomFieldValues \ 110 | # https://metacpan.org/dist/RT-Extension-JSGantt 111 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::JSGantt \ 112 | # https://github.com/bestpractical/app-wsgetmail 113 | # https://metacpan.org/dist/App-wsgetmail 114 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} App::wsgetmail 115 | 116 | # extensions for RT 6.0.x 117 | RUN case "${RT_VERSION}" in \ 118 | "6."*) \ 119 | # https://metacpan.org/dist/RT-Extension-MandatoryOnTransition 120 | cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::MandatoryOnTransition \ 121 | # https://metacpan.org/dist/RT-Extension-ExcelFeed 122 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::ExcelFeed \ 123 | # https://metacpan.org/dist/RT-Extension-AutomaticAssignment 124 | # no tests here as it would require a database 125 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} -n RT::Extension::AutomaticAssignment \ 126 | # https://metacpan.org/dist/RT-Extension-FormTools 127 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::FormTools \ 128 | ;; \ 129 | # older versions for RT 5.0.x 130 | "5."*) \ 131 | # https://metacpan.org/dist/RT-Extension-MandatoryOnTransition 132 | cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::MandatoryOnTransition~">= 0.0000, < 1.0000" \ 133 | # https://metacpan.org/dist/RT-Extension-ExcelFeed 134 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::ExcelFeed~">= 0.0000, < 1.0000" \ 135 | # https://metacpan.org/dist/RT-Extension-AutomaticAssignment 136 | # no tests here as it would require a database 137 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} -n RT::Extension::AutomaticAssignment~">= 1.0000, < 2.0000" \ 138 | # https://metacpan.org/dist/RT-Extension-FormTools 139 | && cpanm -v --install --no-man-pages ${ADDITIONAL_CPANM_ARGS} RT::Extension::FormTools~">= 1.0000, < 2.0000" \ 140 | ;; \ 141 | esac 142 | 143 | # Configure RTIR (only compatible with RT 5.x at the moment) 144 | RUN case "${RT_VERSION}" in "5."*) \ 145 | cd /src/rtir \ 146 | && perl -I /src/rtir/lib Makefile.PL --defaultdeps \ 147 | && make install \ 148 | ;; \ 149 | esac 150 | 151 | ############################################################################# 152 | 153 | FROM perl:5.40.2-slim 154 | LABEL org.opencontainers.image.authors="firefart " 155 | LABEL org.opencontainers.image.title="Request Tracker" 156 | LABEL org.opencontainers.image.source="https://github.com/firefart/rt-docker" 157 | LABEL org.opencontainers.image.description="Request Tracker Docker Setup" 158 | 159 | # Install required packages 160 | RUN DEBIAN_FRONTEND=noninteractive apt-get update \ 161 | && apt-get -q -y install --no-install-recommends \ 162 | procps spawn-fcgi ca-certificates getmail6 wget curl gnupg graphviz libssl3 \ 163 | zlib1g libgd3 libexpat1 libpq5 w3m elinks links html2text lynx openssl cron bash \ 164 | libfcgi-bin libgsasl18 libsecret-1-0 tzdata \ 165 | && apt-get clean \ 166 | && rm -rf /var/lib/apt/lists/* 167 | # msmtp - disabled for now to use the newer version 168 | 169 | # Create RT user 170 | RUN useradd -u 1000 -Ms /bin/bash -d /opt/rt rt 171 | 172 | # copy msmtp 173 | COPY --from=msmtp-builder /usr/local/bin/msmtp /usr/bin/msmtp 174 | COPY --from=msmtp-builder /usr/local/share/locale /usr/local/share/locale 175 | 176 | # copy all needed stuff from the builder image 177 | COPY --from=builder /usr/local/lib/perl5 /usr/local/lib/perl5 178 | COPY --from=builder /opt/rt /opt/rt 179 | # run a final dependency check if we copied all 180 | RUN perl /opt/rt/sbin/rt-test-dependencies --with-pg --with-fastcgi --with-gpg --with-graphviz --with-gd 181 | 182 | # make backwards compatible as we changed the folder to not contain the version info any more 183 | # remove after some time 184 | RUN ln -s /opt/rt /opt/rt5 185 | 186 | # msmtp config 187 | RUN mkdir /msmtp \ 188 | && chown rt:rt /msmtp \ 189 | # also fake sendmail for cronjobs 190 | && ln -s /usr/bin/msmtp /usr/sbin/sendmail 191 | 192 | # getmail 193 | RUN mkdir -p /getmail \ 194 | && chown rt:rt /getmail 195 | 196 | # gpg 197 | RUN mkdir -p /opt/rt/var/data/gpg \ 198 | && chown rt:rt /opt/rt/var/data/gpg 199 | 200 | # smime 201 | RUN mkdir -p /opt/rt/var/data/smime \ 202 | && chown rt:rt /opt/rt/var/data/smime 203 | 204 | # shredder dir 205 | RUN mkdir -p /opt/rt/var/data/RT-Shredder \ 206 | && chown rt:rt /opt/rt/var/data/RT-Shredder 207 | 208 | # RTIR Database stuff for setup 209 | COPY --chown=rt:rt --from=builder /src/rtir/etc /opt/rtir 210 | 211 | # wsgetmail 212 | COPY --chown=rt:rt --from=builder /usr/local/bin/wsgetmail /usr/local/bin/wsgetmail 213 | 214 | # remove default cron jobs 215 | RUN rm -f /etc/cron.d/* \ 216 | && rm -f /etc/cron.daily/* \ 217 | && rm -f /etc/cron.hourly/* \ 218 | && rm -f /etc/cron.monthly/* \ 219 | && rm -f /etc/cron.weekly/* \ 220 | && rm -f /var/spool/cron/crontabs/* 221 | 222 | COPY --chown=root:root --chmod=0700 cron_entrypoint.sh /root/cron_entrypoint.sh 223 | 224 | # update PATH 225 | ENV PATH="${PATH}:/opt/rt/sbin:/opt/rt/bin" 226 | 227 | EXPOSE 9000 228 | 229 | USER rt 230 | WORKDIR /opt/rt/ 231 | 232 | # spawn-fcgi v1.6.4 (ipv6) - spawns FastCGI processes 233 | 234 | # Options: 235 | # -f filename of the fcgi-application (deprecated; ignored if 236 | # is given; needs /bin/sh) 237 | # -d chdir to directory before spawning 238 | # -a
bind to IPv4/IPv6 address (defaults to 0.0.0.0) 239 | # -p bind to TCP-port 240 | # -s bind to Unix domain socket 241 | # -M change Unix domain socket mode (octal integer, default: allow 242 | # read+write for user and group as far as umask allows it) 243 | # -C (PHP only) numbers of childs to spawn (default: not setting 244 | # the PHP_FCGI_CHILDREN environment variable - PHP defaults to 0) 245 | # -F number of children to fork (default 1) 246 | # -b backlog to allow on the socket (default 1024) 247 | # -P name of PID-file for spawned process (ignored in no-fork mode) 248 | # -n no fork (for daemontools) 249 | # -v show version 250 | # -?, -h show this help 251 | # (root only) 252 | # -c chroot to directory 253 | # -S create socket before chroot() (default is to create the socket 254 | # in the chroot) 255 | # -u change to user-id 256 | # -g change to group-id (default: primary group of user if -u 257 | # is given) 258 | # -U change Unix domain socket owner to user-id 259 | # -G change Unix domain socket group to group-id 260 | CMD [ "/usr/bin/spawn-fcgi", "-d", "/opt/rt/", "-p" ,"9000", "-a","0.0.0.0", "-u", "1000", "-n", "--", "/opt/rt/sbin/rt-server.fcgi" ] 261 | 262 | HEALTHCHECK --interval=10s --timeout=3s --start-period=10s --retries=3 CMD REQUEST_METHOD=GET REQUEST_URI=/ SCRIPT_NAME=/ cgi-fcgi -connect localhost:9000 -bind || exit 1 263 | -------------------------------------------------------------------------------- /RT_SiteConfig.pm.example: -------------------------------------------------------------------------------- 1 | ### Base configuration ### 2 | Set($rtname, 'rt'); 3 | Set($WebDomain, 'localhost'); 4 | Set($WebPort, 443); 5 | Set($CanonicalizeRedirectURLs, 1); 6 | Set($CanonicalizeURLsInFeeds, 1); 7 | 8 | Plugin('RT::Extension::MergeUsers'); 9 | Plugin('RT::Extension::TerminalTheme'); 10 | 11 | ### Database connection ### 12 | Set($DatabaseType, 'Pg' ); 13 | Set($DatabaseHost, 'db'); 14 | Set($DatabasePort, '5432'); 15 | Set($DatabaseUser, 'rt'); 16 | Set($DatabasePassword, 'password'); 17 | Set($DatabaseName, 'rt'); 18 | Set($DatabaseAdmin, "rt"); 19 | 20 | Set($SendmailPath, '/usr/bin/msmtp'); 21 | 22 | ### GnuPG configuration ### 23 | Set(%GnuPG, 24 | Enable => 1, 25 | GnuPG => 'gpg', 26 | Passphrase => undef, 27 | OutgoingMessagesFormat => 'RFC' 28 | ); 29 | 30 | Set(%GnuPGOptions, 31 | homedir => '/opt/rt/var/data/gpg', 32 | passphrase => 'PASSPHRASE', 33 | keyserver => 'hkps://keys.openpgp.org', 34 | 'keyserver-options' => 'auto-key-retrieve timeout=20', 35 | 'auto-key-locate' => 'keyserver', 36 | ); 37 | 38 | ### SMIME configuration ### 39 | Set(%SMIME, 40 | Enable => 1, 41 | AcceptUntrustedCAs => 1, 42 | OpenSSL => '/usr/bin/openssl', 43 | Keyring => '/opt/rt/var/data/smime', 44 | CAPath => '/opt/rt/var/data/smime/signing-ca.pem', 45 | Passphrase => { 46 | 'user@user.com' => 'PASSPHRASE', 47 | '' => 'fallback', 48 | }, 49 | ); 50 | 51 | 1; 52 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Request Tracker with Docker 2 | 3 | This is a complete setup for [Request Tracker](https://bestpractical.com/request-tracker) with docker and docker compose. The production setup assumes you have an external postgres database and an external SMTP server for outgoing emails. A local database server is only started in the dev configuration. 4 | 5 | The prebuilt image is available from [https://hub.docker.com/r/firefart/requesttracker](https://hub.docker.com/r/firefart/requesttracker). The image is rebuilt on a daily basis. 6 | 7 | The [Request Tracker for Incident Response (RT-IR)](https://bestpractical.com/rtir) Extension is also installed. 8 | 9 | ## Prerequisites 10 | 11 | - [Docker](https://docs.docker.com/get-docker/) with the `compose` plugin 12 | - an external SMTP server to send emails 13 | - an external IMAP server to receive emails from 14 | - an external Postgres database 15 | 16 | ## Instruction 17 | 18 | To start use either `./dev.sh` which builds the images locally or `./prod.sh` which uses the prebuilt ones from docker hub. Before running this you also need to add the required configuration files (see Configuration). 19 | 20 | ## Configuration 21 | 22 | The following configuration files need to be present before starting: 23 | 24 | - `RT_SiteConfig.pm` : RTs main configuration file. This needs to be present in the root of the dir. See `RT_SiteConfig.pm.example` for an example configration and the needed paths and settings for this configuration. For a full config reference have a look at the [official documentation](https://docs.bestpractical.com/rt/latest/RT_Config.html). 25 | - `Caddyfile`: The webserver config. See `Caddyfile.example` for an example and the [official Caddy doc](https://caddyserver.com/docs/caddyfile) for a reference. 26 | - `./msmtp/msmtp.conf` : config for msmtp (outgoing email). See `msmtp.conf.example` for an example. The `./msmtp` folder is also mounted to `/msmtp/` in the container so you can load certificates from the config file. [MSMTP Configuration Guide](https://marlam.de/msmtp/msmtp.html) 27 | - `crontab` : Crontab file that will be run as the RT user. See contab.example for an example. Crontab output will be sent to the MAILTO address (it uses the msmtp config). You can use [crontab guru](https://crontab.guru/) for help with the format. 28 | - `./getmail/getmailrc`: This file configures your E-Mail fetching. See `getmailrc.example` for an example. `getmail` configuration docs are available under [https://getmail6.org/configuration.html](https://getmail6.org/configuration.html). The configuration options for `rt-mailgate` which is used to store the emails in request tracker can be viewed under [https://docs.bestpractical.com/rt/latest/rt-mailgate.html](https://docs.bestpractical.com/rt/latest/rt-mailgate.html). 29 | 30 | Additional configs: 31 | 32 | - `./certs/`: This folder should contain all optional certificates needed for caddy 33 | - `./gpg/` : This folder should contain the gpg keyring if used in rt. Be sure to chmod the files to user 1000 with 0600 so RT will not complain. 34 | - `./smime/` : This folder should contain the SMIME certificate if configured in RT 35 | - `./shredder/` : This directory will be used by the shredder functionality [https://docs.bestpractical.com/rt/latest/RT/Shredder.html](https://docs.bestpractical.com/rt/latest/RT/Shredder.html) so the backups are stored on the host 36 | 37 | For output of your crontabs you can use the `/cron` directory so the output will be available on the host. 38 | 39 | In the default configuration all output from RT, caddy, getmail and msmtp is available via `docker logs` (or `docker compose -f ... logs`). 40 | 41 | ## Webserver 42 | 43 | The setup uses Caddy as a webserver. You can find an example configuration in [Caddyfile.example](Caddyfile.example). Caddy provides features like auto https with lets encrypt and more stuff that makes it easy to set up. You can find the Caddy documentation here [https://caddyserver.com/docs/caddyfile](https://caddyserver.com/docs/caddyfile). 44 | 45 | Feel free to modify the config to your needs like auto https, certificate based authentication, basic authentication and so on. Just be sure the mailgateway host under port `:8080` is untouched and the main host contains a block for the unauth API path, otherwise everyone with access to your RT instance can create emails without the need to log in first. 46 | 47 | ### Create Certificate 48 | 49 | If you don't want to use the auto https feature (for example in dev) you can provide your own certificates. 50 | 51 | Create a self signed certificate: 52 | 53 | ```bash 54 | openssl req -x509 -newkey rsa:4096 -keyout ./certs/priv.pem -out ./certs/pub.pem -days 3650 -nodes 55 | ``` 56 | 57 | ### Example Caddy Configurations 58 | 59 |
60 | Caddy on a domain with lets encrypt certificates 61 | 62 | ``` 63 | { 64 | admin off 65 | } 66 | 67 | # healthchecks 68 | :1337 { 69 | respond "OK" 200 70 | } 71 | 72 | # mailgate 73 | :8080 { 74 | log 75 | reverse_proxy rt:9000 { 76 | transport fastcgi 77 | } 78 | } 79 | 80 | # request tracker 81 | rt.domain.com:443 { 82 | log 83 | tls user@email.com 84 | 85 | # Block access to the unauth mail gateway endpoint 86 | # we have a seperate mailgate server for that 87 | @blocked path /REST/1.0/NoAuth/mail-gateway 88 | respond @blocked "Nope" 403 89 | 90 | reverse_proxy rt:9000 { 91 | transport fastcgi 92 | } 93 | } 94 | ``` 95 | 96 |
97 | 98 |
99 | Caddy behind a reverse proxy server with a self signed certificate 100 | 101 | `pub.pem` and `priv.pem` need to be inside the `./certs` folder and will be mounted automatically. 102 | 103 | ``` 104 | { 105 | admin off 106 | auto_https off 107 | 108 | servers { 109 | trusted_proxies static 10.0.0.0/22 110 | client_ip_headers X-Orig-Addr 111 | trusted_proxies_strict 112 | } 113 | } 114 | 115 | # healthchecks 116 | :1337 { 117 | respond "OK" 200 118 | } 119 | 120 | # mailgate 121 | :8080 { 122 | log 123 | reverse_proxy rt:9000 { 124 | transport fastcgi 125 | } 126 | } 127 | 128 | # request tracker 129 | :443 { 130 | log 131 | 132 | tls /certs/pub.pem /certs/priv.pem 133 | 134 | # Block access to the unauth mail gateway endpoint 135 | # we have a seperate mailgate server for that 136 | @blocked path /REST/1.0/NoAuth/mail-gateway 137 | respond @blocked "Nope" 403 138 | 139 | reverse_proxy rt:9000 { 140 | transport fastcgi { 141 | env SERVER_NAME {http.request.header.X-Orig-HostHeader} 142 | } 143 | } 144 | } 145 | ``` 146 | 147 |
148 | 149 |
150 | Caddy behind a reverse proxy server with a self signed certificate and client certificate validation 151 | 152 | `pub.pem`, `priv.pem` and `root-ca.pem` need to be inside the `./certs` folder and will be mounted automatically. 153 | 154 | ``` 155 | { 156 | admin off 157 | auto_https off 158 | 159 | servers { 160 | trusted_proxies static 10.0.0.0/22 161 | client_ip_headers X-Orig-Addr 162 | trusted_proxies_strict 163 | } 164 | } 165 | 166 | # healthchecks 167 | :1337 { 168 | respond "OK" 200 169 | } 170 | 171 | # mailgate 172 | :8080 { 173 | log 174 | reverse_proxy rt:9000 { 175 | transport fastcgi 176 | } 177 | } 178 | 179 | # request tracker 180 | :443 { 181 | log 182 | 183 | tls /certs/pub.pem /certs/priv.pem { 184 | protocols tls1.3 185 | client_auth { 186 | mode require_and_verify 187 | trust_pool file /certs/root-ca.pem 188 | } 189 | } 190 | 191 | # Block access to the unauth mail gateway endpoint 192 | # we have a seperate mailgate server for that 193 | @blocked path /REST/1.0/NoAuth/mail-gateway 194 | respond @blocked "Nope" 403 195 | 196 | reverse_proxy rt:9000 { 197 | transport fastcgi { 198 | env SERVER_NAME {http.request.header.X-Orig-HostHeader} 199 | } 200 | } 201 | } 202 | ``` 203 | 204 |
205 | 206 |
207 | Caddy behind a reverse proxy server with a self signed certificate and client certificate validation with subject validation 208 | 209 | `pub.pem`, `priv.pem` and `root-ca.pem` need to be inside the `./certs` folder and will be mounted automatically. 210 | 211 | ``` 212 | { 213 | admin off 214 | auto_https off 215 | 216 | servers { 217 | trusted_proxies static 10.0.0.0/22 218 | client_ip_headers X-Orig-Addr 219 | trusted_proxies_strict 220 | } 221 | } 222 | 223 | # healthchecks 224 | :1337 { 225 | respond "OK" 200 226 | } 227 | 228 | # mailgate 229 | :8080 { 230 | log 231 | reverse_proxy rt:9000 { 232 | transport fastcgi 233 | } 234 | } 235 | 236 | # request tracker 237 | :443 { 238 | @cert-auth { 239 | expression {http.request.tls.client.subject} == "CN=Subject,OU=example,O=com,C=xxx" 240 | } 241 | 242 | log 243 | 244 | tls /certs/pub.pem /certs/priv.pem { 245 | protocols tls1.3 246 | client_auth { 247 | mode require_and_verify 248 | trust_pool file /certs/root-ca.pem 249 | } 250 | } 251 | 252 | # block everything that is not from a trusted ip range 253 | @blocked_trusted not remote_ip 10.0.0.0/22 254 | respond @blocked_trusted "Nope" 403 255 | 256 | # Block access to the unauth mail gateway endpoint 257 | # we have a seperate mailgate server for that 258 | @blocked path /REST/1.0/NoAuth/mail-gateway 259 | respond @blocked "Nope" 403 260 | 261 | reverse_proxy @cert-auth rt:9000 { 262 | transport fastcgi { 263 | env SERVER_NAME {http.request.header.X-Orig-HostHeader} 264 | } 265 | } 266 | } 267 | ``` 268 | 269 |
270 | 271 |
272 | Caddy behind a reverse proxy server with a self signed certificate and client certificate validation with subject validation and on a subpath 273 | 274 | `pub.pem`, `priv.pem` and `root-ca.pem` need to be inside the `./certs` folder and will be mounted automatically. The reverse proxy needs to point to `servername/rt` otherwise you will end up with wrong paths in the cookies which will lead to file uploads not working correctly. 275 | We will also set the REMOTE_USER to a custom header sent from the upstream proxy. 276 | 277 | ``` 278 | { 279 | admin off 280 | auto_https off 281 | 282 | servers { 283 | trusted_proxies static 10.0.0.0/22 284 | client_ip_headers X-Orig-Addr 285 | trusted_proxies_strict 286 | } 287 | } 288 | 289 | # healthchecks 290 | :1337 { 291 | respond "OK" 200 292 | } 293 | 294 | # mailgate 295 | :8080 { 296 | log 297 | reverse_proxy rt:9000 { 298 | transport fastcgi 299 | } 300 | } 301 | 302 | # request tracker 303 | :443 { 304 | @cert-auth { 305 | expression {http.request.tls.client.subject} == "CN=Subject,OU=example,O=com,C=xxx" 306 | } 307 | 308 | log 309 | tls /certs/pub.pem /certs/priv.pem { 310 | protocols tls1.3 311 | client_auth { 312 | mode require_and_verify 313 | trust_pool file /certs/root-ca.pem 314 | } 315 | } 316 | 317 | # block everything that is not from a trusted ip range 318 | @blocked_trusted not remote_ip 10.0.0.0/22 319 | respond @blocked_trusted "Nope" 403 320 | 321 | handle_path /rt/* { 322 | # Block access to the unauth mail gateway endpoint 323 | # we have a seperate mailgate server for that 324 | @blocked path /REST/1.0/NoAuth/mail-gateway 325 | respond @blocked "Nope" 403 326 | 327 | reverse_proxy @cert-auth rt:9000 { 328 | transport fastcgi { 329 | env REMOTE_USER {http.request.header.X-Auth-Username} 330 | env SERVER_NAME {http.request.header.X-Orig-HostHeader} 331 | env REQUEST_URI {uri} 332 | } 333 | } 334 | } 335 | } 336 | ``` 337 | 338 |
339 | 340 | ## Init database 341 | 342 | This initializes a fresh database. This is needed on the first run. 343 | 344 | ```bash 345 | docker compose run --rm rt bash -c 'cd /opt/rt && perl ./sbin/rt-setup-database --action init' 346 | ``` 347 | 348 | You need to restart the rt service after this step as it crashes if the database is not initialized. 349 | 350 | ### DEV 351 | 352 | Hint: Add `--skip-create` in dev as the database is created by docker 353 | 354 | ```bash 355 | docker compose -f docker-compose.yml -f docker-compose.dev.yml run --rm rt bash -c 'cd /opt/rt && perl ./sbin/rt-setup-database --action init --skip-create' 356 | ``` 357 | 358 | ## Upgrade steps 359 | 360 | ### Upgrade Database 361 | 362 | ```bash 363 | docker compose run --rm rt bash -c 'cd /opt/rt && perl ./sbin/rt-setup-database --action upgrade --upgrade-from 4.4.4' 364 | ``` 365 | 366 | ### Fix data inconsistencies 367 | 368 | Run multiple times with the `--resolve` switch until no errors occur 369 | 370 | ```bash 371 | docker compose run --rm rt bash -c 'cd /opt/rt && perl ./sbin/rt-validator --check --resolve' 372 | ``` 373 | 374 | ## RT-IR 375 | 376 | You can simply enable RT-IR in your `RT_SiteConfig.pm` by including `Plugin('RT::IR');`. Please refer to the [docs](https://docs.bestpractical.com/rtir/latest/index.html) for additional install or upgrade steps. 377 | 378 | To initialize the database (ONLY ON THE FIRST RUN!!!! and only after rt is fully set up) 379 | 380 | ```bash 381 | docker compose run --rm rt bash -c 'cd /opt/rt && perl ./sbin/rt-setup-database --action insert --skip-create --datafile /opt/rtir/initialdata' 382 | ``` 383 | 384 | To upgrade 385 | 386 | ```bash 387 | docker compose run --rm rt bash -c 'cd /opt/rt && perl ./sbin/rt-setup-database --action upgrade --skip-create --datadir /opt/rtir/upgrade --package RT::IR --ext-version 5.0.4' 388 | ``` 389 | 390 | Restart docker setup after all steps to fully load RT-IR (just run `./restart_prod.sh`). 391 | 392 | ## Extending 393 | 394 | To include additional containers in this setup like pgadmin or change a default config, you can create a `docker-compose.override.yml` file in the projects root and it will automatically picked up and merged with the default config. Run `docker compose config` to view the merged config. 395 | 396 | ## Deprecated features 397 | 398 | - NGINX: The old setup used nginx for the webserver. If you want to upgrade you need to migrate your nginx config to a Caddy config. See the example Caddy Configuration section for some ideas. 399 | - compose profiles: Previously there were compose profile to also include `dozzle` for viewing logs and `pgadmin` to interact with the database. Both tools are now removed and `pgadmin` is only available in dev mode. If you still need pgadmin you can easily spin it up using docker compose. 400 | -------------------------------------------------------------------------------- /bash_functions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euf -o pipefail 4 | 5 | function check_files() { 6 | # check for needed config files 7 | # these are mounted using docker-compose and are 8 | # required by the setup 9 | if [ ! -f ./RT_SiteConfig.pm ]; then 10 | echo "RT_SiteConfig.pm does not exist. Please see RT_SiteConfig.pm.example for an example configuration." 11 | exit 1 12 | fi 13 | 14 | if [ ! -f ./Caddyfile ]; then 15 | echo "Caddyfile does not exist. Please see Caddyfile.example for an example configuration." 16 | exit 1 17 | fi 18 | 19 | if [ ! -f ./msmtp/msmtp.conf ]; then 20 | echo "./msmtp/msmtp.conf does not exist. Please see msmtp.conf.example for an example configuration." 21 | exit 1; 22 | fi 23 | 24 | if [ ! -f ./crontab ]; then 25 | echo "./crontab does not exist. Please see crontab.example for an example configuration." 26 | exit 1 27 | fi 28 | 29 | if [ ! -f ./getmail/getmailrc ]; then 30 | echo "./getmail/getmailrc does not exist. Please see getmailrc.example for an example configuration." 31 | exit 1 32 | fi 33 | } 34 | 35 | function check_dev_files() { 36 | if [ ! -f ./pgadmin_password.secret ]; then 37 | echo "./pgadmin_password.secret does not exist. Please set a password." 38 | exit 1 39 | fi 40 | 41 | if [ ! -f ./certs/pub.pem ]; then 42 | echo "./certs/pub.pem does not exist. Please see Readme.md if you want to create a self signed certificate." 43 | exit 1 44 | fi 45 | 46 | if [ ! -f ./certs/priv.pem ]; then 47 | echo "./certs/priv.pem does not exist. Please see Readme.md if you want to create a self signed certificate." 48 | exit 1 49 | fi 50 | } 51 | 52 | function fix_file_perms() { 53 | # needed for the gpg and smime stuff 54 | # id 1000 is the rt user inside the docker image 55 | chown -R 1000:1000 ./cron 56 | chown -R 1000:1000 ./gpg 57 | chown -R 1000:1000 ./smime 58 | chown -R 1000:1000 ./shredder 59 | 60 | chmod 0700 ./cron 61 | chmod 0700 ./gpg 62 | chmod 0700 ./smime 63 | chmod 0700 ./shredder 64 | 65 | find ./cron -type f -exec chmod 0600 {} \; 66 | find ./gpg -type f -exec chmod 0600 {} \; 67 | find ./smime -type f -exec chmod 0600 {} \; 68 | find ./shredder -type f -exec chmod 0600 {} \; 69 | } 70 | -------------------------------------------------------------------------------- /certs/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefart/rt-docker/1bc254792b1e02ce106c8b7dc02525db5292de1a/certs/.keep -------------------------------------------------------------------------------- /cron/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefart/rt-docker/1bc254792b1e02ce106c8b7dc02525db5292de1a/cron/.keep -------------------------------------------------------------------------------- /cron_entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # workaournd as docker configs are mounted and do not support the uid parameter: 4 | # https://github.com/docker/compose/issues/9648 5 | 6 | # cron.d files need to have special permissions 7 | cp /crontab /etc/cron.d/crontab 8 | chown root:root /etc/cron.d/crontab 9 | chmod 0644 /etc/cron.d/crontab 10 | 11 | /usr/sbin/cron -f 12 | -------------------------------------------------------------------------------- /crontab.example: -------------------------------------------------------------------------------- 1 | # do NOT use quotes here! 2 | MAILTO=user@user.com 3 | 4 | SHELL=/bin/sh 5 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 6 | 7 | # Example of job definition: 8 | # .---------------- minute (0 - 59) 9 | # | .------------- hour (0 - 23) 10 | # | | .---------- day of month (1 - 31) 11 | # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... 12 | # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat 13 | # | | | | | 14 | # * * * * * user-name command to be executed 15 | 16 | # * * * * * root date >/proc/1/fd/1 2>/proc/1/fd/2 17 | 18 | # clean sessions 19 | 0 0 * * * rt /opt/rt/sbin/rt-clean-sessions 20 | 21 | # refresh full text index 22 | 0 * * * * rt /opt/rt/sbin/rt-fulltext-indexer --quiet 2>&1 | grep -v "Words longer than 2047 characters are ignored" | grep -v "word is too long to be indexed" 23 | 24 | # get mails 25 | * * * * * rt /usr/bin/getmail --rcfile=getmailrc -g /getmail 26 | 27 | # email dashboards 28 | 0 * * * * rt /opt/rt/sbin/rt-email-dashboards 29 | -------------------------------------------------------------------------------- /dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euf -o pipefail 4 | 5 | export DOCKER_BUILDKIT=1 6 | export COMPOSE_DOCKER_CLI_BUILD=1 7 | 8 | DIR="${BASH_SOURCE%/*}" 9 | if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi 10 | . "$DIR/bash_functions.sh" 11 | 12 | check_files 13 | check_dev_files 14 | 15 | fix_file_perms 16 | 17 | docker compose -f docker-compose.yml -f docker-compose.dev.yml stop 18 | docker compose -f docker-compose.yml -f docker-compose.dev.yml rm -f 19 | docker compose -f docker-compose.yml -f docker-compose.dev.yml --progress=plain build --pull 20 | docker compose -f docker-compose.yml -f docker-compose.dev.yml up --remove-orphans 21 | -------------------------------------------------------------------------------- /docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | services: 2 | rt: 3 | build: 4 | args: 5 | ADDITIONAL_CPANM_ARGS: "-n" # disable tests in dev to speed up builds 6 | restart: "no" 7 | deploy: 8 | mode: replicated 9 | replicas: 1 10 | depends_on: 11 | db: 12 | condition: service_healthy 13 | restart: true 14 | 15 | cron: 16 | build: 17 | args: 18 | ADDITIONAL_CPANM_ARGS: "-n" # disable tests in dev to speed up builds 19 | restart: "no" 20 | depends_on: 21 | db: 22 | condition: service_healthy 23 | restart: true 24 | 25 | caddy: 26 | restart: "no" 27 | 28 | db: 29 | image: postgres:latest 30 | restart: "no" 31 | environment: 32 | POSTGRES_DB: rt 33 | POSTGRES_USER: rt 34 | POSTGRES_PASSWORD: password 35 | healthcheck: 36 | test: [ "CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}" ] 37 | interval: 30s 38 | timeout: 10s 39 | retries: 5 40 | start_period: 60s 41 | volumes: 42 | - vol_db:/var/lib/postgresql/data 43 | ports: 44 | - "127.0.0.1:5432:5432" 45 | networks: 46 | - net 47 | 48 | pgadmin: 49 | image: dpage/pgadmin4:latest 50 | restart: "no" 51 | ports: 52 | - "127.0.0.1:8888:80" 53 | environment: 54 | PGADMIN_LISTEN_ADDRESS: 0.0.0.0 55 | PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-root@root.com} 56 | PGADMIN_DEFAULT_PASSWORD_FILE: /run/secrets/pgadmin_password 57 | PGADMIN_DISABLE_POSTFIX: disable 58 | healthcheck: 59 | test: [ "CMD", "wget", "-O", "-", "http://127.0.0.1:80/misc/ping" ] 60 | interval: 10s 61 | timeout: 10s 62 | start_period: 160s 63 | retries: 3 64 | depends_on: 65 | db: 66 | condition: service_healthy 67 | restart: true 68 | secrets: 69 | - pgadmin_password 70 | volumes: 71 | - vol_pgadmin:/var/lib/pgadmin 72 | networks: 73 | - net 74 | 75 | secrets: 76 | pgadmin_password: 77 | file: ./pgadmin_password.secret 78 | 79 | volumes: 80 | vol_pgadmin: 81 | vol_db: 82 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | x-app: &default-app 2 | build: 3 | context: . 4 | image: firefart/requesttracker:latest 5 | restart: unless-stopped 6 | configs: 7 | - source: rt_site_config 8 | target: /opt/rt/etc/RT_SiteConfig.pm 9 | - source: msmtp 10 | target: /etc/msmtprc 11 | - source: getmail 12 | target: /getmail/getmailrc 13 | - source: crontab 14 | target: /crontab # needed as docker compose mounts the config as a bind mount and the uid parameter is not working here 15 | volumes: 16 | - ./msmtp/:/msmtp:ro 17 | - ./gpg/:/opt/rt/var/data/gpg 18 | - ./smime/:/opt/rt/var/data/smime:ro 19 | - ./shredder/:/opt/rt/var/data/RT-Shredder 20 | - /etc/localtime:/etc/localtime:ro 21 | - ./cron/:/cron 22 | # make the host available inside the image 23 | extra_hosts: 24 | - "host.docker.internal:host-gateway" 25 | networks: 26 | - net 27 | 28 | services: 29 | rt: 30 | <<: *default-app 31 | hostname: rt 32 | deploy: 33 | mode: replicated 34 | replicas: 5 35 | endpoint_mode: vip 36 | 37 | cron: 38 | <<: *default-app 39 | hostname: cron 40 | # the cron daemon needs to run as root 41 | user: root 42 | command: ["/root/cron_entrypoint.sh"] 43 | # disable the healthcheck from the main dockerfile 44 | healthcheck: 45 | test: ["CMD", "pidof", "cron"] 46 | interval: 10s 47 | timeout: 10s 48 | retries: 3 49 | depends_on: 50 | # needs to be up so we can use mailgate from the cron container 51 | rt: 52 | condition: service_healthy 53 | restart: true 54 | # we send rt-mailgate over to caddy 55 | caddy: 56 | condition: service_healthy 57 | restart: true 58 | 59 | caddy: 60 | image: caddy:latest 61 | hostname: caddy 62 | restart: unless-stopped 63 | ports: 64 | - "0.0.0.0:443:443" 65 | - "127.0.0.1:8080:8080" # expose mailgate vhost to host 66 | configs: 67 | - source: caddyfile 68 | target: /etc/caddy/Caddyfile 69 | volumes: 70 | - ./certs/:/certs/:ro 71 | - /etc/localtime:/etc/localtime:ro 72 | - vol_caddy_data:/data 73 | - vol_caddy_config:/config 74 | healthcheck: 75 | test: ["CMD", "wget", "-O", "-", "-q", "http://127.0.0.1:1337/"] 76 | interval: 10s 77 | timeout: 10s 78 | retries: 3 79 | depends_on: 80 | rt: 81 | condition: service_healthy 82 | restart: true 83 | networks: 84 | - net 85 | 86 | configs: 87 | caddyfile: 88 | file: ./Caddyfile 89 | rt_site_config: 90 | file: ./RT_SiteConfig.pm 91 | msmtp: 92 | file: ./msmtp/msmtp.conf 93 | getmail: 94 | file: ./getmail/getmailrc 95 | crontab: 96 | file: ./crontab 97 | 98 | networks: 99 | net: 100 | driver: bridge 101 | driver_opts: 102 | com.docker.network.bridge.name: br_rt 103 | 104 | volumes: 105 | vol_caddy_data: 106 | vol_caddy_config: 107 | -------------------------------------------------------------------------------- /getmail/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefart/rt-docker/1bc254792b1e02ce106c8b7dc02525db5292de1a/getmail/.keep -------------------------------------------------------------------------------- /getmailrc.example: -------------------------------------------------------------------------------- 1 | [retriever] 2 | type = SimpleIMAPSSLRetriever 3 | server = mail.host.com 4 | username = user@domain.com 5 | password = pass 6 | mailboxes = ("INBOX",) 7 | 8 | [destination] 9 | type = MDA_external 10 | path = /opt/rt/bin/rt-mailgate 11 | user = rt 12 | group = rt 13 | # 8080 is the mailgate vhost 14 | arguments = ("--url", "http://caddy:8080/", "--queue", "general", "--action", "correspond",) 15 | 16 | [options] 17 | read_all = false 18 | delete = true 19 | verbose = 0 20 | -------------------------------------------------------------------------------- /gpg/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefart/rt-docker/1bc254792b1e02ce106c8b7dc02525db5292de1a/gpg/.keep -------------------------------------------------------------------------------- /logs_prod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker compose logs -f --tail=100 4 | -------------------------------------------------------------------------------- /msmtp.conf.example: -------------------------------------------------------------------------------- 1 | defaults 2 | 3 | account smtp 4 | host smtp.office365.com 5 | port 587 6 | tls on 7 | tls_starttls on 8 | from test@test.com 9 | tls_trust_file /msmtp/ca-bundle.crt 10 | tls_certcheck on 11 | auth on 12 | user user@domain.com 13 | password pass 14 | logfile - 15 | 16 | account default : smtp 17 | -------------------------------------------------------------------------------- /msmtp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefart/rt-docker/1bc254792b1e02ce106c8b7dc02525db5292de1a/msmtp/.keep -------------------------------------------------------------------------------- /prod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this pulls in new images and restarts everything 4 | 5 | set -euf -o pipefail 6 | 7 | export DOCKER_BUILDKIT=1 8 | export COMPOSE_DOCKER_CLI_BUILD=1 9 | 10 | DIR="${BASH_SOURCE%/*}" 11 | if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi 12 | . "$DIR/bash_functions.sh" 13 | 14 | check_files 15 | 16 | fix_file_perms 17 | 18 | docker compose pull 19 | docker compose stop 20 | docker compose rm -f -v -s 21 | docker compose up -d --remove-orphans 22 | docker image prune -f 23 | -------------------------------------------------------------------------------- /restart_prod.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this only restarts prod without pulling in new images 4 | 5 | set -euf -o pipefail 6 | 7 | export DOCKER_BUILDKIT=1 8 | export COMPOSE_DOCKER_CLI_BUILD=1 9 | 10 | DIR="${BASH_SOURCE%/*}" 11 | if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi 12 | . "$DIR/bash_functions.sh" 13 | 14 | check_files 15 | 16 | fix_file_perms 17 | 18 | docker compose stop 19 | docker compose rm -f -v -s 20 | docker compose up -d --remove-orphans 21 | docker image prune -f 22 | -------------------------------------------------------------------------------- /shredder/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefart/rt-docker/1bc254792b1e02ce106c8b7dc02525db5292de1a/shredder/.keep -------------------------------------------------------------------------------- /smime/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefart/rt-docker/1bc254792b1e02ce106c8b7dc02525db5292de1a/smime/.keep --------------------------------------------------------------------------------