Services that need to reach a person hand the message here instead of each carrying a SendGrid key. Email goes to SendGrid, SMS to Twilio; whatsapp and push answer 501 rather than pretending. It binds loopback and the docker bridge only. An authenticated relay on the public internet is an open spam relay the moment the token leaks, and that token is copied into every consumer's environment. The log names a channel, a redacted address and the provider's answer. Never the subject, never the body.
31 lines
1.1 KiB
Docker
31 lines
1.1 KiB
Docker
# The provider calls are stdlib urllib, so the image carries nothing beyond the
|
|
# web server itself. A service that holds the estate's SendGrid and Twilio
|
|
# credentials is the last place to want a wide dependency tree.
|
|
|
|
FROM python:3.11-slim
|
|
|
|
ENV PYTHONUNBUFFERED=1 \
|
|
PYTHONDONTWRITEBYTECODE=1 \
|
|
PIP_NO_CACHE_DIR=1
|
|
|
|
WORKDIR /srv
|
|
|
|
COPY requirements.txt ./
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
|
COPY app ./app
|
|
|
|
# Nothing here needs root, and this process can send mail as the whole estate.
|
|
RUN useradd --system --uid 10002 --home /srv channelexit \
|
|
&& chown -R channelexit:channelexit /srv
|
|
USER channelexit
|
|
|
|
EXPOSE 8080
|
|
|
|
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8080/healthz', timeout=4).status == 200 else 1)"
|
|
|
|
# 0.0.0.0 inside the container only. What keeps this off the internet is the
|
|
# published bind address in the compose file, not this line.
|
|
CMD ["uvicorn", "--factory", "app.main:asgi", "--host", "0.0.0.0", "--port", "8080"]
|