The mesh-mailu container goes with its Dockerfile, the mesh-tools build bases and its bus credential; automx keeps its own image. The code reached the admin API by its name on the mailu network, which a process on the machine cannot, so the admin container publishes 8080 to this machine only and the bundle reaches it on loopback at that port. Mail is still read through docker exec into mailu-imap, so the runtime's account needs the docker socket as nextcloud's does.
563 lines
16 KiB
JSON
563 lines
16 KiB
JSON
{
|
|
"module": "mailu",
|
|
"version": "1",
|
|
"capabilities": [
|
|
"container-runtime"
|
|
],
|
|
"requires": [
|
|
"postgres-database",
|
|
"route",
|
|
"secret"
|
|
],
|
|
"contributes": {
|
|
"postgres-database": {
|
|
"name": "mailu"
|
|
},
|
|
"route": {
|
|
"web": {
|
|
"label": "mail",
|
|
"endpoint": "web-tls",
|
|
"scheme": "https",
|
|
"insecure": true
|
|
},
|
|
"acme": {
|
|
"label": "mail",
|
|
"path": "/.well-known/acme-challenge",
|
|
"endpoint": "web",
|
|
"priority": 100
|
|
},
|
|
"autoconfig": {
|
|
"label": "autoconfig",
|
|
"endpoint": "autoconfig"
|
|
},
|
|
"autodiscover": {
|
|
"label": "autodiscover",
|
|
"endpoint": "autoconfig"
|
|
},
|
|
"automx": {
|
|
"label": "automx",
|
|
"endpoint": "autoconfig"
|
|
}
|
|
}
|
|
},
|
|
"binds": {
|
|
"postgres-database": "${dir:state}/database.json",
|
|
"route": "${dir:state}/route.json"
|
|
},
|
|
"secrets": {
|
|
"postgres-database": "${dir:state}/database.secret",
|
|
"secret": {
|
|
"secret-key": "${dir:state}/secret-key.secret",
|
|
"admin": "${dir:state}/admin.secret",
|
|
"api-token": "${dir:state}/api-token.secret"
|
|
}
|
|
},
|
|
"emits": [
|
|
"user.created",
|
|
"user.deleted",
|
|
"alias.created",
|
|
"alias.deleted"
|
|
],
|
|
"listens": [
|
|
{
|
|
"name": "smtp",
|
|
"port": 25,
|
|
"protocol": "tcp",
|
|
"from": "anywhere",
|
|
"why": "mail from other mail servers",
|
|
"fixed": true
|
|
},
|
|
{
|
|
"name": "pop3",
|
|
"port": 110,
|
|
"protocol": "tcp",
|
|
"from": "anywhere",
|
|
"why": "POP3, kept at parity with the predecessor; pruning legacy protocols is its own deliberate change",
|
|
"fixed": true
|
|
},
|
|
{
|
|
"name": "imap",
|
|
"port": 143,
|
|
"protocol": "tcp",
|
|
"from": "anywhere",
|
|
"why": "IMAP with STARTTLS, kept at parity",
|
|
"fixed": true
|
|
},
|
|
{
|
|
"name": "smtps",
|
|
"port": 465,
|
|
"protocol": "tcp",
|
|
"from": "anywhere",
|
|
"why": "submission over TLS",
|
|
"fixed": true
|
|
},
|
|
{
|
|
"name": "submission",
|
|
"port": 587,
|
|
"protocol": "tcp",
|
|
"from": "anywhere",
|
|
"why": "submission; also what the smtp provision serves consumers",
|
|
"fixed": true
|
|
},
|
|
{
|
|
"name": "imaps",
|
|
"port": 993,
|
|
"protocol": "tcp",
|
|
"from": "anywhere",
|
|
"why": "IMAP over TLS",
|
|
"fixed": true
|
|
},
|
|
{
|
|
"name": "pop3s",
|
|
"port": 995,
|
|
"protocol": "tcp",
|
|
"from": "anywhere",
|
|
"why": "POP3 over TLS, kept at parity",
|
|
"fixed": true
|
|
},
|
|
{
|
|
"name": "web",
|
|
"port": 7080,
|
|
"protocol": "tcp",
|
|
"from": "mesh",
|
|
"why": "the web front over http; only the ACME HTTP-01 passthrough is routed here — everything else 301s to https and would loop a proxy"
|
|
},
|
|
{
|
|
"name": "web-tls",
|
|
"port": 7443,
|
|
"protocol": "tcp",
|
|
"from": "mesh",
|
|
"why": "the web front over its own TLS (admin, webmail, API); its public name is a route grant reaching it here"
|
|
},
|
|
{
|
|
"name": "autoconfig",
|
|
"port": 4243,
|
|
"protocol": "tcp",
|
|
"from": "mesh",
|
|
"why": "automx: mail client autoconfiguration; the autoconfig, autodiscover and automx names are route grants reaching it here"
|
|
},
|
|
{
|
|
"name": "admin-api",
|
|
"port": 8080,
|
|
"protocol": "tcp",
|
|
"from": "machine",
|
|
"why": "the admin API, which this module's own code reaches on loopback from the node's runtime now that it runs outside the mailu network"
|
|
}
|
|
],
|
|
"resources": [
|
|
{
|
|
"id": "mesh-state",
|
|
"type": "directory",
|
|
"mode": "0700",
|
|
"place": "mesh"
|
|
},
|
|
{
|
|
"id": "state",
|
|
"type": "directory",
|
|
"mode": "0700",
|
|
"place": "."
|
|
},
|
|
{
|
|
"id": "grants",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-automx",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "config-env",
|
|
"type": "file",
|
|
"path": "${dir:state}/mailu.env",
|
|
"mode": "0644",
|
|
"content": "ADMIN_ADDRESS=mailu-admin\nANTISPAM_ADDRESS=mailu-antispam\nANTIVIRUS_ADDRESS=mailu-antivirus\nIMAP_ADDRESS=mailu-imap\nSMTP_ADDRESS=mailu-smtp\nFRONT_ADDRESS=mailu-front\nWEBMAIL_ADDRESS=mailu-webmail\nWEBDAV_ADDRESS=mailu-webdav\nREDIS_ADDRESS=mailu-redis\nPORTS=25,80,443,465,993,995,4190,110,143,587\nDOMAIN=${setting:domain}\nHOSTNAMES=${bound:route:name-web}\nPOSTMASTER=admin\nSITENAME=${setting:sitename}\nWEBSITE=${setting:website}\nTLS_FLAVOR=letsencrypt\nSUBNET=192.168.203.0/24\nCOMPOSE_PROJECT_NAME=mailu\nANTIVIRUS=clamav\nWEBMAIL=roundcube\nWEBDAV=radicale\nFETCHMAIL_ENABLED=True\nFETCHMAIL_DELAY=600\nADMIN=true\nWEB_ADMIN=/admin\nWEB_WEBMAIL=/webmail\nWEBROOT_REDIRECT=/webmail\nAPI=true\nWEB_API=/api\nAUTH_RATELIMIT_IP=6000/hour\nAUTH_RATELIMIT_USER=1000/day\nCREDENTIAL_ROUNDS=12\nPASSWORD_SCHEME=PBKDF2\nDISABLE_STATISTICS=True\nMESSAGE_SIZE_LIMIT=50000000\nMESSAGE_RATELIMIT=200/day\nRECIPIENT_DELIMITER=+\nPOSTFIX_MYNETWORKS=127.0.0.0/8 [::1]/128\nRELAYNETS=\nRELAYHOST=\nREJECT_UNLISTED_RECIPIENT=\nDB_FLAVOR=postgresql\nINITIAL_ADMIN_ACCOUNT=admin\nINITIAL_ADMIN_DOMAIN=${setting:domain}\nINITIAL_ADMIN_MODE=ifmissing\nSMTP_PORT=25\nSMTPS_PORT=465\nSUBMISSION_PORT=587\nPOP3_PORT=110\nPOP3S_PORT=995\nIMAP_PORT=143\nIMAPS_PORT=993\nHTTP_PORT=7080\nHTTPS_PORT=7443\nAUTOMX_PORT=4243\nAMX_SMTP_ADDRESS=${bound:route:name-web}\nAMX_SMTP_PORT=587\nAMX_IMAP_ADDRESS=${bound:route:name-web}\nAMX_IMAP_PORT=143\nAMX_MAIL_DOMAINS=${setting:domain}\nDMARC_RUA=admin\nDMARC_RUF=admin\nLETSENCRYPT_SHORTCHAIN=True\nTZ=Etc/UTC\nLOG_LEVEL=INFO\nWELCOME=false\nREAL_IP_HEADER=X-Real-IP\nREAL_IP_FROM=${setting:proxy-address}\nCOMPRESSION=\nCOMPRESS_LEVEL=\nCOMPRESSION_LEVEL=\nBIND_ADDRESS4=127.0.0.1\nBIND_ADDRESS6=::1\nMAILU_VERSION=1.9\nDOCKER_ORG=mailu\nDOCKER_PREFIX=\nWELCOME_SUBJECT=Welcome to your new email account\nWELCOME_BODY=Welcome to your new email account, if you can read this, then it is configured properly!\n"
|
|
},
|
|
{
|
|
"id": "secret-env",
|
|
"type": "file",
|
|
"path": "${dir:state}/secret.env",
|
|
"mode": "0600",
|
|
"content": "SECRET_KEY=${secret:secret-key}\n"
|
|
},
|
|
{
|
|
"id": "database-env",
|
|
"type": "file",
|
|
"path": "${dir:state}/database.env",
|
|
"mode": "0600",
|
|
"content": "DB_FLAVOR=postgresql\nDB_HOST=${bound:postgres-database:at}:${bound:postgres-database:port}\nDB_USER=${bound:postgres-database:as}\nDB_NAME=${bound:postgres-database:as}\nDB_PW=${secret:postgres-database}\n"
|
|
},
|
|
{
|
|
"id": "admin-env",
|
|
"type": "file",
|
|
"path": "${dir:state}/admin.env",
|
|
"mode": "0600",
|
|
"content": "INITIAL_ADMIN_PW=${secret:admin}\nAPI_TOKEN=${secret:api-token}\n"
|
|
},
|
|
{
|
|
"id": "data-certs",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-data",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-dkim",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-mail",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-mailqueue",
|
|
"type": "directory",
|
|
"mode": "0755"
|
|
},
|
|
{
|
|
"id": "data-filter",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-clamav",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-redis",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-webmail",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-dav",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-fetchmail",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-overrides-nginx",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-overrides-dovecot",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-overrides-postfix",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-overrides-rspamd",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "data-overrides-roundcube",
|
|
"type": "directory",
|
|
"mode": "0700"
|
|
},
|
|
{
|
|
"id": "net",
|
|
"type": "network",
|
|
"name": "mailu"
|
|
},
|
|
{
|
|
"id": "resolver",
|
|
"type": "container",
|
|
"name": "mailu-resolver",
|
|
"image": "ghcr.io/mailu/unbound@sha256:3a0fdfb364a63f4f9259526e013c1ef40f5f14de3621ce1560804b3a5909584a",
|
|
"network": "mailu",
|
|
"env-file": [
|
|
"${dir:state}/mailu.env",
|
|
"${dir:state}/secret.env"
|
|
],
|
|
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified",
|
|
"ip": "192.168.203.254"
|
|
},
|
|
{
|
|
"id": "redis",
|
|
"type": "container",
|
|
"name": "mailu-redis",
|
|
"image": "redis@sha256:4bed291aa5efb9f0d77b76ff7d4ab71eee410962965d052552db1fb80576431d",
|
|
"network": "mailu",
|
|
"volumes": [
|
|
"${dir:data-redis}:/data"
|
|
]
|
|
},
|
|
{
|
|
"id": "admin",
|
|
"type": "container",
|
|
"name": "mailu-admin",
|
|
"image": "ghcr.io/mailu/admin@sha256:6dbfdadc4a9590dcb7652357b505200115b689b74008653bbf369e4599a3be5a",
|
|
"network": "mailu",
|
|
"ports": [
|
|
"8080"
|
|
],
|
|
"env-file": [
|
|
"${dir:state}/mailu.env",
|
|
"${dir:state}/secret.env",
|
|
"${dir:state}/database.env",
|
|
"${dir:state}/admin.env"
|
|
],
|
|
"volumes": [
|
|
"${dir:data-data}:/data",
|
|
"${dir:data-dkim}:/dkim"
|
|
],
|
|
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified"
|
|
},
|
|
{
|
|
"id": "imap",
|
|
"type": "container",
|
|
"name": "mailu-imap",
|
|
"image": "ghcr.io/mailu/dovecot@sha256:7f0ed5db996fbdc00adc5c5e38a08492e04f7eb4a9fbd66a03aa9a28ddf23993",
|
|
"network": "mailu",
|
|
"env-file": [
|
|
"${dir:state}/mailu.env"
|
|
],
|
|
"volumes": [
|
|
"${dir:data-mail}:/mail",
|
|
"${dir:data-overrides-dovecot}:/overrides:ro"
|
|
],
|
|
"dns": [
|
|
"192.168.203.254"
|
|
]
|
|
},
|
|
{
|
|
"id": "smtp",
|
|
"type": "container",
|
|
"name": "mailu-smtp",
|
|
"image": "ghcr.io/mailu/postfix@sha256:e2e49f39e53b80eac9e7a2f18d9df11edeb4914fd62dbba89b3155e8e034f62e",
|
|
"network": "mailu",
|
|
"env-file": [
|
|
"${dir:state}/mailu.env"
|
|
],
|
|
"volumes": [
|
|
"${dir:data-mailqueue}:/queue",
|
|
"${dir:data-overrides-postfix}:/overrides:ro"
|
|
],
|
|
"dns": [
|
|
"192.168.203.254"
|
|
]
|
|
},
|
|
{
|
|
"id": "antispam",
|
|
"type": "container",
|
|
"name": "mailu-antispam",
|
|
"image": "ghcr.io/mailu/rspamd@sha256:ff3666d8a61f17d309c5c6f6bcf4d40470b82299ca706ac650301175bb1a079d",
|
|
"network": "mailu",
|
|
"env-file": [
|
|
"${dir:state}/mailu.env"
|
|
],
|
|
"volumes": [
|
|
"${dir:data-filter}:/var/lib/rspamd",
|
|
"${dir:data-overrides-rspamd}:/etc/rspamd/override.d:ro"
|
|
],
|
|
"dns": [
|
|
"192.168.203.254"
|
|
]
|
|
},
|
|
{
|
|
"id": "antivirus",
|
|
"type": "container",
|
|
"name": "mailu-antivirus",
|
|
"image": "clamav/clamav-debian@sha256:b12ef8fefddbba7d88de59bea8a32622f365339154adf02d38fd089112e6745a",
|
|
"network": "mailu",
|
|
"volumes": [
|
|
"${dir:data-clamav}:/var/lib/clamav"
|
|
],
|
|
"dns": [
|
|
"192.168.203.254"
|
|
]
|
|
},
|
|
{
|
|
"id": "webmail",
|
|
"type": "container",
|
|
"name": "mailu-webmail",
|
|
"image": "ghcr.io/mailu/webmail@sha256:bdbee44cdb05a4658f0e3b62cc448de55ca8f8aea172279fda594826144c04f6",
|
|
"network": "mailu",
|
|
"env-file": [
|
|
"${dir:state}/mailu.env",
|
|
"${dir:state}/secret.env"
|
|
],
|
|
"volumes": [
|
|
"${dir:data-webmail}:/data",
|
|
"${dir:data-overrides-roundcube}:/overrides:ro"
|
|
],
|
|
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified",
|
|
"dns": [
|
|
"192.168.203.254"
|
|
]
|
|
},
|
|
{
|
|
"id": "webdav",
|
|
"type": "container",
|
|
"name": "mailu-webdav",
|
|
"image": "ghcr.io/mailu/radicale@sha256:690ed6edf189dfef100a5a8b37c195ebf5d9241ac5f23f2f44b8b7b75726e3de",
|
|
"network": "mailu",
|
|
"env-file": [
|
|
"${dir:state}/mailu.env",
|
|
"${dir:state}/secret.env"
|
|
],
|
|
"volumes": [
|
|
"${dir:data-dav}:/data"
|
|
],
|
|
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified",
|
|
"dns": [
|
|
"192.168.203.254"
|
|
]
|
|
},
|
|
{
|
|
"id": "fetchmail",
|
|
"type": "container",
|
|
"name": "mailu-fetchmail",
|
|
"image": "ghcr.io/mailu/fetchmail@sha256:f881c8412d3bbe73d638469b48321558d6403a9d45bfa043c1e52c752103d42d",
|
|
"network": "mailu",
|
|
"env-file": [
|
|
"${dir:state}/mailu.env",
|
|
"${dir:state}/secret.env"
|
|
],
|
|
"volumes": [
|
|
"${dir:data-fetchmail}:/data"
|
|
],
|
|
"secrets-in-environment": "mailu-admin honours SECRET_KEY_FILE, DB_PW_FILE and API_TOKEN_FILE (configuration.py) but INITIAL_ADMIN_PW is env-only (start.py); the remaining containers' need for SECRET_KEY is unverified",
|
|
"dns": [
|
|
"192.168.203.254"
|
|
]
|
|
},
|
|
{
|
|
"id": "front",
|
|
"type": "container",
|
|
"name": "mailu-front",
|
|
"image": "ghcr.io/mailu/nginx@sha256:36f98897cd1bc9d27628bbb4e04bdf60147af2ec7507d6da77f002c4f256896d",
|
|
"network": "mailu",
|
|
"env-file": [
|
|
"${dir:state}/mailu.env"
|
|
],
|
|
"ports": [
|
|
"25",
|
|
"110",
|
|
"143",
|
|
"465",
|
|
"587",
|
|
"993",
|
|
"995",
|
|
"7080:80",
|
|
"7443:443"
|
|
],
|
|
"volumes": [
|
|
"${dir:data-certs}:/certs",
|
|
"${dir:data-overrides-nginx}:/overrides:ro"
|
|
],
|
|
"dns": [
|
|
"192.168.203.254"
|
|
],
|
|
"logging": "journald"
|
|
},
|
|
{
|
|
"id": "runtime-config",
|
|
"type": "file",
|
|
"path": "${dir:mesh-state}/config.json",
|
|
"mode": "0600",
|
|
"content": "{}\n",
|
|
"merge": "json"
|
|
},
|
|
{
|
|
"id": "automx",
|
|
"type": "container",
|
|
"name": "mailu-automx",
|
|
"artifact": "automx",
|
|
"network": "mailu",
|
|
"env-file": [
|
|
"${dir:state}/mailu.env"
|
|
],
|
|
"ports": [
|
|
"4243"
|
|
],
|
|
"volumes": [
|
|
"${dir:data-automx}:/data"
|
|
]
|
|
}
|
|
],
|
|
"build": {
|
|
"on": [
|
|
{
|
|
"arg": "PYTHON_BASE",
|
|
"image": "python@sha256:25f3cfeaceca14921366af4d1240b56457ef46273bdb508c7b0e8f469f6fd228"
|
|
}
|
|
],
|
|
"artifacts": [
|
|
{
|
|
"name": "code",
|
|
"kind": "bundle",
|
|
"language": "typescript",
|
|
"entrypoints": [
|
|
"index.js",
|
|
"tools/index.js",
|
|
"provisioner/index.js"
|
|
],
|
|
"loads": [
|
|
"index.js",
|
|
"tools/index.js",
|
|
"provisioner/index.js"
|
|
],
|
|
"env": {
|
|
"MESH_MAILU_URL": "http://127.0.0.1:${port:8080}/api/v1",
|
|
"MESH_MAILU_API_KEY_FILE": "${dir:state}/api-token.secret",
|
|
"MESH_MAILU_IMAP_CONTAINER": "mailu-imap",
|
|
"MESH_MAILU_CONFIG_FILE": "${dir:mesh-state}/config.json",
|
|
"MESH_RECEIVES": "${dir:grants}/mesh.json"
|
|
}
|
|
},
|
|
{
|
|
"name": "automx",
|
|
"kind": "image",
|
|
"from": "automx/Dockerfile"
|
|
}
|
|
]
|
|
},
|
|
"provides": [
|
|
{
|
|
"name": "smtp",
|
|
"scope": "mesh"
|
|
}
|
|
],
|
|
"serves": {
|
|
"smtp": {
|
|
"port": 587,
|
|
"domain": "${setting:domain}"
|
|
}
|
|
},
|
|
"receives": {
|
|
"smtp": "${dir:grants}/mesh.json"
|
|
},
|
|
"grants": {
|
|
"smtp": "${dir:grants}"
|
|
},
|
|
"jails": [
|
|
{
|
|
"name": "mailu-front",
|
|
"failregex": "^.*(?:imap|pop3|submission|managesieve)-login: .*\\(auth failed, \\d+ attempts(?: in \\d+ secs)?\\):.*rip=<HOST>(?:,|$)",
|
|
"jail": "backend = systemd\njournalmatch = CONTAINER_NAME=mailu-front\nport = smtp,submission,submissions,imap,imaps,pop3,pop3s\nmaxretry = 3\nfindtime = 1d\nbantime = 1d"
|
|
}
|
|
]
|
|
}
|