# Mailcow: Create Mailbox Create a new mailbox in Mailcow on the Contabo VPS. Covers both interactive (UI) and API-driven creation, with the critical authsource fix for service accounts. --- ## When to Use This Any time a new mailbox is created in Mailcow, but **especially** for service/system accounts that authenticate via SMTP to send mail programmatically (e.g., `no-reply@echo6.co` used by Authentik, `recon@echo6.co` used by the RECON pipeline). These accounts don't log in through the Mailcow web UI or SSO — they pass credentials directly to Postfix over SMTP, so they **must** use local password authentication. --- ## Prerequisites - SSH access to Contabo (`ssh root@100.64.0.1`) - Mailcow API key (stored in Mailcow admin UI under System → Configuration → API) - Mailcow DB password: source from `/opt/mailcow-dockerized/.env` (`DBPASS`) --- ## Inputs ``` LOCAL_PART=no-reply # Left side of the @ sign DOMAIN=echo6.co # Must already exist in Mailcow DISPLAY_NAME="Echo6 No Reply" # Friendly name PASSWORD= # Generate with: openssl rand -base64 24 | tr -d '/+=' QUOTA_MB=256 # Mailbox quota in MB IS_SERVICE_ACCOUNT=true # true = SMTP sender, false = regular user MAILCOW_API_KEY= # From Mailcow admin UI ``` --- ## Step 1: Create the Mailbox ### Option A: Via Mailcow API ```bash ssh root@100.64.0.1 curl -sk -X POST "https://127.0.0.1:8443/api/v1/add/mailbox" \ -H "X-API-Key: ${MAILCOW_API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "local_part": "'${LOCAL_PART}'", "domain": "'${DOMAIN}'", "name": "'${DISPLAY_NAME}'", "password": "'${PASSWORD}'", "password2": "'${PASSWORD}'", "quota": '${QUOTA_MB}', "active": 1, "force_pw_update": 0, "tls_enforce_in": 1, "tls_enforce_out": 1 }' ``` Expected response: ```json [{"type":"success","msg":["mailbox_added","no-reply@echo6.co"]}] ``` ### Option B: Via Mailcow Admin UI 1. Open https://mail.echo6.co (log in as admin) 2. Navigate to **Email → Mailboxes → Add mailbox** 3. Fill in local part, domain, display name, password, quota 4. Click **Add** ### Access Flags For service accounts (send-only), disable unnecessary access after creation: ```bash curl -sk -X POST "https://127.0.0.1:8443/api/v1/edit/mailbox" \ -H "X-API-Key: ${MAILCOW_API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "items": ["'${LOCAL_PART}@${DOMAIN}'"], "attr": { "sogo_access": "0", "imap_access": "0", "pop3_access": "0", "smtp_access": "1" } }' ``` Regular user accounts can leave all access flags at their defaults (all enabled). --- ## Step 2: Fix authsource (CRITICAL for Service Accounts) ### The Problem Mailcow domains configured with OIDC authentication (like `echo6.co` with Authentik SSO) set `authsource=generic-oidc` on **every new mailbox by default**. This tells Dovecot to authenticate the account through the OIDC provider instead of the local password hash. For service accounts that log in via SMTP with a username and password, this means: 1. Postfix receives the SMTP AUTH credentials 2. Postfix hands them to Dovecot for verification 3. Dovecot's Lua passdb sees `authsource=generic-oidc` 4. Dovecot tries to authenticate via Authentik SSO 5. The service account doesn't exist in Authentik → **auth fails silently** 6. SMTP returns `535 5.7.8 Error: authentication failed: (reason unavailable)` The failure message gives no indication that OIDC is the cause. The password is correct, the mailbox exists, and the Mailcow API reports success on password changes — but SMTP auth never works. ### The Fix Change the authsource from `generic-oidc` to `mailcow` in the database: ```bash ssh root@100.64.0.1 # Source the DB password DBPASS=$(grep ^DBPASS /opt/mailcow-dockerized/.env | cut -d= -f2) # Check current authsource docker exec mailcowdockerized-mysql-mailcow-1 \ mysql -u mailcow -p${DBPASS} mailcow -N \ -e "SELECT username, authsource FROM mailbox WHERE username='${LOCAL_PART}@${DOMAIN}'" # Fix: set authsource to local password auth docker exec mailcowdockerized-mysql-mailcow-1 \ mysql -u mailcow -p${DBPASS} mailcow \ -e "UPDATE mailbox SET authsource='mailcow' WHERE username='${LOCAL_PART}@${DOMAIN}'" ``` **When to apply:** | Account Type | authsource | Reason | |-------------|-----------|--------| | Service account (SMTP sender) | `mailcow` | Authenticates with local password via SMTP | | Regular user (SSO login) | `generic-oidc` | Authenticates through Authentik web SSO | | Regular user (IMAP/SMTP client) | `mailcow` | Authenticates with local password from mail client | Rule of thumb: if the account will ever authenticate with a username + password (SMTP, IMAP, POP3), set `authsource=mailcow`. Only leave `generic-oidc` for accounts that exclusively use SSO web login. --- ## Step 3: Verify SMTP Authentication Wait a few seconds after the authsource fix, then test: ```bash # From the Contabo host python3 -c " import smtplib s = smtplib.SMTP('mail.echo6.co', 587, timeout=10) s.starttls() s.login('${LOCAL_PART}@${DOMAIN}', '${PASSWORD}') print('SMTP auth: OK') s.quit() " ``` If SMTP auth is being used from inside a Docker container (like Authentik), also test from there: ```bash docker exec authentik-worker python3 -c " import smtplib s = smtplib.SMTP('mail.echo6.co', 587, timeout=10) s.starttls() s.login('${LOCAL_PART}@${DOMAIN}', '${PASSWORD}') print('Container SMTP auth: OK') s.quit() " ``` Both should print `OK`. If either fails with `535 5.7.8 Error: authentication failed`, re-check the authsource (Step 2). --- ## Step 4: Store Credentials Add the new mailbox credentials to `/home/zvx/projects/.ref/credentials`: ``` # Mailcow: ${LOCAL_PART}@${DOMAIN} MAILCOW_${LOCAL_PART^^}_USER=${LOCAL_PART}@${DOMAIN} MAILCOW_${LOCAL_PART^^}_PASS=${PASSWORD} ``` --- ## Troubleshooting ### SMTP auth fails immediately after mailbox creation **Cause:** `authsource=generic-oidc` (see Step 2). ### SMTP auth fails after it was previously working **Cause 1:** Mailcow may have reset the authsource during a stack restart or update. Re-apply Step 2. **Cause 2:** The password hash may have been corrupted by a failed API password reset. Mailcow's password edit API (`/api/v1/edit/mailbox`) sometimes reports success but doesn't actually update the hash. **Workaround:** Delete the mailbox and recreate it from scratch (Step 1), then re-apply the authsource fix (Step 2). Do not attempt to reset the password via API. ### "Unknown user" in Dovecot logs Check that the mailbox exists and is active: ```bash curl -sk "https://127.0.0.1:8443/api/v1/get/mailbox/${LOCAL_PART}@${DOMAIN}" \ -H "X-API-Key: ${MAILCOW_API_KEY}" | python3 -m json.tool ``` ### Checking Dovecot auth logs ```bash docker logs mailcowdockerized-dovecot-mailcow-1 --since 5m 2>&1 | grep -i auth ``` ### Checking netfilter/fail2ban Too many failed SMTP login attempts can trigger Mailcow's brute-force protection: ```bash docker logs mailcowdockerized-netfilter-mailcow-1 --since 10m 2>&1 | grep -i ban ``` If the Contabo IP (5.189.158.149) is banned, restart the netfilter container: ```bash cd /opt/mailcow-dockerized && docker compose restart netfilter-mailcow ``` --- ## Checklist ``` [ ] Mailbox created (API or UI) [ ] Access flags set appropriately for account type [ ] authsource checked — set to 'mailcow' if service account [ ] SMTP auth verified from host [ ] SMTP auth verified from consuming container (if applicable) [ ] Credentials stored in /home/zvx/projects/.ref/credentials ``` --- ## Reference: Current Service Accounts | Mailbox | Used By | authsource | Purpose | |---------|---------|-----------|---------| | no-reply@echo6.co | Authentik | mailcow | SSO invitation emails, notifications | | cipher@echo6.co | CIPHER | generic-oidc | Daily intelligence briefs | | recon@echo6.co | RECON | generic-oidc | Pipeline notifications | | fulcrum@echo6.co | Fulcrum | generic-oidc | Hub notifications | **Note:** cipher, recon, and fulcrum currently use `generic-oidc`. If any of these need to send mail via SMTP (not through the SSO web UI), their authsource must be changed to `mailcow` per Step 2. --- *Created: 2026-02-16*