mirror of
				https://github.com/mail-in-a-box/mailinabox.git
				synced 2025-10-31 19:00:54 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/master'
This commit is contained in:
		
						commit
						57dea6f285
					
				
							
								
								
									
										11
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @ -9,6 +9,17 @@ 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. | * 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. | ||||||
| * MTA-STS reporting is enabled with reports sent to administrator@ the primary hostname. | * MTA-STS reporting is enabled with reports sent to administrator@ the primary hostname. | ||||||
| 
 | 
 | ||||||
|  | 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. | ||||||
|  | 
 | ||||||
|  | v0.46 (June 11, 2020) | ||||||
|  | --------------------- | ||||||
|  | 
 | ||||||
|  | Security fixes: | ||||||
|  | 
 | ||||||
|  | * Roundcube is updated to version 1.4.6 (https://roundcube.net/news/2020/06/02/security-updates-1.4.5-and-1.3.12). | ||||||
|  | 
 | ||||||
| v0.45 (May 16, 2020) | v0.45 (May 16, 2020) | ||||||
| -------------------- | -------------------- | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -63,7 +63,7 @@ by him: | |||||||
| 	$ curl -s https://keybase.io/joshdata/key.asc | gpg --import | 	$ curl -s https://keybase.io/joshdata/key.asc | gpg --import | ||||||
| 	gpg: key C10BDD81: public key "Joshua Tauberer <jt@occams.info>" imported | 	gpg: key C10BDD81: public key "Joshua Tauberer <jt@occams.info>" imported | ||||||
| 
 | 
 | ||||||
| 	$ git verify-tag v0.45 | 	$ git verify-tag v0.46 | ||||||
| 	gpg: Signature made ..... using RSA key ID C10BDD81 | 	gpg: Signature made ..... using RSA key ID C10BDD81 | ||||||
| 	gpg: Good signature from "Joshua Tauberer <jt@occams.info>" | 	gpg: Good signature from "Joshua Tauberer <jt@occams.info>" | ||||||
| 	gpg: WARNING: This key is not certified with a trusted signature! | 	gpg: WARNING: This key is not certified with a trusted signature! | ||||||
| @ -76,7 +76,7 @@ and on his [personal homepage](https://razor.occams.info/). (Of course, if this | |||||||
| 
 | 
 | ||||||
| Checkout the tag corresponding to the most recent release: | Checkout the tag corresponding to the most recent release: | ||||||
| 
 | 
 | ||||||
| 	$ git checkout v0.45 | 	$ git checkout v0.46 | ||||||
| 
 | 
 | ||||||
| Begin the installation. | Begin the installation. | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,10 +16,10 @@ if [ `date "+%u"` -eq 1 ]; then | |||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| # Take a backup. | # Take a backup. | ||||||
| management/backup.py | management/email_administrator.py "Backup Status" | management/backup.py 2>&1 | management/email_administrator.py "Backup Status" | ||||||
| 
 | 
 | ||||||
| # Provision any new certificates for new domains or domains with expiring certificates. | # Provision any new certificates for new domains or domains with expiring certificates. | ||||||
| management/ssl_certificates.py -q | management/email_administrator.py "TLS Certificate Provisioning Result" | management/ssl_certificates.py -q  2>&1 | management/email_administrator.py "TLS Certificate Provisioning Result" | ||||||
| 
 | 
 | ||||||
| # Run status checks and email the administrator if anything changed. | # Run status checks and email the administrator if anything changed. | ||||||
| management/status_checks.py --show-changes | management/email_administrator.py "Status Checks Change Notice" | management/status_checks.py --show-changes  2>&1 | management/email_administrator.py "Status Checks Change Notice" | ||||||
|  | |||||||
| @ -281,18 +281,20 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en | |||||||
| 		if not has_rec(dmarc_qname, "TXT", prefix="v=DMARC1; "): | 		if not has_rec(dmarc_qname, "TXT", prefix="v=DMARC1; "): | ||||||
| 			records.append((dmarc_qname, "TXT", 'v=DMARC1; p=reject', "Recommended. Prevents use of this domain name for outbound mail by specifying that the SPF rule should be honoured for mail from @%s." % (qname + "." + domain))) | 			records.append((dmarc_qname, "TXT", 'v=DMARC1; p=reject', "Recommended. Prevents use of this domain name for outbound mail by specifying that the SPF rule should be honoured for mail from @%s." % (qname + "." + domain))) | ||||||
| 
 | 
 | ||||||
| 	# Add CardDAV/CalDAV SRV records on the non-primary hostname that points to the primary hostname. | 	# Add CardDAV/CalDAV SRV records on the non-primary hostname that points to the primary hostname | ||||||
|  | 	# for autoconfiguration of mail clients (so only domains hosting user accounts need it). | ||||||
| 	# The SRV record format is priority (0, whatever), weight (0, whatever), port, service provider hostname (w/ trailing dot). | 	# The SRV record format is priority (0, whatever), weight (0, whatever), port, service provider hostname (w/ trailing dot). | ||||||
| 	if domain != env["PRIMARY_HOSTNAME"]: | 	if domain != env["PRIMARY_HOSTNAME"] and domain in get_mail_domains(env, users_only=True): | ||||||
| 		for dav in ("card", "cal"): | 		for dav in ("card", "cal"): | ||||||
| 			qname = "_" + dav + "davs._tcp" | 			qname = "_" + dav + "davs._tcp" | ||||||
| 			if not has_rec(qname, "SRV"): | 			if not has_rec(qname, "SRV"): | ||||||
| 				records.append((qname, "SRV", "0 0 443 " + env["PRIMARY_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain.")) | 				records.append((qname, "SRV", "0 0 443 " + env["PRIMARY_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain.")) | ||||||
| 
 | 
 | ||||||
| 	# Adds autoconfiguration A records for all domains. | 	# Adds autoconfiguration A records for all domains that there are user accounts at. | ||||||
| 	# This allows the following clients to automatically configure email addresses in the respective applications. | 	# This allows the following clients to automatically configure email addresses in the respective applications. | ||||||
| 	# autodiscover.* - Z-Push ActiveSync Autodiscover | 	# autodiscover.* - Z-Push ActiveSync Autodiscover | ||||||
| 	# autoconfig.* - Thunderbird Autoconfig | 	# autoconfig.* - Thunderbird Autoconfig | ||||||
|  | 	if domain in get_mail_domains(env, users_only=True): | ||||||
| 		autodiscover_records = [ | 		autodiscover_records = [ | ||||||
| 			("autodiscover", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), | 			("autodiscover", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), | ||||||
| 			("autodiscover", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), | 			("autodiscover", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."), | ||||||
|  | |||||||
| @ -258,13 +258,15 @@ def get_domain(emailaddr, as_unicode=True): | |||||||
| 			pass | 			pass | ||||||
| 	return ret | 	return ret | ||||||
| 
 | 
 | ||||||
| def get_mail_domains(env, filter_aliases=lambda alias : True): | def get_mail_domains(env, filter_aliases=lambda alias : True, users_only=False): | ||||||
| 	# Returns the domain names (IDNA-encoded) of all of the email addresses | 	# Returns the domain names (IDNA-encoded) of all of the email addresses | ||||||
| 	# configured on the system. | 	# configured on the system. If users_only is True, only return domains | ||||||
| 	return set( | 	# with email addresses that correspond to user accounts. | ||||||
| 		   [get_domain(login, as_unicode=False) for login in get_mail_users(env)] | 	domains = [] | ||||||
| 		 + [get_domain(address, as_unicode=False) for address, *_ in get_mail_aliases(env) if filter_aliases(address) ] | 	domains.extend([get_domain(login, as_unicode=False) for login in get_mail_users(env)]) | ||||||
| 		 ) | 	if not users_only: | ||||||
|  | 		domains.extend([get_domain(address, as_unicode=False) for address, *_ in get_mail_aliases(env) if filter_aliases(address) ]) | ||||||
|  | 	return set(domains) | ||||||
| 
 | 
 | ||||||
| def add_mail_user(email, pw, privs, env): | def add_mail_user(email, pw, privs, env): | ||||||
| 	# validate email | 	# validate email | ||||||
|  | |||||||
| @ -24,13 +24,13 @@ def get_web_domains(env, include_www_redirects=True, exclude_dns_elsewhere=True) | |||||||
| 		# the topmost of each domain we serve. | 		# the topmost of each domain we serve. | ||||||
| 		domains |= set('www.' + zone for zone, zonefile in get_dns_zones(env)) | 		domains |= set('www.' + zone for zone, zonefile in get_dns_zones(env)) | ||||||
| 
 | 
 | ||||||
| 	# Add Autoconfiguration domains, allowing us to serve correct SSL certs. | 	# Add Autoconfiguration domains for domains that there are user accounts at: | ||||||
| 	# 'autoconfig.' for Mozilla Thunderbird auto setup. | 	# 'autoconfig.' for Mozilla Thunderbird auto setup. | ||||||
| 	# 'autodiscover.' for Activesync autodiscovery. | 	# 'autodiscover.' for Activesync autodiscovery. | ||||||
| 	domains |= set('autoconfig.' + maildomain for maildomain in get_mail_domains(env)) | 	domains |= set('autoconfig.' + maildomain for maildomain in get_mail_domains(env, users_only=True)) | ||||||
| 	domains |= set('autodiscover.' + maildomain for maildomain in get_mail_domains(env)) | 	domains |= set('autodiscover.' + maildomain for maildomain in get_mail_domains(env, users_only=True)) | ||||||
| 
 | 
 | ||||||
| 	# 'mta-sts.' for MTA-STS support. | 	# 'mta-sts.' for MTA-STS support for all domains that have email addresses. | ||||||
| 	domains |= set('mta-sts.' + maildomain for maildomain in get_mail_domains(env)) | 	domains |= set('mta-sts.' + maildomain for maildomain in get_mail_domains(env)) | ||||||
| 
 | 
 | ||||||
| 	if exclude_dns_elsewhere: | 	if exclude_dns_elsewhere: | ||||||
| @ -158,9 +158,23 @@ def make_domain_config(domain, templates, ssl_certificates, env): | |||||||
| 
 | 
 | ||||||
| 			# any proxy or redirect here? | 			# any proxy or redirect here? | ||||||
| 			for path, url in yaml.get("proxies", {}).items(): | 			for path, url in yaml.get("proxies", {}).items(): | ||||||
|  | 				# Parse some flags in the fragment of the URL. | ||||||
|  | 				pass_http_host_header = False | ||||||
|  | 				m = re.search("#(.*)$", url) | ||||||
|  | 				if m: | ||||||
|  | 					for flag in m.group(1).split(","): | ||||||
|  | 						if flag == "pass-http-host": | ||||||
|  | 							pass_http_host_header = True | ||||||
|  | 					url = re.sub("#(.*)$", "", url) | ||||||
|  | 
 | ||||||
| 				nginx_conf_extra += "\tlocation %s {" % path | 				nginx_conf_extra += "\tlocation %s {" % path | ||||||
| 				nginx_conf_extra += "\n\t\tproxy_pass %s;" % url | 				nginx_conf_extra += "\n\t\tproxy_pass %s;" % url | ||||||
|  | 				if pass_http_host_header: | ||||||
|  | 					nginx_conf_extra += "\n\t\tproxy_set_header Host $http_host;" | ||||||
| 				nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;" | 				nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;" | ||||||
|  | 				nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Host $http_host;" | ||||||
|  | 				nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Proto $scheme;" | ||||||
|  | 				nginx_conf_extra += "\n\t\tproxy_set_header X-Real-IP $remote_addr;" | ||||||
| 				nginx_conf_extra += "\n\t}\n" | 				nginx_conf_extra += "\n\t}\n" | ||||||
| 			for path, alias in yaml.get("aliases", {}).items(): | 			for path, alias in yaml.get("aliases", {}).items(): | ||||||
| 				nginx_conf_extra += "\tlocation %s {" % path | 				nginx_conf_extra += "\tlocation %s {" % path | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ if [ -z "$TAG" ]; then | |||||||
| 	# want to display in status checks. | 	# want to display in status checks. | ||||||
| 	if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then | 	if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then | ||||||
| 		# This machine is running Ubuntu 18.04. | 		# This machine is running Ubuntu 18.04. | ||||||
| 		TAG=v0.45 | 		TAG=v0.46 | ||||||
| 
 | 
 | ||||||
| 	elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then | 	elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then | ||||||
| 		# This machine is running Ubuntu 14.04. | 		# This machine is running Ubuntu 14.04. | ||||||
|  | |||||||
| @ -136,7 +136,14 @@ function get_default_privateip { | |||||||
| function ufw_allow { | function ufw_allow { | ||||||
| 	if [ -z "${DISABLE_FIREWALL:-}" ]; then | 	if [ -z "${DISABLE_FIREWALL:-}" ]; then | ||||||
| 		# ufw has completely unhelpful output | 		# ufw has completely unhelpful output | ||||||
| 		ufw allow $1 > /dev/null; | 		ufw allow "$1" > /dev/null; | ||||||
|  | 	fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function ufw_limit { | ||||||
|  | 	if [ -z "${DISABLE_FIREWALL:-}" ]; then | ||||||
|  | 		# ufw has completely unhelpful output | ||||||
|  | 		ufw limit "$1" > /dev/null; | ||||||
| 	fi | 	fi | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -256,7 +256,7 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then | |||||||
| 	apt_install ufw | 	apt_install ufw | ||||||
| 
 | 
 | ||||||
| 	# Allow incoming connections to SSH. | 	# Allow incoming connections to SSH. | ||||||
| 	ufw_allow ssh; | 	ufw_limit ssh; | ||||||
| 
 | 
 | ||||||
| 	# ssh might be running on an alternate port. Use sshd -T to dump sshd's #NODOC | 	# ssh might be running on an alternate port. Use sshd -T to dump sshd's #NODOC | ||||||
| 	# settings, find the port it is supposedly running on, and open that port #NODOC | 	# settings, find the port it is supposedly running on, and open that port #NODOC | ||||||
| @ -266,7 +266,7 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then | |||||||
| 	if [ "$SSH_PORT" != "22" ]; then | 	if [ "$SSH_PORT" != "22" ]; then | ||||||
| 
 | 
 | ||||||
| 	echo Opening alternate SSH port $SSH_PORT. #NODOC | 	echo Opening alternate SSH port $SSH_PORT. #NODOC | ||||||
| 	ufw_allow $SSH_PORT #NODOC | 	ufw_limit $SSH_PORT #NODOC | ||||||
| 
 | 
 | ||||||
| 	fi | 	fi | ||||||
| 	fi | 	fi | ||||||
|  | |||||||
| @ -28,8 +28,8 @@ apt_install \ | |||||||
| # Install Roundcube from source if it is not already present or if it is out of date. | # Install Roundcube from source if it is not already present or if it is out of date. | ||||||
| # Combine the Roundcube version number with the commit hash of plugins to track | # Combine the Roundcube version number with the commit hash of plugins to track | ||||||
| # whether we have the latest version of everything. | # whether we have the latest version of everything. | ||||||
| VERSION=1.4.4 | VERSION=1.4.6 | ||||||
| HASH=4e425263f5bec27d39c07bde524f421bda205c07 | HASH=44961ef62bb9c9875141ca34704bbc7d6f36373d | ||||||
| PERSISTENT_LOGIN_VERSION=6b3fc450cae23ccb2f393d0ef67aa319e877e435 | PERSISTENT_LOGIN_VERSION=6b3fc450cae23ccb2f393d0ef67aa319e877e435 | ||||||
| HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5 | HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5 | ||||||
| CARDDAV_VERSION=3.0.3 | CARDDAV_VERSION=3.0.3 | ||||||
| @ -160,7 +160,7 @@ mkdir -p /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundc | |||||||
| chown -R www-data.www-data /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundcube | chown -R www-data.www-data /var/log/roundcubemail /var/tmp/roundcubemail $STORAGE_ROOT/mail/roundcube | ||||||
| 
 | 
 | ||||||
| # Ensure the log file monitored by fail2ban exists, or else fail2ban can't start. | # Ensure the log file monitored by fail2ban exists, or else fail2ban can't start. | ||||||
| sudo -u www-data touch /var/log/roundcubemail/errors | sudo -u www-data touch /var/log/roundcubemail/errors.log | ||||||
| 
 | 
 | ||||||
| # Password changing plugin settings | # Password changing plugin settings | ||||||
| # The config comes empty by default, so we need the settings | # The config comes empty by default, so we need the settings | ||||||
|  | |||||||
| @ -261,6 +261,10 @@ class UfwAllow(Grammar): | |||||||
| 	grammar = (ZERO_OR_MORE(SPACE), L("ufw_allow "), REST_OF_LINE, EOL) | 	grammar = (ZERO_OR_MORE(SPACE), L("ufw_allow "), REST_OF_LINE, EOL) | ||||||
| 	def value(self): | 	def value(self): | ||||||
| 		return shell_line("ufw allow " + self[2].string) | 		return shell_line("ufw allow " + self[2].string) | ||||||
|  | class UfwLimit(Grammar): | ||||||
|  | 	grammar = (ZERO_OR_MORE(SPACE), L("ufw_limit "), REST_OF_LINE, EOL) | ||||||
|  | 	def value(self): | ||||||
|  | 		return shell_line("ufw limit " + self[2].string) | ||||||
| class RestartService(Grammar): | class RestartService(Grammar): | ||||||
| 	grammar = (ZERO_OR_MORE(SPACE), L("restart_service "), REST_OF_LINE, EOL) | 	grammar = (ZERO_OR_MORE(SPACE), L("restart_service "), REST_OF_LINE, EOL) | ||||||
| 	def value(self): | 	def value(self): | ||||||
| @ -275,7 +279,7 @@ class OtherLine(Grammar): | |||||||
| 		return "<pre class='shell'><div>" + recode_bash(self.string.strip()) + "</div></pre>\n" | 		return "<pre class='shell'><div>" + recode_bash(self.string.strip()) + "</div></pre>\n" | ||||||
| 
 | 
 | ||||||
| class BashElement(Grammar): | class BashElement(Grammar): | ||||||
| 	grammar = Comment | CatEOF | EchoPipe | EchoLine | HideOutput | EditConf | SedReplace | AptGet | UfwAllow | RestartService | OtherLine | 	grammar = Comment | CatEOF | EchoPipe | EchoLine | HideOutput | EditConf | SedReplace | AptGet | UfwAllow | UfwLimit | RestartService | OtherLine | ||||||
| 	def value(self): | 	def value(self): | ||||||
| 		return self[0].value() | 		return self[0].value() | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user