Add example job

This commit is contained in:
Daniel Berteaud 2024-01-05 10:29:53 +01:00
parent 7cffdd0289
commit f58675a8c7
18 changed files with 746 additions and 0 deletions

9
example/LICENSE Normal file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2023 nomad
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
example/README.md Normal file
View File

@ -0,0 +1,3 @@
# matrix
Matrix stack (Synaspe, Element, Synapse-Admin)

View File

@ -0,0 +1,3 @@
Kind = "service-defaults"
Name = "matrix"
Protocol = "http"

View File

@ -0,0 +1,22 @@
Kind = "service-intentions"
Name = "matrix"
Sources = [
{
Name = "traefik"
Permissions = [
{
Action = "allow"
HTTP {
PathRegex = "^/_(matrix|synapse)/.*"
Methods = ["GET", "HEAD", "POST", "OPTIONS", "PUT", "DELETE"]
}
},
{
Action = "allow"
HTTP {
Methods = ["GET", "HEAD"]
}
}
]
}
]

View File

@ -0,0 +1,23 @@
FROM nginxinc/nginx-unprivileged:alpine
MAINTAINER Daniel Berteaud <dbd@ehtrace.com>
ARG ELEMENT_VERSION=1.11.53 \
SYNAPSE_ADMIN_VERSION=0.8.7
ENV ELEMENT_NGINX_BIND_ADDR=0.0.0.0 \
ELEMENT_NGINX_BIND_PORT=8710
USER root
RUN set -eux &&\
mkdir -p /opt/element &&\
mkdir -p /opt/synapse-admin &&\
curl -sSL https://github.com/element-hq/element-web/releases/download/v${ELEMENT_VERSION}/element-v${ELEMENT_VERSION}.tar.gz |\
tar xvz -C /opt/element/ --strip-components 1 &&\
curl -sSL https://github.com/Awesome-Technologies/synapse-admin/releases/download/${SYNAPSE_ADMIN_VERSION}/synapse-admin-${SYNAPSE_ADMIN_VERSION}-dirty.tar.gz |\
tar xvz -C /opt/synapse-admin --strip-components 1 &&\
rm -rf /opt/synapse-admin/data/* &&\
chown -R root: /opt/element /opt/synapse-admin
USER nginx
EXPOSE ${ELEMENT_BIND_PORT}

View File

@ -0,0 +1,15 @@
server {
listen ${ELEMENT_NGINX_BIND_ADDR}:${ELEMENT_NGINX_BIND_PORT} default_server;
location / {
root /opt/element;
index index.html index.htm;
}
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

View File

@ -0,0 +1,65 @@
# syntax=docker/dockerfile:labs
FROM python:3.11-alpine AS builder
ARG SYNAPSE_VERSION=1.98.0
RUN set -euxo pipefail &&\
apk --no-cache add \
curl \
ca-certificates \
postgresql15-dev \
build-base \
&&\
mkdir /opt/synapse &&\
cd /opt/synapse &&\
python3 -m venv venv &&\
source venv/bin/activate &&\
pip --no-cache install \
bleach \
matrix-synapse-ldap3 \
psycopg2 \
authlib \
lxml \
twisted \
txacme \
Jinja2 \
pysaml2 \
&&\
pip --no-cache install matrix-synapse==${SYNAPSE_VERSION}
FROM python:3.11-alpine
MAINTAINER Daniel Berteaud <dbd@ehtrace.com>
ENV PATH=/opt/synapse/venv/bin:${PATH} \
LANG=fr_FR.utf8 \
TZ=Europe/Paris \
SYNAPSE_LISTEN_ADDR=0.0.0.0 \
SYNAPSE_CONFIG=/data/conf/homeserver.yml \
SYNAPSE_PG_DB=synapse \
SYNAPSE_PG_HOST=127.0.0.1 \
SYNAPSE_PG_PORT=5432 \
SYNAPSE_PG_USER=synapse \
SYNAPSE_PG_PASSWORD=synapse
ADD https://git.lapiole.org/nomad/base_tools.git#master /
COPY --from=builder /opt /opt
RUN set -euxo pipefail &&\
apk --no-cache add \
tini \
libpq \
&&\
addgroup --gid 8008 synapse &&\
adduser --system --ingroup synapse --disabled-password --uid 8008 --home /opt/synapse --shell /sbin/nologin synapse &&\
mkdir /data &&\
chown synapse:synapse /data &&\
chmod 700 /data
COPY root/ /
EXPOSE 8008
USER synapse
ENTRYPOINT ["tini", "--", "/entrypoint.sh"]
CMD ["matrix-synapse"]

View File

@ -0,0 +1,5 @@
#!/bin/sh
set -euo pipefail
mkdir -p /data/uploads /data/media_store /data/conf /data/tmp

View File

@ -0,0 +1,3 @@
#!/bin/sh
export SYNAPSE_PUBLIC_URL=${SYNAPSE_PUBLIC_URL:-https://${SYNAPSE_SERVER_NAME}}

View File

@ -0,0 +1,25 @@
#!/bin/sh
set -euo pipefail
source /opt/synapse/venv/bin/activate
umask 007
if [ ! -e "${SYNAPSE_CONFIG}" ]; then
echo "Generating a default ${SYNAPSE_CONFIG}"
if [ -z "${SYNAPSE_SERVER_NAME}" ]; then
echo "You must set SYNAPSE_SERVER_NAME environment variable"
exit 1
fi
envsubst < /opt/synapse/templates/homeserver.yml > ${SYNAPSE_CONFIG}
fi
echo "Generate missing keys if needed"
python3 -m synapse.app.homeserver \
--config-path ${SYNAPSE_CONFIG} \
--generate-missing-configs \
--report-stats=no \
--config-directory=/data/conf \
--keys-directory=/data/conf \
--data-directory=/data

View File

@ -0,0 +1,41 @@
server_name: "${SYNAPSE_SERVER_NAME}"
public_baseurl: '${SYNAPSE_PUBLIC_URL}'
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
bind_addresses: [${SYNAPSE_LISTEN_ADDR}]
resources:
- names: [client, federation]
compress: false
database:
name: psycopg2
args:
database: '${SYNAPSE_PG_DB}'
host: '${SYNAPSE_PG_HOST}'
port: ${SYNAPSE_PG_PORT}
user: '${SYNAPSE_PG_USER}'
password: '${SYNAPSE_PG_PASSWORD}'
web_client: False
soft_file_limit: 0
filter_timeline_limit: 5000
log_config: /opt/synapse/templates/logging.conf
media_store_path: /data/media_store
uploads_path: /data/uploads
registration_shared_secret: "${SYNAPSE_SHARED_SECRET}"
macaroon_secret_key: "${SYNAPSE_MACAROON_SECRET}"
form_secret: "${SYNAPSE_FORM_SECERT}"
signing_key_path: "/data/conf/signing.key"
report_stats: false
trusted_key_servers:
- server_name: "matrix.org"
suppress_key_server_warning: true
serve_server_wellknown: true

View File

@ -0,0 +1,30 @@
version: 1
formatters:
precise:
format: '%(name)s - %(lineno)d - %(levelname)s - %(request)s- %(message)s'
filters:
context:
(): synapse.util.logcontext.LoggingContextFilter
request: ""
handlers:
console:
class: logging.StreamHandler
formatter: precise
filters: [context]
loggers:
synapse:
level: ERROR
synapse.storage.SQL:
# beware: increasing this to DEBUG will make synapse log sensitive
# information such as access tokens.
level: ERROR
root:
level: ERROR
handlers: [console]

View File

@ -0,0 +1,10 @@
#!/bin/sh
set -euo pipefail
source /opt/synapse/venv/bin/activate
exec python3 -m synapse.app.homeserver \
--config-path ${SYNAPSE_CONFIG} \
--config-directory=/data/conf \
--keys-directory=/data/conf \
--data-directory=/data

12
example/init/vault-database Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
set -euo pipefail
vault write database/roles/matrix-synapse \
db_name="postgres" \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT \"matrix-synapse\" TO \"{{name}}\"; \
ALTER ROLE \"{{name}}\" SET role = \"matrix-synapse\"" \
default_ttl="12h" \
max_ttl="720h"

436
example/matrix.nomad.hcl Normal file
View File

@ -0,0 +1,436 @@
job "matrix" {
datacenters = ["dc1"]
group "matrix" {
network {
mode = "bridge"
}
volume "data" {
source = "matrix-synapse-data"
type = "csi"
access_mode = "single-node-writer"
attachment_mode = "file-system"
}
service {
name = "matrix"
port = 8008
check {
type = "http"
path = "/health"
expose = true
interval = "10s"
timeout = "5s"
task = "synapse"
check_restart {
limit = 12
grace = "10m"
}
}
tags = [
"traefik.enable=true",
"traefik.http.middlewares.matrix-headers.headers.contentsecuritypolicy=connect-src 'self' https://scalar.vector.im https://api.maptiler.com;frame-src 'self' blob: https://scalar.vector.im/ https://meet.element.io;img-src 'self' data: blob: https://img.youtube.com https://*.ytimg.com;script-src 'self' https://usercontent.riot.im https://scalar.vector.im;worker-src 'self' blob:;",
"traefik.http.routers.matrix-admin.rule=Host(`matrix.consul`) && (PathPrefix(`/_admin/`) || PathPrefix(`/_synapse/admin`))",
"traefik.http.routers.matrix-admin.entrypoints=https",
"traefik.http.routers.matrix-admin.middlewares=rate-limit-high@file,inflight-high@file,security-headers@file,hsts@file,compression@file",
"traefik.http.routers.matrix.rule=Host(`matrix.consul`) || (Host(`matrix.consul`) && PathRegexp(`^/(_(synapse|matrix)|\\.well-known/matrix)/.*`))",
"traefik.http.routers.matrix.entrypoints=https",
"traefik.http.routers.matrix.middlewares=rate-limit-high@file,inflight-high@file,security-headers@file,hsts@file",
]
connect {
sidecar_service {
proxy {
upstreams {
destination_name = "postgres"
local_bind_port = 5432
}
}
}
sidecar_task {
resources {
cpu = 50
memory = 64
}
}
}
}
# wait for required services tp be ready before starting the main task
task "wait-for" {
driver = "docker"
user = 1053
config {
image = "danielberteaud/wait-for:24.1-1"
readonly_rootfs = true
pids_limit = 20
}
lifecycle {
hook = "prestart"
}
env {
SERVICE_0 = "postgres.service.consul"
}
resources {
cpu = 10
memory = 10
memory_max = 30
}
}
task "synapse" {
driver = "docker"
leader = true
config {
image = "danielberteaud/matrix-synapse:1.98.0-2"
pids_limit = 200
readonly_rootfs = true
}
vault {
policies = ["matrix-synapse"]
env = false
disable_file = true
}
env {
SYNAPSE_CONFIG = "/secrets/homeserver.yml"
}
# Use a template block instead of env {} so we can fetch values from vault
template {
data = <<_EOT
LANG=fr_FR.utf8
TMPDIR=/data/tmp
TZ=Europe/Paris
_EOT
destination = "secrets/.env"
perms = 400
env = true
}
template {
data = <<_EOT
alias_creation_rules:
- action: allow
alias: '*'
user_id: '*'
allow_guest_access: false
allowed_avatar_mimetypes:
- image/png
- image/jpeg
- image/gif
database:
args:
database: matrix-synapse
host: 127.0.0.1
password: '{{ with secret "database/creds/matrix-synapse" }}{{ .Data.password }}{{ end }}'
port: "5432"
user: '{{ with secret "database/creds/matrix-synapse" }}{{ .Data.username }}{{ end }}'
name: psycopg2
default_identity_server: https://matrix.org
delete_stale_devices_after: 180d
email:
client_base_url: https://matrix.consul
enable_notifs: true
notif_for_new_users: true
notif_from: '%(app)s <no-reply@consul>'
require_transport_security: false
smtp_host: 127.0.0.1
smtp_port: 25
forgotten_room_retention_period: 15d
form_secret: '{{ with secret "kv/service/matrix" }}{{ .Data.data.form_secret }}{{ end }}'
listeners:
- path: /alloc/tmp/synapse.sock
resources:
- compress: false
names:
- client
- federation
type: http
log_config: /secrets/logging.conf
macaroon_secret_key: '{{ with secret "kv/service/matrix" }}{{ .Data.data.macaroon_secret_key }}{{ end }}'
max_avatar_size: 4M
media_retention:
local_media_lifetime: 730d
remote_media_lifetime: 14d
media_store_path: /data/media_store
password_config:
enabled: false
public_baseurl: https://matrix.consul
push:
include_content: event_id_only
report_stats: false
request_token_inhibit_3pid_errors: true
server_name: matrix.consul
server_notices:
system_mxid_display_name: Notification bot
system_mxid_localpart: server
signing_key_path: /data/conf/matrix.consul.signing.key
sso:
client_whitelist:
- https://matrix.consul
update_profile_information: true
suppress_key_server_warning: true
trusted_key_servers:
- server_name: matrix.org
url_preview_enabled: true
url_preview_ip_range_blacklist:
- 127.0.0.0/8
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 100.64.0.0/10
- 192.0.0.0/24
- 169.254.0.0/16
- 192.88.99.0/24
- 198.18.0.0/15
- 192.0.2.0/24
- 198.51.100.0/24
- 203.0.113.0/24
- 224.0.0.0/4
url_preview_url_blacklist:
- username: '*'
- netloc: ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$
web_client: false
_EOT
destination = "secrets/homeserver.yml"
uid = 100000
gid = 108008
perms = 0640
}
template {
data = <<_EOT
version: 1
formatters:
precise:
format: '%(name)s - %(lineno)d - %(levelname)s - %(request)s- %(message)s'
filters:
context:
(): synapse.util.logcontext.LoggingContextFilter
request: ""
handlers:
console:
class: logging.StreamHandler
formatter: precise
filters: [context]
loggers:
synapse.storage.SQL:
# beware: increasing this to DEBUG will make synapse log sensitive
# information such as access tokens.
level: INFO
root:
level: DEBUG
handlers: [console]
_EOT
destination = "secrets/logging.conf"
}
volume_mount {
volume = "data"
destination = "/data"
}
resources {
cpu = 500
memory = 384
}
}
task "nginx" {
driver = "docker"
config {
image = "danielberteaud/matrix-element:1.11.53-1"
readonly_rootfs = true
pids_limit = 100
volumes = [
"local/nginx.conf:/etc/nginx/conf.d/default.conf:ro",
]
mount {
type = "tmpfs"
target = "/tmp"
tmpfs_options {
size = 5000000
}
}
}
template {
data = <<_EOT
upstream synapse {
server unix:/alloc/tmp/synapse.sock;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 127.0.0.1:8008 default_server;
server_tokens off;
root /opt/element;
index index.html;
include /etc/nginx/mime.types;
default_type application/octet-stream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_socket_keepalive on;
proxy_buffering off;
proxy_request_buffering off;
client_max_body_size 100m;
set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
location /_matrix {
proxy_pass http://synapse;
proxy_read_timeout 600;
}
location /_synapse {
proxy_pass http://synapse;
proxy_read_timeout 600;
}
location = /health {
access_log off;
proxy_pass http://synapse;
}
location /index.html {
add_header Cache-Control "no-cache";
}
location /version {
add_header Cache-Control "no-cache";
}
location /config.json {
add_header Cache-Control "no-cache";
alias /local/element.json;
}
location /.well-known/matrix/server {
add_header Access-Control-Allow-Origin *;
default_type application/json;
expires 6h;
return 200 '{"m.server": "matrix.consul:443"}';
}
location /.well-known/matrix/client {
add_header Access-Control-Allow-Origin *;
default_type application/json;
expires 6h;
return 200 '{"m.homeserver": {"base_url": "https://matrix.consul"}}';
}
# Expose synapse admin
location /_admin/ {
alias /opt/synapse-admin/;
expires 30d;
}
# default files
location / {
expires 30d;
}
}
_EOT
destination = "local/nginx.conf"
}
template {
data = <<_EOT
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.consul",
"server_name": "matrix.consul"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
"brand": "Element",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
"https://scalar.vector.im/_matrix/integrations/v1",
"https://scalar.vector.im/api",
"https://scalar-staging.vector.im/_matrix/integrations/v1",
"https://scalar-staging.vector.im/api",
"https://scalar-staging.element.im/scalar/api"
],
"show_labs_settings": true,
"roomDirectory": {
"servers": [
"matrix.org"
]
},
"disable_guests": true,
"settingDefaults": {
"UIFeature.feedback": false,
"UIFeature.thirdPartyId": false
},
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx",
"happyJson": true
}
_EOT
destination = "local/element.json"
}
resources {
cpu = 20
memory = 16
}
}
}
}

19
example/prep.d/10-mv-conf.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
set -eu
if [ "matrix" != "matrix" ]; then
for DIR in vault consul nomad; do
if [ -d output/${DIR} ]; then
for FILE in $(find output/${DIR} -name "*matrix*.hcl" -type f); do
NEW_FILE=$(echo "${FILE}" | sed -E "s/matrix/matrix/g")
mv "${FILE}" "${NEW_FILE}"
done
fi
done
fi

18
example/prep.d/20-rand-pwd.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/sh
set -euo pipefail
# Initialize random passwords if needed
if ! vault kv list kv/service 2>/dev/null | grep -q -E '^matrix$'; then
vault kv put kv/service/matrix \
macaroon_secret_key=$(pwgen -s -n 50 1) \
form_secret=$(pwgen -s -n 50 1)
fi
for PWD in macaroon_secret_key form_secret; do
if ! vault kv get -field ${PWD} kv/service/matrix >/dev/null 2>&1; then
vault kv patch kv/service/matrix \
${PWD}=$(pwgen -s -n 50 1)
fi
done

View File

@ -0,0 +1,7 @@
path "kv/data/service/matrix" {
capabilities = ["read"]
}
path "database/creds/matrix-synapse" {
capabilities = ["read"]
}