mirror of
				https://github.com/mail-in-a-box/mailinabox.git
				synced 2025-11-03 19:30:54 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/mail-in-a-box/mailinabox
# Conflicts: # setup/management.sh
This commit is contained in:
		
						commit
						24c156c594
					
				@ -15,7 +15,7 @@ info:
 | 
			
		||||
  license:
 | 
			
		||||
    name: CC0 1.0 Universal
 | 
			
		||||
    url: https://creativecommons.org/publicdomain/zero/1.0/legalcode
 | 
			
		||||
  version: 0.47.0
 | 
			
		||||
  version: 0.51.0
 | 
			
		||||
  x-logo:
 | 
			
		||||
    url: https://mailinabox.email/static/logo.png
 | 
			
		||||
    altText: Mail-in-a-Box logo
 | 
			
		||||
@ -744,30 +744,37 @@ paths:
 | 
			
		||||
              schema:
 | 
			
		||||
                type: string
 | 
			
		||||
  /dns/zonefile/{zone}:
 | 
			
		||||
      get:
 | 
			
		||||
          tags:
 | 
			
		||||
              - DNS
 | 
			
		||||
          summary: Get DNS zonefile
 | 
			
		||||
          description: Returns an array of all managed top-level domains.
 | 
			
		||||
          operationId: getDnsZonefile
 | 
			
		||||
          x-codeSamples:
 | 
			
		||||
              - lang: curl
 | 
			
		||||
                source: |
 | 
			
		||||
                    curl -X GET "https://{host}/admin/dns/zonefile/<zone>" \
 | 
			
		||||
                      -u "<email>:<password>"
 | 
			
		||||
          responses:
 | 
			
		||||
              200:
 | 
			
		||||
                  description: Successful operation
 | 
			
		||||
                  content:
 | 
			
		||||
                      application/json:
 | 
			
		||||
                          schema:
 | 
			
		||||
                              $ref: '#/components/schemas/DNSZonefileResponse'
 | 
			
		||||
              403:
 | 
			
		||||
                  description: Forbidden
 | 
			
		||||
                  content:
 | 
			
		||||
                      text/html:
 | 
			
		||||
                          schema:
 | 
			
		||||
                              type: string
 | 
			
		||||
    parameters:
 | 
			
		||||
      - in: path
 | 
			
		||||
        name: zone
 | 
			
		||||
        schema:
 | 
			
		||||
          $ref: '#/components/schemas/Hostname'
 | 
			
		||||
        required: true
 | 
			
		||||
        description: Hostname
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - DNS
 | 
			
		||||
      summary: Get DNS zonefile
 | 
			
		||||
      description: Returns a DNS zone file for a hostname.
 | 
			
		||||
      operationId: getDnsZonefile
 | 
			
		||||
      x-codeSamples:
 | 
			
		||||
        - lang: curl
 | 
			
		||||
          source: |
 | 
			
		||||
            curl -X GET "https://{host}/admin/dns/zonefile/<zone>" \
 | 
			
		||||
              -u "<email>:<password>"
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: Successful operation
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                $ref: '#/components/schemas/DNSZonefileResponse'
 | 
			
		||||
        403:
 | 
			
		||||
          description: Forbidden
 | 
			
		||||
          content:
 | 
			
		||||
            text/html:
 | 
			
		||||
              schema:
 | 
			
		||||
                type: string
 | 
			
		||||
  /dns/update:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
@ -1806,7 +1813,7 @@ components:
 | 
			
		||||
        text/plain:
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
            example: 1.2.3.4
 | 
			
		||||
            example: '1.2.3.4'
 | 
			
		||||
            description: The value of the DNS record.
 | 
			
		||||
          example: '1.2.3.4'
 | 
			
		||||
  schemas:
 | 
			
		||||
@ -2690,13 +2697,6 @@ components:
 | 
			
		||||
          type: string
 | 
			
		||||
    MfaEnableSuccessResponse:
 | 
			
		||||
      type: string
 | 
			
		||||
    MfaEnableBadRequestResponse:
 | 
			
		||||
      type: object
 | 
			
		||||
      required:
 | 
			
		||||
        - error
 | 
			
		||||
      properties:
 | 
			
		||||
        error:
 | 
			
		||||
          type: string
 | 
			
		||||
    MfaDisableRequest:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
 | 
			
		||||
@ -459,6 +459,23 @@ def list_target_files(config):
 | 
			
		||||
			raise ValueError(e.reason)
 | 
			
		||||
 | 
			
		||||
		return [(key.name[len(path):], key.size) for key in bucket.list(prefix=path)]
 | 
			
		||||
	elif target.scheme == 'b2':
 | 
			
		||||
		from b2sdk.v1 import InMemoryAccountInfo, B2Api
 | 
			
		||||
		from b2sdk.v1.exception import NonExistentBucket
 | 
			
		||||
		info = InMemoryAccountInfo()
 | 
			
		||||
		b2_api = B2Api(info)
 | 
			
		||||
		
 | 
			
		||||
		# Extract information from target
 | 
			
		||||
		b2_application_keyid = target.netloc[:target.netloc.index(':')]
 | 
			
		||||
		b2_application_key = target.netloc[target.netloc.index(':')+1:target.netloc.index('@')]
 | 
			
		||||
		b2_bucket = target.netloc[target.netloc.index('@')+1:]
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			b2_api.authorize_account("production", b2_application_keyid, b2_application_key)
 | 
			
		||||
			bucket = b2_api.get_bucket_by_name(b2_bucket)
 | 
			
		||||
		except NonExistentBucket as e:
 | 
			
		||||
			raise ValueError("B2 Bucket does not exist. Please double check your information!")
 | 
			
		||||
		return [(key.file_name, key.size) for key, _ in bucket.ls()]
 | 
			
		||||
 | 
			
		||||
	else:
 | 
			
		||||
		raise ValueError(config["target"])
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,11 @@
 | 
			
		||||
#!/usr/local/lib/mailinabox/env/bin/python3
 | 
			
		||||
#
 | 
			
		||||
# During development, you can start the Mail-in-a-Box control panel
 | 
			
		||||
# by running this script, e.g.:
 | 
			
		||||
#
 | 
			
		||||
# service mailinabox stop # stop the system process
 | 
			
		||||
# DEBUG=1 management/daemon.py
 | 
			
		||||
# service mailinabox start # when done debugging, start it up again
 | 
			
		||||
 | 
			
		||||
import os, os.path, re, json, time
 | 
			
		||||
import multiprocessing.pool, subprocess
 | 
			
		||||
@ -690,7 +697,22 @@ def log_failed_login(request):
 | 
			
		||||
# APP
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
	if "DEBUG" in os.environ: app.debug = True
 | 
			
		||||
	if "DEBUG" in os.environ:
 | 
			
		||||
		# Turn on Flask debugging.
 | 
			
		||||
		app.debug = True
 | 
			
		||||
 | 
			
		||||
		# Use a stable-ish master API key so that login sessions don't restart on each run.
 | 
			
		||||
		# Use /etc/machine-id to seed the key with a stable secret, but add something
 | 
			
		||||
		# and hash it to prevent possibly exposing the machine id, using the time so that
 | 
			
		||||
		# the key is not valid indefinitely.
 | 
			
		||||
		import hashlib
 | 
			
		||||
		with open("/etc/machine-id") as f:
 | 
			
		||||
			api_key = f.read()
 | 
			
		||||
		api_key += "|" + str(int(time.time() / (60*60*2)))
 | 
			
		||||
		hasher = hashlib.sha1()
 | 
			
		||||
		hasher.update(api_key.encode("ascii"))
 | 
			
		||||
		auth_service.key = hasher.hexdigest()
 | 
			
		||||
 | 
			
		||||
	if "APIKEY" in os.environ: auth_service.key = os.environ["APIKEY"]
 | 
			
		||||
 | 
			
		||||
	if not app.debug:
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@
 | 
			
		||||
        <option value="local">{{hostname}}</option>
 | 
			
		||||
        <option value="rsync">rsync</option>
 | 
			
		||||
        <option value="s3">Amazon S3</option>
 | 
			
		||||
        <option value="b2">Backblaze B2</option>
 | 
			
		||||
      </select>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
@ -111,6 +112,31 @@
 | 
			
		||||
      <input type="text" class="form-control" rows="1" id="backup-target-pass">
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <!-- Backblaze -->
 | 
			
		||||
  <div class="form-group backup-target-b2">
 | 
			
		||||
      <div class="col-sm-10 col-sm-offset-2">
 | 
			
		||||
        <p>Backups are stored in a <a href="https://www.backblaze.com/" target="_blank" rel="noreferrer">Backblaze</a> B2 bucket. You must have a Backblaze account already.</p>
 | 
			
		||||
        <p>You MUST manually copy the encryption password from <tt class="backup-encpassword-file"></tt> to a safe and secure location. You will need this file to decrypt backup files. It is NOT stored in your Backblaze B2 bucket.</p>
 | 
			
		||||
      </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="form-group backup-target-b2">
 | 
			
		||||
      <label for="backup-target-b2-user" class="col-sm-2 control-label">B2 Application KeyID</label>
 | 
			
		||||
      <div class="col-sm-8">
 | 
			
		||||
        <input type="text" class="form-control" rows="1" id="backup-target-b2-user">
 | 
			
		||||
      </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="form-group backup-target-b2">
 | 
			
		||||
      <label for="backup-target-b2-pass" class="col-sm-2 control-label">B2 Application Key</label>
 | 
			
		||||
      <div class="col-sm-8">
 | 
			
		||||
        <input type="text" class="form-control" rows="1" id="backup-target-b2-pass">
 | 
			
		||||
      </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="form-group backup-target-b2">
 | 
			
		||||
      <label for="backup-target-b2-bucket" class="col-sm-2 control-label">B2 Bucket</label>
 | 
			
		||||
      <div class="col-sm-8">
 | 
			
		||||
        <input type="text" class="form-control" rows="1" id="backup-target-b2-bucket">
 | 
			
		||||
      </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <!-- Common -->
 | 
			
		||||
  <div class="form-group backup-target-local backup-target-rsync backup-target-s3">
 | 
			
		||||
    <label for="min-age" class="col-sm-2 control-label">Retention Days:</label>
 | 
			
		||||
@ -144,7 +170,7 @@
 | 
			
		||||
 | 
			
		||||
function toggle_form() {
 | 
			
		||||
  var target_type = $("#backup-target-type").val();
 | 
			
		||||
  $(".backup-target-local, .backup-target-rsync, .backup-target-s3").hide();
 | 
			
		||||
  $(".backup-target-local, .backup-target-rsync, .backup-target-s3, .backup-target-b2").hide();
 | 
			
		||||
  $(".backup-target-" + target_type).show();
 | 
			
		||||
 | 
			
		||||
  init_inputs(target_type);
 | 
			
		||||
@ -215,7 +241,7 @@ function show_system_backup() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function show_custom_backup() {
 | 
			
		||||
    $(".backup-target-local, .backup-target-rsync, .backup-target-s3").hide();
 | 
			
		||||
    $(".backup-target-local, .backup-target-rsync, .backup-target-s3, .backup-target-b2").hide();
 | 
			
		||||
    api(
 | 
			
		||||
      "/system/backup/config",
 | 
			
		||||
      "GET",
 | 
			
		||||
@ -245,6 +271,15 @@ function show_custom_backup() {
 | 
			
		||||
          var host = hostpath.shift();
 | 
			
		||||
          $("#backup-target-s3-host").val(host);
 | 
			
		||||
          $("#backup-target-s3-path").val(hostpath.join('/'));
 | 
			
		||||
        } else if (r.target.substring(0, 5) == "b2://") {
 | 
			
		||||
          $("#backup-target-type").val("b2");
 | 
			
		||||
          var targetPath = r.target.substring(5);
 | 
			
		||||
          var b2_application_keyid = targetPath.split(':')[0];
 | 
			
		||||
          var b2_applicationkey = targetPath.split(':')[1].split('@')[0];
 | 
			
		||||
          var b2_bucket = targetPath.split('@')[1];
 | 
			
		||||
          $("#backup-target-b2-user").val(b2_application_keyid);
 | 
			
		||||
          $("#backup-target-b2-pass").val(b2_applicationkey);
 | 
			
		||||
          $("#backup-target-b2-bucket").val(b2_bucket);
 | 
			
		||||
        }
 | 
			
		||||
        toggle_form()
 | 
			
		||||
      })
 | 
			
		||||
@ -264,6 +299,11 @@ function set_custom_backup() {
 | 
			
		||||
    target = "rsync://" + $("#backup-target-rsync-user").val() + "@" + $("#backup-target-rsync-host").val()
 | 
			
		||||
                                                                + "/" + $("#backup-target-rsync-path").val();
 | 
			
		||||
    target_user = '';
 | 
			
		||||
  } else if (target_type == "b2") {
 | 
			
		||||
    target = 'b2://' + $('#backup-target-b2-user').val() + ':' + $('#backup-target-b2-pass').val()
 | 
			
		||||
        + '@' + $('#backup-target-b2-bucket').val()
 | 
			
		||||
    target_user = '';
 | 
			
		||||
    target_pass = '';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,11 +18,7 @@ while [ -d /usr/local/lib/python3.4/dist-packages/acme ]; do
 | 
			
		||||
	pip3 uninstall -y acme;
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# duplicity is used to make backups of user data. It uses boto
 | 
			
		||||
# (via Python 2) to do backups to AWS S3. boto from the Ubuntu
 | 
			
		||||
# package manager is too out-of-date -- it doesn't support the newer
 | 
			
		||||
# S3 api used in some regions, which breaks backups to those regions.
 | 
			
		||||
# See #627, #653.
 | 
			
		||||
# duplicity is used to make backups of user data.
 | 
			
		||||
#
 | 
			
		||||
# virtualenv is used to isolate the Python 3 packages we
 | 
			
		||||
# install via pip from the system-installed packages.
 | 
			
		||||
@ -30,7 +26,11 @@ done
 | 
			
		||||
# certbot installs EFF's certbot which we use to
 | 
			
		||||
# provision free TLS certificates.
 | 
			
		||||
apt_install duplicity python-pip virtualenv certbot
 | 
			
		||||
hide_output pip2 install --upgrade boto
 | 
			
		||||
 | 
			
		||||
# b2sdk is used for backblaze backups.
 | 
			
		||||
# boto is used for amazon aws backups.
 | 
			
		||||
# Both are installed outside the pipenv, so they can be used by duplicity
 | 
			
		||||
hide_output pip3 install --upgrade b2sdk boto
 | 
			
		||||
 | 
			
		||||
# Create a virtualenv for the installation of Python 3 packages
 | 
			
		||||
# used by the management daemon.
 | 
			
		||||
@ -51,7 +51,7 @@ hide_output $venv/bin/pip install --upgrade \
 | 
			
		||||
	rtyaml "email_validator>=1.0.0" "exclusiveprocess" \
 | 
			
		||||
	flask dnspython python-dateutil \
 | 
			
		||||
    qrcode[pil] pyotp \
 | 
			
		||||
	"idna>=2.0.0" "cryptography==2.2.2" boto psutil postfix-mta-sts-resolver ldap3
 | 
			
		||||
	"idna>=2.0.0" "cryptography==2.2.2" boto psutil postfix-mta-sts-resolver b2sdk ldap3
 | 
			
		||||
 | 
			
		||||
# CONFIGURATION
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -107,6 +107,9 @@ else
 | 
			
		||||
    ln -sf /snap/bin/certbot /usr/bin/certbot
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Install the duplicity PPA.
 | 
			
		||||
hide_output add-apt-repository -y ppa:duplicity-team/duplicity-release-git
 | 
			
		||||
 | 
			
		||||
# ### Update Packages
 | 
			
		||||
 | 
			
		||||
# Update system packages to make sure we have the latest upstream versions
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user