diff --git a/.gitignore b/.gitignore index f3cdb1b..14e6c4a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ tools/__pycache__/ externals/ .env .vagrant +api/docs/api-docs.html \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 38fb419..9c10d78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,39 @@ CHANGELOG ========= +v0.50 (September 25, 2020) +-------------------------- + +Setup: + +* When upgrading from versions before v0.40, setup will now warn that ownCloud/Nextcloud data cannot be migrated rather than failing the installation. + +Mail: + +* An MTA-STS policy for incoming mail is now published (in DNS and over HTTPS) when the primary hostname and email address domain both have a signed TLS certificate installed, allowing senders to know that an encrypted connection should be enforced. +* The per-IP connection limit to the IMAP server has been doubled to allow more devices to connect at once, especially with multiple users behind a NAT. + +DNS: + +* autoconfig and autodiscover subdomains and CalDAV/CardDAV SRV records are no longer generated for domains that don't have user accounts since they are unnecessary. +* IPv6 addresses can now be specified for secondary DNS nameservers in the control panel. + +TLS: + +* TLS certificates are now provisioned in groups by parent domain to limit easy domain enumeration and make provisioning more resilient to errors for particular domains. + +Control Panel: + +* The control panel API is now fully documented at https://mailinabox.email/api-docs.html. +* User passwords can now have spaces. +* Status checks for automatic subdomains have been moved into the section for the parent domain. +* Typo fixed. + +Web: + +* The default web page served on fresh installations now adds the `noindex` meta tag. +* The HSTS header is revised to also be sent on non-success responses. + v0.48 (August 26, 2020) ----------------------- diff --git a/README.md b/README.md index 09937d0..f7de677 100644 --- a/README.md +++ b/README.md @@ -102,20 +102,21 @@ Our goals are to: Additionally, this project has a [Code of Conduct](CODE_OF_CONDUCT.md), which supersedes the goals above. Please review it when joining our community. -The Box -------- + +In The Box +---------- Mail-in-a-Box turns a fresh ~~Ubuntu 18.04 LTS~~ Debian 10 (Buster) 64-bit machine into a working mail server by installing and configuring various components. -It is a one-click email appliance. There are no user-configurable setup options. It "just works". +It is a one-click email appliance. There are no user-configurable setup options. It "just works." The components installed are: -* SMTP ([postfix](http://www.postfix.org/)), IMAP ([dovecot](http://dovecot.org/)), CardDAV/CalDAV ([Nextcloud](https://nextcloud.com/)), and Exchange ActiveSync ([z-push](http://z-push.org/)) servers -* Webmail ([Roundcube](http://roundcube.net/)), mail filter rules (also using dovecot), and email client autoconfig settings (served by [nginx](http://nginx.org/)) +* SMTP ([postfix](http://www.postfix.org/)), IMAP ([Dovecot](http://dovecot.org/)), CardDAV/CalDAV ([Nextcloud](https://nextcloud.com/)), and Exchange ActiveSync ([z-push](http://z-push.org/)) servers +* Webmail ([Roundcube](http://roundcube.net/)), mail filter rules (thanks to Roundcube and Dovecot), and email client autoconfig settings (served by [nginx](http://nginx.org/)) * Spam filtering ([spamassassin](https://spamassassin.apache.org/)) and greylisting ([postgrey](http://postgrey.schweikert.ch/)) * DNS ([nsd4](https://www.nlnetlabs.nl/projects/nsd/)) with [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework), DKIM ([OpenDKIM](http://www.opendkim.org/)), [DMARC](https://en.wikipedia.org/wiki/DMARC), [DNSSEC](https://en.wikipedia.org/wiki/DNSSEC), [DANE TLSA](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities), [MTA-STS](https://tools.ietf.org/html/rfc8461), and [SSHFP](https://tools.ietf.org/html/rfc4255) policy records automatically set -* HTTPS TLS certificates are automatically provisioned using [Let's Encrypt](https://letsencrypt.org/) (needed for webmail, CardDAV/CalDAV, ActiveSync, MTA-STS policy, etc.). +* TLS certificates are automatically provisioned using [Let's Encrypt](https://letsencrypt.org/) for protecting https and all of the other services on the box * Backups ([duplicity](http://duplicity.nongnu.org/)), firewall ([ufw](https://launchpad.net/ufw)), intrusion protection ([fail2ban](http://www.fail2ban.org/wiki/index.php/Main_Page)), and basic system monitoring ([munin](http://munin-monitoring.org/)) It also includes system management tools: @@ -124,10 +125,11 @@ It also includes system management tools: * A control panel for adding/removing mail users, aliases, custom DNS records, configuring backups, etc. * An API for all of the actions on the control panel -It also supports static website hosting since the box is serving HTTPS anyway. +It also supports static website hosting since the box is serving HTTPS anyway. (To serve a website for your domains elsewhere, just add a custom DNS "A" record in you Mail-in-a-Box's control panel to point domains to another server.) For more information on how Mail-in-a-Box handles your privacy, see the [security details page](security.md). + Installation ------------ @@ -146,7 +148,7 @@ by him: $ curl -s https://keybase.io/joshdata/key.asc | gpg --import gpg: key C10BDD81: public key "Joshua Tauberer " imported - $ git verify-tag v0.48 + $ git verify-tag v0.50 gpg: Signature made ..... using RSA key ID C10BDD81 gpg: Good signature from "Joshua Tauberer " gpg: WARNING: This key is not certified with a trusted signature! @@ -159,7 +161,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this Checkout the tag corresponding to the most recent release: - $ git checkout v0.48 + $ git checkout v0.50 Begin the installation. @@ -169,6 +171,9 @@ For help, DO NOT contact Josh directly --- I don't do tech support by email or t Post your question on the [discussion forum](https://discourse.mailinabox.email/) instead, where maintainers and Mail-in-a-Box users may be able to help you. +Note that while we want everything to "just work," we can't control the rest of the Internet. Other mail services might block or spam-filter email sent from your Mail-in-a-Box. +This is a challenge faced by everyone who runs their own mail server, with or without Mail-in-a-Box. See our discussion forum for tips about that. + Contributing and Development ---------------------------- @@ -182,6 +187,7 @@ This project was inspired in part by the ["NSA-proof your email in 2 hours"](htt Mail-in-a-Box is similar to [iRedMail](http://www.iredmail.org/) and [Modoboa](https://github.com/tonioo/modoboa). + The History ----------- diff --git a/api/docs/generate-docs.sh b/api/docs/generate-docs.sh new file mode 100755 index 0000000..e7951d8 --- /dev/null +++ b/api/docs/generate-docs.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +# Requirements: +# - Node.js +# - redoc-cli (`npm install redoc-cli -g`) + +redoc-cli bundle ../mailinabox.yml \ + -t template.hbs \ + -o api-docs.html \ + --templateOptions.metaDescription="Mail-in-a-Box HTTP API" \ + --title="Mail-in-a-Box HTTP API" \ + --options.expandSingleSchemaField \ + --options.hideSingleRequestSampleTab \ + --options.jsonSampleExpandLevel=10 \ + --options.hideDownloadButton \ + --options.theme.logo.maxHeight=180px \ + --options.theme.logo.maxWidth=180px \ + --options.theme.colors.primary.main="#C52" \ + --options.theme.typography.fontSize=16px \ + --options.theme.typography.fontFamily="Raleway, sans-serif" \ + --options.theme.typography.headings.fontFamily="Ubuntu, Arial, sans-serif" \ + --options.theme.typography.code.fontSize=15px \ + --options.theme.typography.code.fontFamily='"Source Code Pro", monospace' \ No newline at end of file diff --git a/api/docs/template.hbs b/api/docs/template.hbs new file mode 100644 index 0000000..0de7d22 --- /dev/null +++ b/api/docs/template.hbs @@ -0,0 +1,31 @@ + + + + + + {{title}} + + + + + + + + + {{{redocHead}}} + + + + {{{redocHTML}}} + + + diff --git a/api/mailinabox.yml b/api/mailinabox.yml new file mode 100644 index 0000000..57ba5aa --- /dev/null +++ b/api/mailinabox.yml @@ -0,0 +1,2531 @@ +openapi: 3.0.3 +info: + title: Mail-in-a-Box + description: | + Mail-in-a-Box API HTTP specification. + + # Introduction + This API is documented in [**OpenAPI format**](http://spec.openapis.org/oas/v3.0.3). + ([View the full HTTP specification](https://raw.githubusercontent.com/mail-in-a-box/mailinabox/api-spec/api/mailinabox.yml).) + + All endpoints are relative to `https://{host}/admin` and are secured with [`Basic Access` authentication](https://en.wikipedia.org/wiki/Basic_access_authentication). + contact: + name: Mail-in-a-Box support + url: https://mailinabox.email/ + license: + name: CC0 1.0 Universal + url: https://creativecommons.org/publicdomain/zero/1.0/legalcode + version: 0.47.0 + x-logo: + url: https://mailinabox.email/static/logo.png + altText: Mail-in-a-Box logo +externalDocs: + description: Find out more about Mail-in-a-box. + url: https://mailinabox.email/ +servers: + - url: https://{host}/admin + variables: + host: + default: box.example.com + description: The API hostname. +security: + - basicAuth: [] +tags: + - name: User + description: Endpoints related to user authentication. + - name: Mail + description: | + Mail operations, which include getting all users, getting all aliases, adding/updating/removing users and aliases and getting all mail domains. + - name: DNS + description: | + DNS operations, which include adding custom records, adding a secondary nameserver and viewing all DNS records. + - name: SSL + description: | + TLS (SSL) Certificates operations, which include checking certificate status + and installing custom certificates. + - name: Web + description: | + Static web hosting operations, which include getting domain information and updating domain root directories. + - name: System + description: | + System operations, which include system status checks, new version checks + and reboot status. +paths: + /me: + get: + tags: + - User + summary: Get user information + description: | + Returns user information. Used for user authentication. + + Authenticate a user by supplying the auth token as a base64 encoded string in + format `email:password` using basic authentication headers. + + If successful, a long-lived `api_key` is returned which can be used for subsequent + requests to the API. + operationId: getMe + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/me" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/MeResponse' + examples: + invalid: + value: + reason: Incorrect username or password + status: invalid + ok: + value: + api_key: 1a2b3c4d5e6f7g8h9i0j + email: user@example.com + privileges: + - admin + status: ok + /system/status: + post: + tags: + - System + summary: Get system status + description: | + Returns an array of statuses which can include headings. + operationId: getSystemStatus + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/status" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemStatusResponse' + example: + - type: heading + text: System + extra: [] + - type: warning + text: This domain's DNSSEC DS record is not set + extra: + - monospace: false + text: 'Digest Type: 2 / SHA-25' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/version: + get: + tags: + - System + summary: Get system version + description: Returns installed Mail-in-a-Box version. + operationId: getSystemVersion + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/version" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemVersionResponse' + example: v0.46 + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/latest-upstream-version: + post: + tags: + - System + summary: Get system upstream version + description: Returns Mail-in-a-Box upstream version. + operationId: getSystemUpstreamVersion + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/latest-upstream-version" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemVersionUpstreamResponse' + example: v0.47 + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/updates: + get: + tags: + - System + summary: Get system updates + description: Returns system (apt) updates. + operationId: getSystemUpdates + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/updates" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemUpdatesResponse' + example: | + libgnutls30 (3.5.18-1ubuntu1.4) + libxau6 (1:1.0.8-1ubuntu1) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/update-packages: + post: + tags: + - System + summary: Update system packages + description: Updates system (apt) packages. + operationId: updateSystemPackages + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/update-packages" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemUpdatePackagesResponse' + example: | + Calculating upgrade... + The following packages will be upgraded: + cloud-init grub-common + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/privacy: + get: + tags: + - System + summary: Get system privacy status + description: | + Returns system privacy (new-version check) status. + + Response: + + - `true`: Private, new-version checks will not be performed + - `false`: Not private, new-version checks will be performed + operationId: getSystemPrivacyStatus + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/privacy" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemPrivacyStatusResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - System + summary: Update system privacy + description: | + Updates system privacy (new-version checks). + + Request: + + - `value: private`: Disable new version checks + - `value: off`: Enable new version checks + operationId: updateSystemPrivacy + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/SystemPrivacyUpdateRequest' + examples: + enable: + summary: Enable new version checks + value: + value: 'off' + disable: + summary: Disable new version checks + value: + value: private + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/privacy" \ + -d "value=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemPrivacyUpdateResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/reboot: + get: + tags: + - System + summary: Get system reboot status + description: | + Returns the system reboot status. + + Response: + + - `true`: A reboot is required + - `false`: A reboot is not required + operationId: getSystemRebootStatus + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/reboot" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemRebootStatusResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - System + summary: Reboot system + description: Reboots the system. + operationId: rebootSystem + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/reboot" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemRebootResponse' + example: No reboot is required, so it is not allowed. + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/backup/status: + get: + tags: + - System + summary: Get system backup status + description: | + Returns the system backup status. + + If the list of backups is empty, this implies no backups have been made yet. + operationId: getSystemBackupStatus + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/backup/status" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemBackupStatusResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /system/backup/config: + get: + tags: + - System + summary: Get system backup config + description: Returns the system backup config. + operationId: getSystemBackupConfig + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/system/backup/config" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SystemBackupConfigResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - System + summary: Update system backup config + description: Updates the system backup config. + operationId: updateSystemBackupConfig + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/SystemBackupConfigUpdateRequest' + examples: + s3: + summary: S3 backup + value: + target: s3://s3.eu-central-1.amazonaws.com/box-example-com + target_user: ACCESS_KEY + target_pass: SECRET_ACCESS_KEY + minAge: 3 + local: + summary: Local backup + value: + target: local + target_user: '' + target_pass: '' + minAge: 3 + rsync: + summary: Rsync backup + value: + target: rsync://username@box.example.com//backups/box.example.com + target_user: '' + target_pass: '' + minAge: 3 + off: + summary: Disable backups + value: + target: 'off' + target_user: '' + target_pass: '' + minAge: 0 + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/system/backup/config" \ + -d "target=" \ + -d "target_user=" \ + -d "target_pass=" \ + -d "min_age=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SystemBackupConfigUpdateResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /ssl/status: + get: + tags: + - SSL + summary: Get SSL status + description: Returns the SSL status for all domains. + operationId: getSSLStatus + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/ssl/status" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SSLStatusResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /ssl/csr/{domain}: + post: + tags: + - SSL + summary: Generate SSL CSR + description: | + Generates a Certificate Signing Request (CSR) for a domain & country code. + operationId: generateSSLCSR + parameters: + - in: path + name: domain + schema: + $ref: '#/components/schemas/Hostname' + required: true + description: Domain to generate CSR for. + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/SSLCSRGenerateRequest' + example: + countrycode: 'GB' + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/ssl/csr/" \ + -d "countrycode=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SSLCSRGenerateResponse' + example: | + -----BEGIN CERTIFICATE REQUEST----- + MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t + ... + JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0= + -----END CERTIFICATE REQUEST----- + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /ssl/install: + post: + tags: + - SSL + summary: Install SSL certificate + description: | + Installs a custom certificate. The chain certificate is optional. + operationId: installSSLCertificate + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/SSLCertificateInstallRequest' + example: + domain: example.com + cert: CERT_STRING + chain: CHAIN_STRING + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/ssl/install" \ + -d "domain=" \ + -d "cert=" \ + -d "chain=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/SSLCertificateInstallResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /ssl/provision: + post: + tags: + - SSL + summary: Provision SSL certificates + description: | + Provisions certificates for all domains. + operationId: provisionSSLCertificates + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/ssl/provision" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/SSLCertificatesProvisionResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/secondary-nameserver: + get: + tags: + - DNS + summary: Get DNS secondary nameserver + description: | + Returns a list of nameserver hostnames. + operationId: getDnsSecondaryNameserver + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/secondary-nameserver" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSSecondaryNameserverResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - DNS + summary: Add DNS secondary nameserver + description: | + Adds one or more secondary nameservers. + operationId: addDnsSecondaryNameserver + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/DNSSecondaryNameserverAddRequest' + example: + hostnames: ns2.hostingcompany.com, ns3.hostingcompany.com + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/dns/secondary-nameserver" \ + -d "hostnames=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSSecondaryNameserverAddResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: Could not resolve the IP address of badhostname + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/zones: + get: + tags: + - DNS + summary: Get DNS zones + description: Returns an array of all managed top-level domains. + operationId: getDnsZones + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/zones" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSZonesResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/update: + post: + tags: + - DNS + summary: Update DNS + description: Updates the DNS. Involves creating zone files and restarting `nsd`. + operationId: updateDns + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/DNSUpdateRequest' + example: + force: 1 + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/dns/update" \ + -d "force=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSUpdateResponse' + 400: + description: Bad request + content: + text/html: + schema: + type: string + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/custom: + get: + tags: + - DNS + summary: Get DNS custom records + description: Returns all custom DNS records. + operationId: getDnsCustomRecords + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/custom" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSCustomRecordsResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/custom/{qname}/{rtype}: + parameters: + - in: path + name: qname + schema: + $ref: '#/components/schemas/Hostname' + required: true + description: DNS record query name + - in: path + name: rtype + schema: + $ref: '#/components/schemas/DNSRecordType' + required: true + description: Record type + get: + tags: + - DNS + summary: Get DNS custom records + description: Returns all custom records for the specified query name and type. + operationId: getDnsCustomRecordsForQNameAndType + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/custom//" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSCustomRecordsResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - DNS + summary: Add DNS custom record + description: Adds a custom DNS record for the specified query name and type. + operationId: addDnsCustomRecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/dns/custom//" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordUpsertResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: "'badhostname' does not appear to be an IPv4 or IPv6 address" + 403: + description: Forbidden + content: + text/html: + schema: + type: string + put: + tags: + - DNS + summary: Update DNS custom record + description: Updates an existing DNS custom record value for the specified qname and type. + operationId: updateDnsCustomRecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -x PUT "https://{host}/admin/dns/custom//" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordUpsertResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: "'badhostname' does not appear to be an IPv4 or IPv6 address" + 403: + description: Forbidden + content: + text/html: + schema: + type: string + delete: + tags: + - DNS + summary: Remove DNS custom record + description: Removes a DNS custom record for the specified domain, type & value. + operationId: removeDnsCustomRecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -X DELETE "https://{host}/admin/dns/custom//" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordRemoveResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: badhostname is not a domain name or a subdomain of a domain name managed by this box + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/custom/{qname}: + parameters: + - in: path + name: qname + schema: + $ref: '#/components/schemas/Hostname' + required: true + description: DNS query name. + get: + tags: + - DNS + summary: Get DNS custom A records + description: Returns all custom A records for the specified query name. + operationId: getDnsCustomARecordsForQName + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/custom/" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSCustomRecordsResponse' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + post: + tags: + - DNS + summary: Add DNS custom A record + description: Adds a custom DNS A record for the specified query name. + operationId: addDnsCustomARecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/dns/custom/" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordUpsertResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: "'badhostname' does not appear to be an IPv4 or IPv6 address" + 403: + description: Forbidden + content: + text/html: + schema: + type: string + put: + tags: + - DNS + summary: Update DNS custom A record + description: Updates an existing DNS custom A record value for the specified qname. + operationId: updateDnsCustomARecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -x PUT "https://{host}/admin/dns/custom/" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordUpsertResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: "'badhostname' does not appear to be an IPv4 or IPv6 address" + 403: + description: Forbidden + content: + text/html: + schema: + type: string + delete: + tags: + - DNS + summary: Remove DNS custom A record + description: Removes a DNS custom A record for the specified domain & value. + operationId: removeDnsCustomARecord + requestBody: + $ref: '#/components/requestBodies/DNSCustomRecordRequest' + x-codeSamples: + - lang: curl + source: | + curl -X DELETE "https://{host}/admin/dns/custom/" \ + -H "Content-Type: text/plain" \ + --data-raw "" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/DNSCustomRecordRemoveResponse' + example: 'updated DNS: example.com' + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: badhostname is not a domain name or a subdomain of a domain name managed by this box + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /dns/dump: + get: + tags: + - DNS + summary: Get DNS dump + description: Returns all DNS records. + operationId: getDnsDump + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/dns/dump" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/DNSDumpResponse' + example: + - - example1.com + - - explanation: Required. Specifies the hostname (and priority) of the machine that handles @example.com mail. + qname: example1.com + rtype: MX + value: 10 box.example1.com. + - - example2.com + - - explanation: Required. Specifies the hostname (and priority) of the machine that handles @example.com mail. + qname: example2.com + rtype: MX + value: 10 box.example2.com. + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users: + get: + tags: + - Mail + summary: Get mail users + description: Returns all mail users. + operationId: getMailUsers + parameters: + - in: query + name: format + schema: + $ref: '#/components/schemas/MailUsersResponseFormat' + description: The format of the response. + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/mail/users?format=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/MailUsersResponse' + text/html: + schema: + $ref: '#/components/schemas/MailUsersSimpleResponse' + example: | + user1@example.com + user2@example.com + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/add: + post: + tags: + - Mail + summary: Add mail user + description: Adds a new mail user. + operationId: addMailUser + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserAddRequest' + examples: + normal: + summary: Normal user + value: + email: user@example.com + password: s3curE_pa5Sw0rD + privileges: '' + admin: + summary: Admin user + value: + email: user@example.com + password: s3curE_pa5Sw0rD + privileges: admin + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/add" \ + -d "email=" \ + -d "password=" \ + -d "privileges=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserAddResponse' + example: | + mail user added + updated DNS: OpenDKIM configuration + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: Invalid email address + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/remove: + post: + tags: + - Mail + summary: Remove mail user + description: Removes an existing mail user. + operationId: removeMailUser + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserRemoveRequest' + example: + email: user@example.com + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/remove" \ + -d "email=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserRemoveResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: That's not a user (invalid@example.com) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/privileges/add: + post: + tags: + - Mail + summary: Add mail user privilege + description: Adds a privilege to an existing mail user. + operationId: addMailUserPrivilege + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserAddPrivilegeRequest' + example: + email: user@example.com + privilege: admin + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/privileges/add" \ + -d "email=" \ + -d "privilege=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserAddPrivilegeResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: That's not a user (invalid@example.com) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/privileges/remove: + post: + tags: + - Mail + summary: Remove mail user privilege + description: Removes a privilege from an existing mail user. + operationId: removeMailUserPrivilege + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserRemovePrivilegeRequest' + example: + email: user@example.com + privilege: admin + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/privileges/remove" \ + -d "email=" \ + -d "privilege=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserRemovePrivilegeResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: That's not a user (invalid@example.com) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/password: + post: + tags: + - Mail + summary: Set mail user password + description: Sets a password for an existing mail user. + operationId: setMailUserPassword + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailUserSetPasswordRequest' + example: + email: user@example.com + password: s3curE_pa5Sw0rD + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/users/password" \ + -d "email=" \ + -d "password=" \ + -u ":" \ + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserSetPasswordResponse' + example: OK + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: Passwords must be at least eight characters + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/users/privileges: + get: + tags: + - Mail + summary: Get mail user privileges + description: Returns all privileges for an existing mail user. + operationId: getMailUserPrivileges + parameters: + - in: query + name: email + schema: + $ref: '#/components/schemas/Email' + description: The email you want to get privileges for. + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/mail/users/privileges?email=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailUserPrivilegesResponse' + example: admin + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/domains: + get: + tags: + - Mail + summary: Get mail domains + description: Returns all mail domains. + operationId: getMailDomains + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/mail/domains" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailDomainsResponse' + example: | + example1.com + example2.com + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/aliases: + get: + tags: + - Mail + summary: Get mail aliases + description: Returns all mail aliases. + operationId: getMailAliases + parameters: + - in: query + name: format + schema: + $ref: '#/components/schemas/MailAliasesResponseFormat' + description: The format of the response. + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/mail/aliases?format=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/MailAliasByDomain' + text/html: + schema: + $ref: '#/components/schemas/MailAliasesSimpleResponse' + example: | + abuse@example.com administrator@example.com + admin@example.com administrator@example.com + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/aliases/add: + post: + tags: + - Mail + summary: Upsert mail alias + description: | + Adds or updates a mail alias. If updating, you need to set `update_if_exists: 1`. + operationId: upsertMailAlias + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailAliasUpsertRequest' + examples: + regular: + summary: Regular alias + value: + update_if_exists: 0 + address: user@example.com + forwards_to: user2@example.com + permitted_senders: + catchall: + summary: Catch-all + value: + update_if_exists: 0 + address: '@example.com' + forwards_to: user@otherexample.com + permitted_senders: + domainalias: + summary: Domain alias + value: + update_if_exists: 0 + address: '@example.com' + forwards_to: '@otherexample.com' + permitted_senders: + update: + summary: Update existing alias + value: + update_if_exists: 1 + address: user@example.com + forwards_to: user2@example.com + permitted_senders: user3@example.com, user4@example.com + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/aliases/add" \ + -d "update_if_exists=" \ + -d "address=" \ + -d "forwards_to=" \ + -d "permitted_senders=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailAliasUpsertResponse' + example: alias updated + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: Invalid email address (invalid@example.com) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /mail/aliases/remove: + post: + tags: + - Mail + summary: Remove mail alias + description: Removes a mail alias. + operationId: removeMailAlias + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/MailAliasRemoveRequest' + example: + address: user@example.com + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/mail/aliases/remove" \ + -d "address=" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/MailAliasRemoveResponse' + example: alias removed + 400: + description: Bad request + content: + text/html: + schema: + type: string + example: That's not an alias (invalid@example) + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /web/domains: + get: + tags: + - Web + summary: Get web domains + description: Returns all static web domains. + operationId: getWebDomains + x-codeSamples: + - lang: curl + source: | + curl -X GET "https://{host}/admin/web/domains" \ + -u ":" + responses: + 200: + description: Successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/WebDomain' + 403: + description: Forbidden + content: + text/html: + schema: + type: string + /web/update: + post: + tags: + - Web + summary: Update web + description: Updates static websites, used for updating domain root directories. + operationId: updateWeb + x-codeSamples: + - lang: curl + source: | + curl -X POST "https://{host}/admin/web/update" \ + -u ":" + responses: + 200: + description: Successful operation + content: + text/html: + schema: + $ref: '#/components/schemas/WebUpdateResponse' + example: web updated + 403: + description: Forbidden + content: + text/html: + schema: + type: string +components: + securitySchemes: + basicAuth: + type: http + scheme: basic + description: | + Credentials can be supplied using the `Authorization` header in + format `Authorization: Basic {access-token}`. + + The `access-token` is comprised of the Base64 encoding of `username:password`. + The `username` is the mail user's email address, and `password` can either be the mail user's + password, or the `api_key` returned from the `getMe` operation. + + When using `curl`, you can supply user credentials using the `-u` or `--user` parameter. + requestBodies: + DNSCustomRecordRequest: + required: true + content: + text/plain: + schema: + type: string + example: 1.2.3.4 + description: The value of the DNS record. + example: '1.2.3.4' + schemas: + MailUsersResponseFormat: + type: string + enum: + - text + - json + example: json + description: Response format (`application/json` or `text/html`). + MailAliasesResponseFormat: + type: string + enum: + - text + - json + example: json + description: Response format (`application/json` or `text/html`). + MailUserSetPasswordResponse: + type: string + example: OK + description: Mail user set password response. + MailUserRemoveResponse: + type: string + example: OK + description: Mail user remove response. + MailUserAddResponse: + type: string + example: | + mail user added + updated DNS: OpenDKIM configuration + description: | + Mail user add response. + + Can include information about operations related to adding new users, like updating DNS. + MailUserAddPrivilegeResponse: + type: string + example: OK + description: Mail user add admin privilege response. + MailUserRemovePrivilegeResponse: + type: string + example: OK + description: Mail user remove admin privilege response. + MailUsersSimpleResponse: + type: string + example: | + user1@example.com + user2@example.com + description: Get mail users text format response. + MailUserPrivilegesResponse: + $ref: '#/components/schemas/MailUserPrivilege' + description: Mail user privileges response. + example: admin + MailDomainsResponse: + type: string + example: | + example1.com + example2.com + description: Mail domains response. + MailUsersResponse: + type: array + items: + $ref: '#/components/schemas/MailUserByDomain' + description: Get mail aliases JSON format response. + MailUserByDomain: + type: object + required: + - domain + - users + properties: + domain: + $ref: '#/components/schemas/Hostname' + users: + type: array + items: + $ref: '#/components/schemas/MailUser' + description: Mail users by domain. + MailUser: + type: object + required: + - email + - privileges + - status + properties: + email: + $ref: '#/components/schemas/Email' + privileges: + type: array + items: + $ref: '#/components/schemas/MailUserPrivilege' + status: + $ref: '#/components/schemas/MailUserStatus' + mailbox: + type: string + example: /home/user-data/mail/mailboxes/example.com/user + description: Mail user details. + MailAliasesSimpleResponse: + type: string + example: | + abuse@example.com administrator@example.com + admin@example.com administrator@example.com + description: Get mail aliases text format response. + MailAliasByDomain: + type: object + required: + - domain + - aliases + properties: + domain: + $ref: '#/components/schemas/Hostname' + aliases: + type: array + items: + $ref: '#/components/schemas/MailAlias' + description: Mail aliases by domain. + MailAlias: + type: object + required: + - address + - address_display + - forwards_to + - permitted_senders + - required + properties: + address: + $ref: '#/components/schemas/Email' + address_display: + $ref: '#/components/schemas/Email' + forwards_to: + type: array + items: + $ref: '#/components/schemas/Email' + permitted_senders: + type: array + nullable: true + items: + $ref: '#/components/schemas/Email' + required: + type: boolean + example: true + description: Mail alias details. + MailAliasUpsertResponse: + type: string + example: alias updated + description: Mail alias add/update response. + MailAliasUpsertRequest: + type: object + required: + - update_if_exists + - address + - forwards_to + - permitted_senders + properties: + update_if_exists: + type: integer + format: int32 + minimum: 0 + maximum: 1 + example: 1 + description: Set to `1` when updating an alias. + address: + $ref: '#/components/schemas/Email' + forwards_to: + type: string + example: user1@example.com, user2@example.com + description: | + If adding a regular or catch-all alias, the format needs to be `user@example.com`. + Multiple address can be separated by newlines or commas. + + If adding a domain alias, the format needs to be `@example.com`. + permitted_senders: + type: string + nullable: true + example: user1@example.com, user2@example.com + description: | + Mail users that can send mail claiming to be from any address on the alias domain. + Multiple address can be separated by newlines or commas. + + Leave empty to allow any mail user listed in `forwards_to` to send mail claiming to be from any address on the alias domain. + description: Mail alias upsert request. + MailAliasRemoveResponse: + type: string + example: alias removed + description: Mail alias remove response. + MailAliasRemoveRequest: + type: object + required: + - address + properties: + address: + $ref: '#/components/schemas/Email' + description: Mail aliases remove request. + DNSRecordType: + enum: + - A + - AAAA + - CAA + - CNAME + - TXT + - MX + - SRV + - SSHFP + - NS + example: MX + description: DNS record type. + DNSDumpResponse: + type: array + items: + $ref: '#/components/schemas/DNSDumpDomains' + description: DNS dump response. + DNSDumpDomains: + type: array + items: + oneOf: + - $ref: '#/components/schemas/Hostname' + - $ref: '#/components/schemas/DNSDumpDomainRecords' + description: | + A list of records per domain. + + The first item in the list is the domain and the second item is the list of records. + DNSDumpDomainRecords: + type: array + items: + $ref: '#/components/schemas/DNSDumpDomainRecord' + description: List of domain records. + DNSDumpDomainRecord: + type: object + required: + - explanation + - qname + - type + - value + properties: + explanation: + type: string + example: Required. Specifies the hostname (and priority) of the machine that handles @example.com mail + qname: + $ref: '#/components/schemas/Hostname' + rtype: + $ref: '#/components/schemas/DNSRecordType' + value: + type: string + example: 10 example.com. + description: Domain DNS record details. + DNSCustomRecord: + type: object + required: + - qname + - rtype + - value + properties: + qname: + $ref: '#/components/schemas/Hostname' + rtype: + $ref: '#/components/schemas/DNSRecordType' + value: + type: string + example: 10 example.com. + description: Custom DNS record detail detail. + DNSCustomRecordsResponse: + type: array + items: + $ref: '#/components/schemas/DNSCustomRecord' + description: Custom DNS records response. + DNSZonesResponse: + type: array + items: + $ref: '#/components/schemas/Hostname' + description: DNS zones response. + DNSSecondaryNameserverResponse: + type: object + required: + - hostnames + properties: + hostnames: + type: array + items: + type: string + example: ns1.example.com + description: Secondary nameserver/s response. + DNSCustomRecordRemoveResponse: + type: string + example: 'updated DNS: example.com' + description: Custom DNS record remove response. + DNSCustomRecordUpsertResponse: + type: string + example: 'updated DNS: example.com' + description: Custom DNS record add response. + DNSUpdateRequest: + type: object + required: + - force + properties: + force: + type: integer + format: int32 + minimum: 0 + maximum: 1 + example: 1 + description: Force an update even if mailinabox detects no changes are required. + description: DNS update request. + DNSUpdateResponse: + type: string + example: | + updated DNS: example1.com,example2.com + description: DNS update response. + DNSSecondaryNameserverAddRequest: + type: object + required: + - hostnames + properties: + hostnames: + type: string + description: Hostnames separated with commas or spaces. + example: ns2.hostingcompany.com, ns3.hostingcompany.com + description: Secondary nameserver/s add request. + DNSSecondaryNameserverAddResponse: + type: string + example: 'updated DNS: example.com' + description: Secondary nameserver/s add response. + SystemPrivacyUpdateRequest: + type: object + required: + - value + properties: + value: + $ref: '#/components/schemas/SystemPrivacyStatus' + description: Update system privacy request. + SystemPrivacyStatus: + type: string + enum: + - private + - 'off' + example: private + description: System privacy status. + MailUserSetPasswordRequest: + type: object + required: + - email + - password + properties: + email: + $ref: '#/components/schemas/Email' + password: + type: string + format: password + description: Mail user set password request. + MailUserAddRequest: + type: object + required: + - email + - password + - privileges + properties: + email: + $ref: '#/components/schemas/Email' + password: + type: string + format: password + privileges: + $ref: '#/components/schemas/MailUserPrivilege' + description: Mail user add request. + MailUserRemoveRequest: + type: object + required: + - email + properties: + email: + $ref: '#/components/schemas/Email' + description: Mail user remove request. + MailUserStatus: + type: string + enum: + - active + - inactive + example: active + description: Mail user status. + MailUserPrivilege: + type: string + enum: + - admin + - '' + example: admin + description: Mail user privilege. + MailUserAddPrivilegeRequest: + type: object + required: + - email + - privilege + properties: + email: + $ref: '#/components/schemas/Email' + privilege: + $ref: '#/components/schemas/MailUserPrivilege' + description: Mail user add privilege request. + MailUserRemovePrivilegeRequest: + type: object + required: + - email + - privilege + properties: + email: + $ref: '#/components/schemas/Email' + privilege: + $ref: '#/components/schemas/MailUserPrivilege' + description: Mail user remove privilege request. + SSLCSRGenerateRequest: + type: object + required: + - countrycode + properties: + countrycode: + type: string + example: GB + description: Generate SSL CSR request. + SSLCSRGenerateResponse: + type: string + example: | + -----BEGIN CERTIFICATE REQUEST----- + MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3K6dwLM2Nk8kVhIBaZmp + eY6y7O0T3jrexEKlW839TVYdcH+K35V1NxilbMFKMuHeowGwFyyiqOy/OUYNeq+T + Rz3s4b1qG2p01dwlsXHHYmXLYTAhvqvY+CU5ksieuZbyHRTwbHViQ0xtRXwoVCnj + CkN7kJVpkLfVN0/BG6NBFpv/JI8F+hwp+IHdkC1gUXRrLJNC79ERqFP8HoqdQWNw + OGGFaOe2aQhvj2zt8wFncyKVc40UKVbSzGGzdL2MPiAJHgZ2lmeY1xDyX1lOt12R + IFPwtxmbxaxYaVfe2hxl7m88xV3OjYcKgwVYDusk2XJ37cGew5g+NbBvzEeEUpF9 + 5wIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAD7UPC3/Nkgpn53mT9puUonYdJg9 + SD8vvTK/N78CzoEgPNyq+bYbqlcvVPKIdItf9TMiqfOSvW3e3NvkRisYle8Qp+0C + 8pafXBvQ9eHt5CFeJn4sH9GnxeflOZT/P9Jnp71KtZQvOobirX4GgEWs79g+/NHb + Zyf8rbadt9HruNhKA5nlP8cn7Rdc/iuJU8MVSQszI1s1DEcXMPxr6iqb2g87/ifH + lWcK59kvRJkCcPhPzjpUy9NulucH4WFA/WqKeDNFS/oC+upV5w8EDEcfnenJFG+N + JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0= + -----END CERTIFICATE REQUEST----- + description: Generate SSL CSR response. + SSLCertificateInstallRequest: + type: object + required: + - domain + - cert + - chain + properties: + domain: + $ref: '#/components/schemas/Hostname' + cert: + type: string + description: TLS/SSL certificate. + example: | + -----BEGIN CERTIFICATE----- + MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3K6dwLM2Nk8kVhIBaZmp + eY6y7O0T3jrexEKlW839TVYdcH+K35V1NxilbMFKMuHeowGwFyyiqOy/OUYNeq+T + Rz3s4b1qG2p01dwlsXHHYmXLYTAhvqvY+CU5ksieuZbyHRTwbHViQ0xtRXwoVCnj + CkN7kJVpkLfVN0/BG6NBFpv/JI8F+hwp+IHdkC1gUXRrLJNC79ERqFP8HoqdQWNw + OGGFaOe2aQhvj2zt8wFncyKVc40UKVbSzGGzdL2MPiAJHgZ2lmeY1xDyX1lOt12R + IFPwtxmbxaxYaVfe2hxl7m88xV3OjYcKgwVYDusk2XJ37cGew5g+NbBvzEeEUpF9 + 5wIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAD7UPC3/Nkgpn53mT9puUonYdJg9 + SD8vvTK/N78CzoEgPNyq+bYbqlcvVPKIdItf9TMiqfOSvW3e3NvkRisYle8Qp+0C + 8pafXBvQ9eHt5CFeJn4sH9GnxeflOZT/P9Jnp71KtZQvOobirX4GgEWs79g+/NHb + Zyf8rbadt9HruNhKA5nlP8cn7Rdc/iuJU8MVSQszI1s1DEcXMPxr6iqb2g87/ifH + lWcK59kvRJkCcPhPzjpUy9NulucH4WFA/WqKeDNFS/oC+upV5w8EDEcfnenJFG+N + JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0= + -----END CERTIFICATE----- + chain: + type: string + description: TLS/SSL intermediate chain (if provided, else empty string). + example: | + -----BEGIN CERTIFICATE----- + MIICaDCCAVACAQAwIzELMAkGA1UEBhMCQlMxFDASBgNVBAMMC2V4YW1wbGUuY29t + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3K6dwLM2Nk8kVhIBaZmp + eY6y7O0T3jrexEKlW839TVYdcH+K35V1NxilbMFKMuHeowGwFyyiqOy/OUYNeq+T + Rz3s4b1qG2p01dwlsXHHYmXLYTAhvqvY+CU5ksieuZbyHRTwbHViQ0xtRXwoVCnj + CkN7kJVpkLfVN0/BG6NBFpv/JI8F+hwp+IHdkC1gUXRrLJNC79ERqFP8HoqdQWNw + OGGFaOe2aQhvj2zt8wFncyKVc40UKVbSzGGzdL2MPiAJHgZ2lmeY1xDyX1lOt12R + IFPwtxmbxaxYaVfe2hxl7m88xV3OjYcKgwVYDusk2XJ37cGew5g+NbBvzEeEUpF9 + 5wIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAD7UPC3/Nkgpn53mT9puUonYdJg9 + SD8vvTK/N78CzoEgPNyq+bYbqlcvVPKIdItf9TMiqfOSvW3e3NvkRisYle8Qp+0C + 8pafXBvQ9eHt5CFeJn4sH9GnxeflOZT/P9Jnp71KtZQvOobirX4GgEWs79g+/NHb + Zyf8rbadt9HruNhKA5nlP8cn7Rdc/iuJU8MVSQszI1s1DEcXMPxr6iqb2g87/ifH + lWcK59kvRJkCcPhPzjpUy9NulucH4WFA/WqKeDNFS/oC+upV5w8EDEcfnenJFG+N + JmFDQESSfUxLPHLC660Wnf3GmrP/duZHpPC+qTe8b1AlQ7zDT3cOaAQ+Mb0= + -----END CERTIFICATE----- + description: Install certificate request. `chain` can be an empty string. + SSLCertificateInstallResponse: + type: string + example: OK + description: Install certificate response. + SSLCertificatesProvisionResponse: + type: object + required: + - requests + properties: + requests: + type: array + items: + type: object + required: + - log + - result + - domains + properties: + log: + type: array + items: + type: string + example: + - 'The domain name does not resolve to this machine: [Not Set] (A), [Not Set] (AAAA).' + result: + type: string + enum: + - installed + - error + - skipped + example: installed + domains: + type: array + items: + $ref: '#/components/schemas/Hostname' + description: SSL certificates provision response. + SystemPrivacyStatusResponse: + type: boolean + description: | + System privacy status response. + + - `true`: Private, new-version checks will not be performed + - `false`: Not private, new-version checks will be performed + example: false + SystemVersionResponse: + type: string + description: System version response. + example: v0.46 + SystemVersionUpstreamResponse: + type: string + description: System version upstream response. + example: v0.47 + SystemUpdatesResponse: + type: string + description: System updates response. + example: | + libgnutls30 (3.5.18-1ubuntu1.4) + libxau6 (1:1.0.8-1ubuntu1) + SystemUpdatePackagesResponse: + type: string + example: | + Reading package lists... + Building dependency tree... + Reading state information... + Calculating upgrade... + The following packages will be upgraded: + cloud-init grub-common grub-pc grub-pc-bin grub2-common libgnutls30 + libldap-2.4-2 libldap-common libxau6 linux-firmware python3-distupgrade + qemu-guest-agent sosreport ubuntu-release-upgrader-core + 14 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. + Need to get 79.9 MB of archives. + After this operation, 3893 kB of additional disk space will be used. + Get:1 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libgnutls30 amd64 3.5.18-1ubuntu1.4 [645 kB] + Preconfiguring packages ... + Fetched 79.9 MB in 2s (52.4 MB/s) + (Reading database ... 48457 files and directories currently installed.) + description: System update packages response. + SystemPrivacyUpdateResponse: + type: string + example: OK + description: System privacy update response. + SystemRebootStatusResponse: + type: boolean + description: | + System reboot status response. + + - `true`: A reboot is required + - `false`: A reboot is not required + example: true + SystemRebootResponse: + type: string + example: No reboot is required, so it is not allowed. + description: System reboot response. + SystemStatusResponse: + type: array + items: + $ref: '#/components/schemas/StatusEntry' + description: System status response. + StatusEntry: + type: object + required: + - type + - text + - extra + properties: + type: + $ref: '#/components/schemas/StatusEntryType' + text: + type: string + example: This domain"s DNSSEC DS record is not set + extra: + type: array + items: + $ref: '#/components/schemas/StatusEntryExtra' + description: System status entry. + StatusEntryType: + type: string + enum: + - heading + - ok + - warning + - error + example: warning + description: System status entry type. + StatusEntryExtra: + type: object + required: + - monospace + - text + properties: + monospace: + type: boolean + example: false + text: + type: string + example: 'Digest Type: 2 / SHA-256' + description: System entry extra information. + SystemBackupConfigUpdateRequest: + type: object + required: + - target + - target_user + - target_pass + - min_age + properties: + target: + type: string + format: hostname + example: s3://s3.eu-central-1.amazonaws.com/box-example-com + target_user: + type: string + example: username + target_pass: + type: string + example: password + format: password + min_age: + type: integer + format: int32 + minimum: 1 + example: 3 + description: Backup config update request. + SystemBackupConfigUpdateResponse: + type: string + example: OK + description: Backup config update response. + SystemBackupConfigResponse: + type: object + required: + - enc_pw_file + - file_target_directory + - min_age_in_days + - ssh_pub_key + - target + properties: + enc_pw_file: + type: string + example: /home/user-data/backup/secret_key.txt + file_target_directory: + type: string + example: /home/user-data/backup/encrypted + min_age_in_days: + type: integer + format: int32 + minimum: 1 + example: 3 + ssh_pub_key: + type: string + example: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDb root@box.example.com\n + target: + type: string + format: hostname + example: s3://s3.eu-central-1.amazonaws.com/box-example-com + target_user: + type: string + target_pass: + type: string + description: Backup config response. + SystemBackupStatusResponse: + type: object + required: + - unmatched_file_size + properties: + backups: + type: array + items: + $ref: '#/components/schemas/SystemBackupStatus' + unmatched_file_size: + type: integer + format: int32 + example: 0 + error: + type: string + example: Something is wrong with the backup + description: Backup status response. Lists the status for all backups. + SystemBackupStatus: + type: object + required: + - date + - date_delta + - date_str + - full + - size + - volumes + properties: + date: + type: string + format: date-time + example: 20200801T023706Z + date_delta: + type: string + example: 15 hours, 40 minutes + date_str: + type: string + example: 2020-08-01 03:37:06 BST + deleted_in: + type: string + example: approx. 6 days + full: + type: boolean + example: false + size: + type: integer + format: int32 + example: 125332 + volumes: + type: integer + format: int32 + example: 1 + description: Backup status details. + SSLStatusResponse: + type: object + required: + - can_provision + - status + properties: + can_provision: + type: array + items: + type: string + status: + type: array + items: + $ref: '#/components/schemas/SSLStatus' + description: SSL status response for all relevant domains. + SSLStatus: + type: object + required: + - domain + - status + - text + properties: + domain: + $ref: '#/components/schemas/Hostname' + status: + $ref: '#/components/schemas/SSLStatusType' + text: + type: string + example: Signed & valid. The certificate expires in 87 days on 10/28/20. + description: SSL status details for domain. + SSLStatusType: + type: string + enum: + - success + - danger + - not-applicable + example: success + description: SSL status type. + Email: + type: string + format: email + example: user@example.com + description: Email format. + Hostname: + type: string + format: hostname + example: example.com + description: Hostname format. + MeResponse: + type: object + required: + - status + properties: + api_key: + type: string + example: 12345abcde + email: + $ref: '#/components/schemas/Email' + privileges: + type: array + items: + $ref: '#/components/schemas/MailUserPrivilege' + reason: + type: string + example: Incorrect username or password + status: + $ref: '#/components/schemas/MeAuthStatus' + description: Me (user) response. + MeAuthStatus: + type: string + enum: + - ok + - invalid + example: invalid + description: Me (user) authentication result. + WebDomain: + type: object + required: + - custom_root + - domain + - root + - ssl_certificate + - static_enabled + properties: + custom_root: + type: string + example: /home/user-data/www/example.com + domain: + $ref: '#/components/schemas/Hostname' + root: + type: string + example: /home/user-data/www/default + ssl_certificate: + type: array + minItems: 2 + maxItems: 2 + uniqueItems: true + items: + oneOf: + - type: string + example: No certificate installed. + - type: string + enum: + - danger + - success + example: danger + static_enabled: + type: boolean + example: true + description: Web domain details. + WebUpdateResponse: + type: string + example: web updated + description: Web update response. diff --git a/conf/www_default.html b/conf/www_default.html index 21dbcda..e78b0bb 100644 --- a/conf/www_default.html +++ b/conf/www_default.html @@ -2,6 +2,7 @@ Redirecting... + diff --git a/management/dns_update.py b/management/dns_update.py index 21ebc53..46fb4ed 100755 --- a/management/dns_update.py +++ b/management/dns_update.py @@ -351,14 +351,10 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en ("_mta-sts", "TXT", "v=STSv1; id=" + mta_sts_policy_id, "Optional. Part of the MTA-STS policy for incoming mail. If set, a MTA-STS policy must also be published.") ]) - # Rules can be custom configured accoring to https://tools.ietf.org/html/rfc8460. + # Enable SMTP TLS reporting (https://tools.ietf.org/html/rfc8460) if the user has set a config option. # Skip if the rules below if the user has set a custom _smtp._tls record. - if not has_rec("_smtp._tls", "TXT", prefix="v=TLSRPTv1;"): - tls_rpt_string = "" - tls_rpt_email = env.get("MTA_STS_TLSRPT_EMAIL", "postmaster@%s" % env['PRIMARY_HOSTNAME']) - if tls_rpt_email: # if a reporting address is not cleared - tls_rpt_string = " rua=mailto:%s" % tls_rpt_email - mta_sts_records.append(("_smtp._tls", "TXT", "v=TLSRPTv1;%s" % tls_rpt_string, "Optional. Enables MTA-STS reporting.")) + if env.get("MTA_STS_TLSRPT_RUA") and not has_rec("_smtp._tls", "TXT", prefix="v=TLSRPTv1;"): + mta_sts_records.append(("_smtp._tls", "TXT", "v=TLSRPTv1; rua=" + env["MTA_STS_TLSRPT_RUA"], "Optional. Enables MTA-STS reporting.")) for qname, rtype, value, explanation in mta_sts_records: if value is None or value.strip() == "": continue # skip IPV6 if not set if not has_rec(qname, rtype): @@ -967,20 +963,17 @@ def set_secondary_dns(hostnames, env): try: response = resolver.resolve(item, "A") except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): - response = resolver.resolve(item, "AAAA") - except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): - raise ValueError("Could not resolve the IP address of %s." % item) + try: + response = resolver.query(item, "AAAA") + except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): + raise ValueError("Could not resolve the IP address of %s." % item) else: # Validate IP address. try: if "/" in item[4:]: v = ipaddress.ip_network(item[4:]) # raises a ValueError if there's a problem - if not isinstance(v, ipaddress.IPv4Network) and not isinstance(v, ipaddress.IPv6Network): - raise ValueError("That's neither an IPv4 or IPv6 subnet.") else: v = ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem - if not isinstance(v, ipaddress.IPv4Address) and not isinstance(v, ipaddress.IPv6Address): - raise ValueError("That's neither an IPv4 or IPv6 address.") except ValueError: raise ValueError("'%s' is not an IPv4 or IPv6 address or subnet." % item[4:]) diff --git a/management/mailconfig.py b/management/mailconfig.py index dd597cd..b061ea7 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -605,8 +605,6 @@ def validate_password(pw): # validate password if pw.strip() == "": raise ValueError("No password provided.") - if re.search(r"[\s]", pw): - raise ValueError("Passwords cannot contain spaces.") if len(pw) < 8: raise ValueError("Passwords must be at least eight characters.") diff --git a/management/templates/aliases.html b/management/templates/aliases.html index e8d0cb1..848fcf4 100644 --- a/management/templates/aliases.html +++ b/management/templates/aliases.html @@ -288,7 +288,7 @@ function aliases_remove(elem) { }, function(r) { // Responses are multiple lines of pre-formatted text. - show_modal_error("Remove User", $("
").text(r));
+          show_modal_error("Remove Alias", $("
").text(r));
           show_aliases();
         });
     });
diff --git a/management/templates/custom-dns.html b/management/templates/custom-dns.html
index a2d5042..6984b08 100644
--- a/management/templates/custom-dns.html
+++ b/management/templates/custom-dns.html
@@ -90,7 +90,7 @@
     

Multiple secondary servers can be separated with commas or spaces (i.e., ns2.hostingcompany.com ns3.hostingcompany.com). - To enable zone transfers to additional servers without listing them as secondary nameservers, add an IP address or subnet using xfr:10.20.30.40 or xfr:10.20.30.40/24. + To enable zone transfers to additional servers without listing them as secondary nameservers, add an IP address or subnet using xfr:10.20.30.40 or xfr:10.0.0.0/8.