Update to 2021-12-01 19:13

This commit is contained in:
Daniel Berteaud
2021-12-01 19:13:34 +01:00
commit 4c4556c660
2153 changed files with 60999 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
<%@ page import="java.security.InvalidKeyException" %>
<%@ page import="java.security.NoSuchAlgorithmException" %>
<%@ page import="java.security.SecureRandom" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="java.util.TreeSet" %>
<%@ page import="javax.crypto.Mac" %>
<%@ page import="javax.crypto.SecretKey" %>
<%!
public static final String DOMAIN_KEY =
"{{ item.stdout }}";
public static String generateRedirect(HttpServletRequest request, String name) {
HashMap params = new HashMap();
String ts = System.currentTimeMillis()+"";
params.put("account", name);
params.put("by", "name"); // needs to be part of hmac
params.put("timestamp", ts);
params.put("expires", "0"); // means use the default
String preAuth = computePreAuth(params, DOMAIN_KEY);
return request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+"/service/preauth/?" +
"account="+name+
"&by=name"+
"&timestamp="+ts+
"&expires=0"+
"&preauth="+preAuth;
}
public static String computePreAuth(Map params, String key) {
TreeSet names = new TreeSet(params.keySet());
StringBuffer sb = new StringBuffer();
for (Iterator it=names.iterator(); it.hasNext();) {
if (sb.length() > 0) sb.append('|');
sb.append(params.get(it.next()));
}
return getHmac(sb.toString(), key.getBytes());
}
private static String getHmac(String data, byte[] key) {
try {
ByteKey bk = new ByteKey(key);
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(bk);
return toHex(mac.doFinal(data.getBytes()));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("fatal error", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("fatal error", e);
}
}
static class ByteKey implements SecretKey {
private byte[] mKey;
ByteKey(byte[] key) {
mKey = (byte[]) key.clone();;
}
public byte[] getEncoded() {
return mKey;
}
public String getAlgorithm() {
return "HmacSHA1";
}
public String getFormat() {
return "RAW";
}
}
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder(data.length * 2);
for (int i=0; i<data.length; i++ ) {
sb.append(hex[(data[i] & 0xf0) >>> 4]);
sb.append(hex[data[i] & 0x0f] );
}
return sb.toString();
}
private static final char[] hex =
{'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' ,
'8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f'};
%><%
String casUser = request.getRemoteUser().toString().trim();
String redirect = generateRedirect(request, casUser+"@{{ item.item }}");
response.sendRedirect(redirect);
%>
<html>
<head>
<title>Pre-auth redirect</title>
</head>
<body>You should never see this page!</body>
</html>

View File

@@ -0,0 +1,100 @@
<%@ page import="java.security.InvalidKeyException" %>
<%@ page import="java.security.NoSuchAlgorithmException" %>
<%@ page import="java.security.SecureRandom" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Iterator" %>
<%@ page import="java.util.TreeSet" %>
<%@ page import="javax.crypto.Mac" %>
<%@ page import="javax.crypto.SecretKey" %>
<%!
public static final String DOMAIN_KEY =
"{{ item.stdout }}";
public static String generateRedirect(HttpServletRequest request, String name) {
HashMap params = new HashMap();
String ts = System.currentTimeMillis()+"";
params.put("account", name);
params.put("by", "name"); // needs to be part of hmac
params.put("timestamp", ts);
params.put("expires", "0"); // means use the default
params.put("admin", "1");
String preAuth = computePreAuth(params, DOMAIN_KEY);
return request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+"/service/preauth/?" +
"account="+name+
"&by=name"+
"&timestamp="+ts+
"&expires=0"+
"&admin=1"+
"&preauth="+preAuth;
}
public static String computePreAuth(Map params, String key) {
TreeSet names = new TreeSet(params.keySet());
StringBuffer sb = new StringBuffer();
for (Iterator it=names.iterator(); it.hasNext();) {
if (sb.length() > 0) sb.append('|');
sb.append(params.get(it.next()));
}
return getHmac(sb.toString(), key.getBytes());
}
private static String getHmac(String data, byte[] key) {
try {
ByteKey bk = new ByteKey(key);
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(bk);
return toHex(mac.doFinal(data.getBytes()));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("fatal error", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("fatal error", e);
}
}
static class ByteKey implements SecretKey {
private byte[] mKey;
ByteKey(byte[] key) {
mKey = (byte[]) key.clone();;
}
public byte[] getEncoded() {
return mKey;
}
public String getAlgorithm() {
return "HmacSHA1";
}
public String getFormat() {
return "RAW";
}
}
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder(data.length * 2);
for (int i=0; i<data.length; i++ ) {
sb.append(hex[(data[i] & 0xf0) >>> 4]);
sb.append(hex[data[i] & 0x0f] );
}
return sb.toString();
}
private static final char[] hex =
{'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' ,
'8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f'};
%><%
String casUser = request.getRemoteUser().toString().trim();
String redirect = generateRedirect(request, casUser+"@{{ item.item }}");
response.sendRedirect(redirect);
%>
<html>
<head>
<title>Pre-auth redirect</title>
</head>
<body>You should never see this page!</body>
</html>

View File

@@ -0,0 +1,60 @@
#!/bin/bash -e
{% if zcs_letsencrypt == True %}
if [ $1 == "{{ inventory_hostname }}" ]; then
cat /var/lib/dehydrated/certificates/certs/{{ inventory_hostname }}/privkey.pem > /opt/zimbra/ssl/zimbra/commercial/commercial.key
chown zimbra:zimbra /opt/zimbra/ssl/zimbra/commercial/commercial.key
chmod 600 /opt/zimbra/ssl/zimbra/commercial/commercial.key
cp /var/lib/dehydrated/certificates/certs/{{ inventory_hostname }}/cert.pem /tmp/zimbra_cert.pem
cp /var/lib/dehydrated/certificates/certs/{{ inventory_hostname }}/chain.pem /tmp/zimbra_chain.pem
# Zimbra needs the root cert to validate the whole chain
cat <<_EOF >> /tmp/zimbra_chain.pem
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
-----END CERTIFICATE-----
_EOF
chown zimbra:zimbra /opt/zimbra/ssl/zimbra/commercial/commercial.key /tmp/zimbra_{cert,chain}.pem
su - zimbra -c "/opt/zimbra/bin/zmcertmgr deploycrt comm /tmp/zimbra_cert.pem /tmp/zimbra_chain.pem"
rm -f /tmp/zimbra_{cert,chain}.pem
{% if 'mta' in zcs_enabled_components %}
su - zimbra -c "/opt/zimbra/bin/zmmtactl reload"
{% endif %}
{% if 'proxy' in zcs_enabled_components %}
# Don't put the root cert for nginx, as some monitoring tools might not like this
cat /var/lib/dehydrated/certificates/certs/{{ inventory_hostname }}/fullchain.pem > /opt/zimbra/conf/nginx.crt
su - zimbra -c "/opt/zimbra/bin/zmproxyctl reload"
{% endif %}
{% if 'mailbox' in zcs_enabled_components %}
su - zimbra -c "/opt/zimbra/bin/zmmailboxdctl reload"
{% endif %}
fi
{% endif %}

View File

@@ -0,0 +1,9 @@
- type: log
enabled: True
paths:
- /var/log/zimbra.log
- /opt/zimbra/log/*.log
exclude_files:
- '\.[gx]z$'
- '\d+$'

View File

@@ -0,0 +1,11 @@
#!/bin/sh
set -eo pipefail
echo 'Removing Zimbra database dumps'
rm -f /home/lbkp/zimbra/{ldap*,mysql*}
rm -rf /home/lbkp/zimbra/exports/
# Remove snapshot mount point
echo 'Cleanup backup mount point and snapshot'
/opt/zimbra/bin/zmbh --post --shutdown=ldap --verbose --mount=/home/lbkp/zimbra/mount

View File

@@ -0,0 +1,68 @@
#!/bin/sh
set -eo pipefail
mkdir -p /home/lbkp/zimbra/mount
source /opt/zimbra/bin/zmshutil
zmsetvars
echo 'Starting Zimbra backup'
{% if 'ldap' in zcs_enabled_components %}
echo 'Dumping LDAP database'
# Dump ldap data
/opt/zimbra/common/sbin/slapcat \
-F /opt/zimbra/data/ldap/config \
-n 0 | zstd -c > /home/lbkp/zimbra/ldap-config.ldif.zst
/opt/zimbra/common/sbin/slapcat \
-F /opt/zimbra/data/ldap/config \
-b "" | zstd -c > /home/lbkp/zimbra/ldap.ldif.zst
{% endif %}
{% if 'mailbox' in zcs_enabled_components %}
# Dump MySQL data
echo 'Dumping MySQL database'
/opt/zimbra/common/bin/mysqldump \
--user=root \
--password=$mysql_root_password \
--socket=$mysql_socket \
--all-databases \
--single-transaction \
--flush-logs | zstd -c > /home/lbkp/zimbra/mysql.sql.zst
# Export calendars, tasks and address books to ics and vcf files
OLDIFS=$IFS
IFS=$'\n'
for MAILBOX in $(/usr/local/bin/zmprov getQuotaUsage $(hostname --fqdn) | awk '{ print $1}'); do
echo ''
echo "Cheking contact and calendar folders for $MAILBOX"
for LINE in $(/usr/local/bin/zmmailbox -z -m $MAILBOX -t 0 getAllFolders); do
# Skip folders whose ID indicates it's a shared folder
if echo $LINE | grep -qiP '^\s*[a-z0-9]{8}\-[a-z0-9]'; then
continue
fi
DIR=$(echo $LINE | perl -ne 'm/\s*\d+\s+(\w{4})\s+\d+\s+\d+\s+(\/.*)/ && print "$2\n"')
if echo $DIR | grep -qiP '\([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}:\d+\)$'; then
continue
fi
TYPE=$(echo $LINE | perl -ne 'm/\s*\d+\s+(\w{4})\s+\d+\s+\d+\s+(\/.*)/ && print "$1\n"')
if [ "$TYPE" == "appo" -o "$TYPE" == "task" -o "$TYPE" == "cont" ]; then
echo "Exporting folder $DIR (account $MAILBOX, type $TYPE)"
mkdir -p /home/lbkp/zimbra/exports/$MAILBOX/$(dirname $DIR)
fi
if [ "$TYPE" == "appo" -o "$TYPE" == "task" ]; then
/usr/local/bin/zmmailbox -z -m $MAILBOX -t 0 getRestUrl "$(printf '%q' $DIR)?fmt=ics" > "/home/lbkp/zimbra/exports/$MAILBOX/$DIR"".ics"
elif [ "$TYPE" == "cont" ]; then
/usr/local/bin/zmmailbox -z -m $MAILBOX -t 0 getRestUrl "$(printf '%q' $DIR)?fmt=vcf" > "/home/lbkp/zimbra/exports/$MAILBOX/$DIR"".vcf"
fi
done
done
IFS=$OLDIFS
{% endif %}
# Try to snapshot Zimbra tree
echo ''
echo "Handle /opt/zimbra snapshot / bind mount on /home/lbkp/zimbra/mount"
/opt/zimbra/bin/zmbh --pre --snap-size=10G --verbose --mount=/home/lbkp/zimbra/mount --shutdown=ldap

View File

@@ -0,0 +1,23 @@
$ModLoad imuxsock
$ModLoad imjournal
$OmitLocalLogging on
$IMJournalStateFile imjournal.state
$WorkDirectory /var/lib/rsyslog
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
{% if 'logger' in zcs_enabled_components %}
$ModLoad imudp
$UDPServerRun 514
local0.* -/var/log/zimbra.log
local1.* -/var/log/zimbra-stats.log
auth.* -/var/log/zimbra.log
mail.* -/var/log/zimbra.log
{% else %}
local0.* @{{ zcs_log_hostname }}
local1.* @{{ zcs_log_hostname }}
auth.* @{{ zcs_log_hostname }}
local0.* -/var/log/zimbra.log
local1.* -/var/log/zimbra-stats.log
auth.* -/var/log/zimbra.log
mail.* @{{ zcs_log_hostname }}
mail.* -/var/log/zimbra.log
{% endif %}

View File

@@ -0,0 +1,86 @@
AVDOMAIN="{{ zcs_main_domain }}"
AVUSER="admin@{{ zcs_main_domain }}"
CREATEADMIN="admin@{{ zcs_main_domain }}"
CREATEADMINPASS="zimbraadmin"
CREATEDOMAIN="{{ zcs_main_domain }}"
DOCREATEADMIN="yes"
DOCREATEDOMAIN="yes"
DOTRAINSA="yes"
EXPANDMENU="no"
HOSTNAME="{{ inventory_hostname }}"
HTTPPORT="8080"
HTTPPROXYPORT="80"
HTTPSPORT="8443"
HTTPSPROXYPORT="443"
IMAPPORT="7143"
IMAPPROXYPORT="143"
IMAPSSLPORT="7993"
IMAPSSLPROXYPORT="993"
INSTALL_WEBAPPS="service zimlet zimbra zimbraAdmin"
JAVAHOME="/opt/zimbra/common/lib/jvm/java"
LDAPBESSEARCHSET="set"
LDAPHOST="{{ zcs_primary_ldap }}"
LDAPPORT="389"
LDAPREPLICATIONTYPE="{{ zcs_i_am_primary_ldap | ternary('master','salve') }}"
LDAPSERVERID="2"
MAILBOXDMEMORY="{{ (ansible_memtotal_mb * 0.25) | int }}"
MAILPROXY="TRUE"
MODE="redirect"
MYSQLMEMORYPERCENT="30"
POPPORT="7110"
POPPROXYPORT="110"
POPSSLPORT="7995"
POPSSLPROXYPORT="995"
PROXYMODE="redirect"
REMOVE="no"
RUNARCHIVING="no"
RUNAV="{{ (zcs_run_av == True) | ternary('yes','no') }}"
RUNCBPOLICYD="{{ (zcs_run_cbpolicyd == True) | ternary('yes','no') }}"
RUNDKIM="{{ (zcs_run_dkim == True) | ternary('yes','no') }}"
RUNSA="{{ (zcs_run_sa == True) | ternary('yes','no') }}"
RUNVMHA="no"
SERVICEWEBAPP="yes"
SMTPDEST="admin@{{ zcs_main_domain }}"
SMTPHOST="{{ inventory_hostname }}"
SMTPNOTIFY="yes"
SMTPSOURCE="admin@{{ zcs_main_domain }}"
SNMPNOTIFY="{{ ('snmp' in zcs_components) | ternary('yes','no') }}"
SNMPTRAPHOST="{{ inventory_hostname }}"
SPELLURL="http://{{ inventory_hostname }}:7780/aspell.php"
STARTSERVERS="yes"
STRICTSERVERNAMEENABLED="TRUE"
TRAINSAHAM="ham.{{ ('ham' | password_hash('sha256', 65534 | random(seed=zcs_main_domain) | string))[9:17] }}@{{ zcs_main_domain }}"
TRAINSASPAM="spam.{{ ('spam' | password_hash('sha256', 65534 | random(seed=zcs_main_domain) | string))[9:17] }}@{{ zcs_main_domain }}"
UIWEBAPPS="yes"
UPGRADE="yes"
USEEPHEMERALSTORE="no"
USESPELL="{{ ('spell' in zcs_components) | ternary('yes','no') }}"
VERSIONUPDATECHECKS="TRUE"
VIRUSQUARANTINE="virus-quarantine.{{ ('virus' | password_hash('sha256', 65534 | random(seed=zcs_main_domain) | string))[9:17] }}@{{ zcs_main_domain }}"
ZIMBRA_REQ_SECURITY="yes"
ldap_bes_searcher_password="{{ ('bes_searcher' | password_hash('sha256', 65534 | random(seed=zcs_main_domain) | string))[9:22] }}"
ldap_dit_base_dn_config="cn=zimbra"
ldap_nginx_password="{{ ('nginx' | password_hash('sha256', 65534 | random(seed=zcs_main_domain) | string))[9:22] }}"
mailboxd_directory="/opt/zimbra/mailboxd"
mailboxd_keystore="/opt/zimbra/mailboxd/etc/keystore"
mailboxd_keystore_password="{{ ('keystore' | password_hash('sha256', 65534 | random(seed=zcs_main_domain) | string))[9:22] }}"
mailboxd_server="jetty"
mailboxd_truststore="/opt/zimbra/common/lib/jvm/java/lib/security/cacerts"
mailboxd_truststore_password="changeit"
postfix_mail_owner="postfix"
postfix_setgid_group="postdrop"
ssl_default_digest="sha256"
zimbraFeatureBriefcasesEnabled="Enabled"
zimbraFeatureTasksEnabled="Enabled"
zimbraIPMode="ipv4"
zimbraMailProxy="{{ ('proxy' in zcs_components) | ternary('TRUE','FALSE') }}"
zimbraMtaMyNetworks="127.0.0.0/8"
zimbraPrefTimeZoneId="{{ system_tz | default('Europe/Paris') }}"
zimbraReverseProxyLookupTarget="TRUE"
zimbraVersionCheckNotificationEmail="admin@{{ zcs_main_domain }}"
zimbraVersionCheckNotificationEmailFrom="admin@{{ zcs_main_domain }}"
zimbraVersionCheckSendNotifications="TRUE"
zimbraWebProxy="{{ ('proxy' in zcs_components) | ternary('TRUE','FALSE') }}"
zimbra_ldap_userdn="uid=zimbra,cn=admins,cn=zimbra"
zimbra_require_interprocess_security="1"
INSTALL_PACKAGES="zimbra-core {{ zcs_components | map('regex_replace', '^(.*)$', 'zimbra-\\1') | join(' ') }} "

View File

@@ -0,0 +1,6 @@
{# Accept License #}y
{# Use Online repo #}y
{% for component in [ 'ldap', 'logger', 'mta', 'dnscache', 'snmp', 'store', 'apache', 'spell', 'memcached', 'proxy', 'drive', 'imapd', 'chat' ] %}
{{ (component in zcs_components) | ternary('y','n') }}
{% endfor %}
{# Confirm continue #}y

View File

@@ -0,0 +1,3 @@
#!/bin/bash -e
su - zimbra -c "LC_ALL=C.utf8 /opt/zimbra/bin/{{ item }} $*"

View File

@@ -0,0 +1,7 @@
[Unit]
Description=Sync LDAP accounts into Zimbra
[Service]
Type=oneshot
ExecStart=/opt/zimbra/bin/zmldapsync
TimeoutSec=300

View File

@@ -0,0 +1,8 @@
[Unit]
Description=Sync LDAP Users with Zimbra
[Timer]
OnCalendar=*:0/5
[Install]
WantedBy=timers.target

View File

@@ -0,0 +1,14 @@
---
general:
notify:
from: zimbra@{{ ansible_domain }}
to: {{ system_admin_email }}
domains:
{% for domain in zcs_domains.keys() %}
{% if zcs_domains[domain].ldapsync is defined %}
{{ domain }}:
{{ zcs_domains[domain].ldapsync | to_nice_yaml(indent=2) | indent(width=4, first=True) }}
{% endif %}
{% endfor %}