diff options
author | Cullum Smith <cullum@sacredheartsc.com> | 2024-10-04 21:59:59 -0400 |
---|---|---|
committer | Cullum Smith <cullum@sacredheartsc.com> | 2024-10-04 21:59:59 -0400 |
commit | 1e088983f6a80b6fd47543d0b4989e9ddb3234d5 (patch) | |
tree | 16af4205d84c9194257887e5e54653f79e96f987 | |
parent | a1bddcb1de1053994fb445267ca5d1ffaecb0fb5 (diff) | |
download | infrastructure-1e088983f6a80b6fd47543d0b4989e9ddb3234d5.tar.gz |
add imap stuff
48 files changed, 1297 insertions, 17 deletions
diff --git a/files/etc/cron.d/acme.common b/files/etc/cron.d/acme.common new file mode 100644 index 0000000..05bf064 --- /dev/null +++ b/files/etc/cron.d/acme.common @@ -0,0 +1,2 @@ +MAILTO=root +00 15 * * * ${acme_user} lockf -t 0 /tmp/acme-cron.lock acme.sh --cron --home ${acme_home} --syslog 6 > /dev/null diff --git a/files/etc/pf.conf.freebsd b/files/etc/pf.conf.freebsd index e01f49d..881fcea 100644 --- a/files/etc/pf.conf.freebsd +++ b/files/etc/pf.conf.freebsd @@ -5,8 +5,12 @@ $(if [ -n "${pf_egress_interfaces:-}" ]; then fi) allowed_tcp_ports = "{ $(join ', ' ${allowed_tcp_ports:-}) }" allowed_udp_ports = "{ $(join ', ' ${allowed_udp_ports:-}) }" + +$([ "${acme_standalone:-}" = true ] && cat <<EOF acme_standalone_port = ${acme_standalone_port} -acme_standalone_user = ${acme_uid} +acme_standalone_user = $(id -u "$acme_user") +EOF +) nfscbd_port = ${nfscbd_port} set block-policy return diff --git a/files/usr/local/etc/dovecot/conf.d/10-auth.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/10-auth.conf.imap_server new file mode 100644 index 0000000..7a908e6 --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/10-auth.conf.imap_server @@ -0,0 +1,7 @@ +auth_default_realm = ${realm} +auth_gssapi_hostname = "\$ALL" +auth_username_format = %Ln +auth_mechanisms = gssapi plain login +auth_cache_size = 10M + +!include auth-ldap.conf.ext diff --git a/files/usr/local/etc/dovecot/conf.d/10-mail.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/10-mail.conf.imap_server new file mode 100644 index 0000000..0fa15ea --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/10-mail.conf.imap_server @@ -0,0 +1,23 @@ +mail_location = mdbox:~/mdbox + +namespace inbox { + type = private + separator = / + inbox = yes + subscriptions = yes +} + +mail_plugins = \$mail_plugins quota virtual fts fts_solr + +mail_privileged_group = ${dovecot_vmail_user} + +first_valid_uid = ${dovecot_vmail_uid} +last_valid_uid = ${dovecot_vmail_uid} + +first_valid_gid = ${dovecot_vmail_uid} +last_valid_gid = ${dovecot_vmail_uid} + +# recommended configuration for quota:count +protocol !indexer-worker { + mail_vsize_bg_after_count = 100 +} diff --git a/files/usr/local/etc/dovecot/conf.d/10-master.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/10-master.conf.imap_server new file mode 100644 index 0000000..1445cb6 --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/10-master.conf.imap_server @@ -0,0 +1,32 @@ +service imap-login { + inet_listener imap { + port = 0 + } + + inet_listener imaps { + port = 993 + ssl = yes + } +} + +service lmtp { + user = ${dovecot_vmail_user} + inet_listener lmtp { + port = ${lmtp_port} + ssl = yes + } +} + +service auth-worker { + user = \$default_internal_user +} + +# Allow the vmail user to write to stats. This isn't strictly necessary, but +# prevents dovecot-lda from spamming the mail log with errors. +service stats { + unix_listener stats-writer { + user = dovecot + group = ${dovecot_vmail_user} + mode = 0660 + } +} diff --git a/files/usr/local/etc/dovecot/conf.d/10-ssl.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/10-ssl.conf.imap_server new file mode 100644 index 0000000..9f90a47 --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/10-ssl.conf.imap_server @@ -0,0 +1,8 @@ +ssl = required + +ssl_cert = <${dovecot_tls_cert} +ssl_key = <${dovecot_tls_key} + +ssl_min_protocol = TLSv1.2 +ssl_cipher_list = ${dovecot_cipherlist} +ssl_prefer_server_ciphers = no diff --git a/files/usr/local/etc/dovecot/conf.d/15-lda.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/15-lda.conf.imap_server new file mode 100644 index 0000000..557594b --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/15-lda.conf.imap_server @@ -0,0 +1,8 @@ +recipient_delimiter = ${dovecot_recipient_delimiter} +lda_original_recipient_header = X-Original-To + +lda_mailbox_autocreate = yes + +protocol lda { + mail_plugins = \$mail_plugins sieve +} diff --git a/files/usr/local/etc/dovecot/conf.d/15-mailboxes.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/15-mailboxes.conf.imap_server new file mode 100644 index 0000000..540947c --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/15-mailboxes.conf.imap_server @@ -0,0 +1,31 @@ +namespace inbox { + + mailbox INBOX { + auto = subscribe + } + + mailbox Drafts { + auto = subscribe + special_use = \Drafts + } + + mailbox Junk { + auto = subscribe + special_use = \Junk + } + + mailbox Trash { + auto = subscribe + special_use = \Trash + } + + mailbox Sent { + auto = subscribe + special_use = \Sent + } + + mailbox Archive { + auto = subscribe + special_use = \Archive + } +} diff --git a/files/usr/local/etc/dovecot/conf.d/20-imap.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/20-imap.conf.imap_server new file mode 100644 index 0000000..ae67bae --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/20-imap.conf.imap_server @@ -0,0 +1,3 @@ +protocol imap { + mail_plugins = $mail_plugins imap_quota imap_sieve +} diff --git a/files/usr/local/etc/dovecot/conf.d/20-lmtp.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/20-lmtp.conf.imap_server new file mode 100644 index 0000000..2619ce5 --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/20-lmtp.conf.imap_server @@ -0,0 +1,3 @@ +protocol lmtp { + mail_plugins = $mail_plugins sieve +} diff --git a/files/usr/local/etc/dovecot/conf.d/20-managesieve.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/20-managesieve.conf.imap_server new file mode 100644 index 0000000..f4adea9 --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/20-managesieve.conf.imap_server @@ -0,0 +1,11 @@ +protocols = $protocols sieve + +service managesieve-login { + inet_listener sieve { + port = 4190 + } + + inet_listener sieve_deprecated { + port = 0 + } +} diff --git a/files/usr/local/etc/dovecot/conf.d/90-fts.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/90-fts.conf.imap_server new file mode 100644 index 0000000..fbe7e0f --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/90-fts.conf.imap_server @@ -0,0 +1,6 @@ +plugin { + fts_autoindex = yes + fts = solr + fts_solr = url=http://127.0.0.1:${solr_port}/solr/dovecot/ + fts_tika = http://127.0.0.1:${tika_port}/tika/ +} diff --git a/files/usr/local/etc/dovecot/conf.d/90-quota.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/90-quota.conf.imap_server new file mode 100644 index 0000000..7bffe9f --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/90-quota.conf.imap_server @@ -0,0 +1,32 @@ +plugin { + quota = count:User quota + quota_vsizes = yes + quota_rule = *:storage=${dovecot_default_quota} + quota_grace = ${dovecot_quota_grace_percent}%% + + quota_status_success = DUNNO + quota_status_nouser = DUNNO + quota_status_overquota = "552 5.2.2 Mailbox is full" + + quota_warning = storage=95%% quota-warning 95 %u ${dovecot_quota_mail_from} + quota_warning2 = storage=90%% quota-warning 90 %u ${dovecot_quota_mail_from} + quota_warning3 = storage=80%% quota-warning 80 %u ${dovecot_quota_mail_from} +} + +service quota-warning { + executable = script ${dovecot_script_dir}/quota-warning.sh + user = ${dovecot_vmail_user} + unix_listener quota-warning { + user = dovecot + group = ${dovecot_vmail_user} + mode = 0660 + } +} + +service quota-status { + executable = quota-status -p postfix + inet_listener { + port = ${quota_status_port} + } + client_limit = 5 +} diff --git a/files/usr/local/etc/dovecot/conf.d/90-sieve-extprograms.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/90-sieve-extprograms.conf.imap_server new file mode 100644 index 0000000..06250e9 --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/90-sieve-extprograms.conf.imap_server @@ -0,0 +1,3 @@ +plugin { + sieve_pipe_bin_dir = ${dovecot_sieve_pipe_bin_dir} +} diff --git a/files/usr/local/etc/dovecot/conf.d/90-sieve.conf.imap_server b/files/usr/local/etc/dovecot/conf.d/90-sieve.conf.imap_server new file mode 100644 index 0000000..cd67671 --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/90-sieve.conf.imap_server @@ -0,0 +1,28 @@ +plugin { + sieve = file:~/sieve;active=~/.dovecot.sieve + + sieve_before = ${dovecot_sieve_before_dir} + + sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute + + sieve_plugins = sieve_extprograms sieve_imapsieve + + sieve_quota_max_scripts = 10 + sieve_quota_max_storage = 2M + + # The default value for this is "sender", but that will totally break SPF + sieve_redirect_envelope_from = orig_recipient + + # From elsewhere to Junk folder + imapsieve_mailbox1_name = Junk + imapsieve_mailbox1_causes = COPY + imapsieve_mailbox1_before = file:${dovecot_conf_dir}/report-spam.sieve + + # From Junk folder to elsewhere + imapsieve_mailbox2_name = * + imapsieve_mailbox2_from = Junk + imapsieve_mailbox2_causes = COPY + imapsieve_mailbox2_before = file:${dovecot_conf_dir}/report-ham.sieve + + sieve_global_extensions = +vnd.dovecot.pipe +} diff --git a/files/usr/local/etc/dovecot/conf.d/auth-ldap.conf.ext.imap_server b/files/usr/local/etc/dovecot/conf.d/auth-ldap.conf.ext.imap_server new file mode 100644 index 0000000..9237f1f --- /dev/null +++ b/files/usr/local/etc/dovecot/conf.d/auth-ldap.conf.ext.imap_server @@ -0,0 +1,8 @@ +passdb { + driver = ldap + args = ${dovecot_conf_dir}/dovecot-ldap-passdb.conf.ext +} +userdb { + driver = ldap + args = ${dovecot_conf_dir}/dovecot-ldap-userdb.conf.ext +} diff --git a/files/usr/local/etc/dovecot/dovecot-ldap-passdb.conf.ext.imap_server b/files/usr/local/etc/dovecot/dovecot-ldap-passdb.conf.ext.imap_server new file mode 100644 index 0000000..5158954 --- /dev/null +++ b/files/usr/local/etc/dovecot/dovecot-ldap-passdb.conf.ext.imap_server @@ -0,0 +1,11 @@ +uris = ${ldap_uri} + +sasl_bind = yes +sasl_mech = gssapi +sasl_realm = ${realm} + +base = ${users_basedn} + +auth_bind = yes +pass_attrs = uid=user +user_filter = (uid=%u) diff --git a/files/usr/local/etc/dovecot/dovecot-ldap-userdb.conf.ext.imap_server b/files/usr/local/etc/dovecot/dovecot-ldap-userdb.conf.ext.imap_server new file mode 100644 index 0000000..fc939a6 --- /dev/null +++ b/files/usr/local/etc/dovecot/dovecot-ldap-userdb.conf.ext.imap_server @@ -0,0 +1,17 @@ +uris = ${ldap_uri} + +sasl_bind = yes +sasl_mech = gssapi +sasl_realm = ${realm} + +base = ${users_basedn} +user_filter = (|(mailAddress=%u)(uid=%u)) +user_attrs = \ + =user=%{ldap:uid}, \ + =uid=${dovecot_vmail_uid}, \ + =gid=${dovecot_vmail_uid}, \ + =home=${dovecot_vmail_dir}/%{ldap:uid} \ + mailQuota=quota_rule=\*:storage=%{ldap:mailQuota} + +iterate_attrs = uid=user +iterate_filter = (mailAddress=*) diff --git a/files/usr/local/etc/dovecot/dovecot.conf.imap_server b/files/usr/local/etc/dovecot/dovecot.conf.imap_server new file mode 100644 index 0000000..2045e76 --- /dev/null +++ b/files/usr/local/etc/dovecot/dovecot.conf.imap_server @@ -0,0 +1,5 @@ +protocols = imap lmtp + +import_environment = \$import_environment KRB5_KTNAME=${dovecot_keytab} KRB5_CLIENT_KTNAME=${dovecot_keytab} + +!include conf.d/*.conf diff --git a/files/usr/local/etc/dovecot/report-ham.sieve.imap_server b/files/usr/local/etc/dovecot/report-ham.sieve.imap_server new file mode 100644 index 0000000..578e7b2 --- /dev/null +++ b/files/usr/local/etc/dovecot/report-ham.sieve.imap_server @@ -0,0 +1,15 @@ +require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; + +if environment :matches "imap.mailbox" "*" { + set "mailbox" "${1}"; +} + +if string "${mailbox}" "Trash" { + stop; +} + +if environment :matches "imap.email" "*" { + set "email" "${1}"; +} + +pipe :copy "report-ham.sh" [ "${email}" ]; diff --git a/files/usr/local/etc/dovecot/report-spam.sieve.imap_server b/files/usr/local/etc/dovecot/report-spam.sieve.imap_server new file mode 100644 index 0000000..d34c71b --- /dev/null +++ b/files/usr/local/etc/dovecot/report-spam.sieve.imap_server @@ -0,0 +1,7 @@ +require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; + +if environment :matches "imap.email" "*" { + set "email" "${1}"; +} + +pipe :copy "report-spam.sh" [ "${email}" ]; diff --git a/files/usr/local/etc/dovecot/sieve-before.d/10-rspamd.sieve.imap_server b/files/usr/local/etc/dovecot/sieve-before.d/10-rspamd.sieve.imap_server new file mode 100644 index 0000000..7931a71 --- /dev/null +++ b/files/usr/local/etc/dovecot/sieve-before.d/10-rspamd.sieve.imap_server @@ -0,0 +1,5 @@ +require ["fileinto"]; + +if header :is "X-Spam" "Yes" { + fileinto "Junk"; +} diff --git a/files/usr/local/etc/nginx/nginx.conf.common b/files/usr/local/etc/nginx/nginx.conf.common index 9ab993c..1da7c3c 100644 --- a/files/usr/local/etc/nginx/nginx.conf.common +++ b/files/usr/local/etc/nginx/nginx.conf.common @@ -47,7 +47,7 @@ $([ "${nginx_gssapi:-}" = true ] && cat <<EOF EOF ) -$([ "${nginx_acme:-}" = true ] && cat <<EOF +$([ "${acme:-}" = true ] && [ "${acme_standalone:-}" != true ] && cat <<EOF server { listen 0.0.0.0:80 default_server; listen [::]:80 default_server; diff --git a/files/usr/local/etc/poudriere.d/make.conf.pkg_repository b/files/usr/local/etc/poudriere.d/make.conf.pkg_repository index 6dbbafc..fd35928 100644 --- a/files/usr/local/etc/poudriere.d/make.conf.pkg_repository +++ b/files/usr/local/etc/poudriere.d/make.conf.pkg_repository @@ -74,7 +74,6 @@ sysutils_htop_SET=LSOF sysutils_k3b_UNSET=EMOVIX VCDIMAGER sysutils_rsyslog8_SET=GSSAPI RELP OPENSSL sysutils_rsyslog8_UNSET=GCRYPT -www_apache${apache_version}_SET=AUTHNZ_LDAP LDAP SUEXEC SUEXEC_SYSLOG www_chromium_SET=WIDEVINE www_firefox_UNSET=PROFILE JACK www_nginx_SET=HTTPV3 HTTPV3_QTLS HTTP_AUTH_KRB5 HTTP_AUTH_LDAP diff --git a/files/usr/local/etc/poudriere.d/pkglist.pkg_repository b/files/usr/local/etc/poudriere.d/pkglist.pkg_repository index 5e95f8c..d24ce06 100644 --- a/files/usr/local/etc/poudriere.d/pkglist.pkg_repository +++ b/files/usr/local/etc/poudriere.d/pkglist.pkg_repository @@ -14,6 +14,7 @@ dns/unbound editors/vim@console editors/vim@tiny ftp/php${php_version}-curl +java/openjdk21 lang/python lang/php${php_version} mail/dovecot @@ -50,9 +51,6 @@ sysutils/stow sysutils/tmux sysutils/tree textproc/php${php_version}-xml -www/apache${apache_version} -www/mod_auth_gssapi -www/mod_php${php_version} www/nginx www/php${php_version}-opcache www/php${php_version}-session diff --git a/files/usr/local/etc/rc.d/solr.imap_server b/files/usr/local/etc/rc.d/solr.imap_server new file mode 100644 index 0000000..70cc324 --- /dev/null +++ b/files/usr/local/etc/rc.d/solr.imap_server @@ -0,0 +1,76 @@ +#!/bin/sh + +# PROVIDE: solr +# REQUIRE: LOGIN +# BEFORE: dovecot +# KEYWORD: shutdown + +. /etc/rc.subr + +name=solr +rcvar=solr_enable + +load_rc_config "$name" + +: ${solr_enable:='NO'} +: ${solr_host:='127.0.0.1'} +: ${solr_port:='8983'} +: ${solr_syslog_priority:='info'} +: ${solr_syslog_facility:='daemon'} +: ${solr_heap_size:='512m'} +: ${solr_softcommit_ms:='60000'} + +solr_java_home=/usr/local/openjdk21 +solr_syslog_tag=solr +solr_data_dir=/var/db/solr +solr_install_dir=/usr/local/solr +solr_conf_dir=/usr/local/etc/solr +solr_config_file="${solr_conf_dir}/solrconfig.xml" +solr_log4j_config_file="${solr_conf_dir}/log4j2.xml" +solr_jar="${solr_install_dir}/server/start.jar" + +solr_user=solr +solr_chdir="${solr_install_dir}/server" + +pidfile=/var/run/solr/solr.pid +command=/usr/sbin/daemon + +command_args="-f \ +-s ${solr_syslog_priority} \ +-l ${solr_syslog_facility} \ +-T ${solr_syslog_tag} \ +-p ${pidfile} \ +-t solr \ +${solr_java_home}/bin/java \ +-server \ +-Xmx${solr_heap_size} \ +-XX:+UseG1GC \ +-XX:+PerfDisableSharedMem \ +-XX:+ParallelRefProcEnabled \ +-XX:MaxGCPauseMillis=250 \ +-XX:+AlwaysPreTouch \ +-XX:+ExplicitGCInvokesConcurrent \ +-XX:+CrashOnOutOfMemoryError \ +-Djava.awt.headless=true \ +-Dlog4j.configurationFile=${solr_log4j_config_file} \ +-Djetty.host=${solr_host} \ +-Djetty.port=${solr_port} \ +-Djetty.home=${solr_install_dir}/server \ +-Dsolr.autoSoftCommit.maxTime=${solr_softcommit_ms} \ +-Dsolr.solr.home=${solr_data_dir} \ +-Dsolr.data.home= \ +-Dsolr.install.dir=${solr_install_dir} \ +-Dsolr.default.confdir=${solr_conf_dir} \ +-jar ${solr_jar} \ +--module=http \ +--module=gzip" + +required_files="${solr_config_file} ${solr_log4j_config_file} ${solr_jar}" +procname="${solr_java_home}/bin/java" +start_precmd=solr_prestart + +solr_prestart(){ + install -d -m 0755 -o ${solr_user} /var/run/solr +} + +run_rc_command "$1" diff --git a/files/usr/local/etc/rc.d/tika.imap_server b/files/usr/local/etc/rc.d/tika.imap_server new file mode 100644 index 0000000..95f1c88 --- /dev/null +++ b/files/usr/local/etc/rc.d/tika.imap_server @@ -0,0 +1,55 @@ +#!/bin/sh + +# PROVIDE: tika +# REQUIRE: LOGIN +# BEFORE: dovecot +# KEYWORD: shutdown + +. /etc/rc.subr + +name=tika +rcvar=tika_enable + +load_rc_config "$name" + +: ${tika_enable:='NO'} +: ${tika_host:='127.0.0.1'} +: ${tika_port:='9998'} +: ${tika_syslog_priority:='info'} +: ${tika_syslog_facility:='daemon'} + +tika_user=tika +tika_java_home=/usr/local/openjdk21 +tika_syslog_tag=tika +tika_jar=/usr/local/tika/tika.jar +tika_config_file=/usr/local/etc/tika/config.xml +tika_log4j_config_file=/usr/local/etc/tika/log4j2.xml + +pidfile=/var/run/tika/tika.pid +command=/usr/sbin/daemon + +command_args="-f \ +-s ${tika_syslog_priority} \ +-l ${tika_syslog_facility} \ +-T ${tika_syslog_tag} \ +-p ${pidfile} \ +-t tika \ +${tika_java_home}/bin/java \ +-server \ +-Xmx64m \ +-Djava.awt.headless=true \ +-Dlog4j.configurationFile=${tika_log4j_config_file} \ +-jar ${tika_jar} \ +-c ${tika_config_file} \ +-h ${tika_host} \ +-p ${tika_port}" + +required_files="${tika_config_file} ${tika_log4j_config_file} ${tika_jar}" +procname="${tika_java_home}/bin/java" +start_precmd=tika_prestart + +tika_prestart(){ + install -d -m 0755 -o ${tika_user} /var/run/tika +} + +run_rc_command "$1" diff --git a/files/usr/local/etc/solr/log4j2.xml.imap_server b/files/usr/local/etc/solr/log4j2.xml.imap_server new file mode 100644 index 0000000..cabde07 --- /dev/null +++ b/files/usr/local/etc/solr/log4j2.xml.imap_server @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="STDOUT" target="SYSTEM_OUT"> + <PatternLayout> + <Pattern> + %maxLen{%-5p %c %m%notEmpty{ =>%ex{short}}}{10240}%n + </Pattern> + </PatternLayout> + </Console> + </Appenders> + <Loggers> + <Root level="INFO"> + <AppenderRef ref="STDOUT"/> + </Root> + </Loggers> +</Configuration> diff --git a/files/usr/local/etc/solr/solrconfig.xml.imap_server b/files/usr/local/etc/solr/solrconfig.xml.imap_server new file mode 100644 index 0000000..f7a9d7b --- /dev/null +++ b/files/usr/local/etc/solr/solrconfig.xml.imap_server @@ -0,0 +1,280 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<config> + <luceneMatchVersion>9.3.0</luceneMatchVersion> + <!-- the rest of this file is unchanged from the defaults --> + <dataDir>${solr.data.dir:}</dataDir> + <directoryFactory name="DirectoryFactory" + class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/> + <codecFactory class="solr.SchemaCodecFactory"/> + <indexConfig> + <lockType>${solr.lock.type:native}</lockType> + </indexConfig> + <jmx /> + <updateHandler class="solr.DirectUpdateHandler2"> + <updateLog> + <str name="dir">${solr.ulog.dir:}</str> + <int name="numVersionBuckets">${solr.ulog.numVersionBuckets:65536}</int> + </updateLog> + <autoCommit> + <maxTime>${solr.autoCommit.maxTime:15000}</maxTime> + <openSearcher>false</openSearcher> + </autoCommit> + <autoSoftCommit> + <maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime> + </autoSoftCommit> + </updateHandler> + + <query> + <maxBooleanClauses>${solr.max.booleanClauses:1024}</maxBooleanClauses> + <filterCache size="512" + initialSize="512" + autowarmCount="0"/> + <queryResultCache size="512" + initialSize="512" + autowarmCount="0"/> + + <documentCache size="512" + initialSize="512" + autowarmCount="0"/> + + <cache name="perSegFilter" + size="10" + initialSize="0" + autowarmCount="10" + regenerator="solr.NoOpRegenerator" /> + + <enableLazyFieldLoading>true</enableLazyFieldLoading> + + <queryResultWindowSize>20</queryResultWindowSize> + + <queryResultMaxDocsCached>200</queryResultMaxDocsCached> + + <listener event="newSearcher" class="solr.QuerySenderListener"> + <arr name="queries"> + </arr> + </listener> + <listener event="firstSearcher" class="solr.QuerySenderListener"> + <arr name="queries"> + </arr> + </listener> + + <useColdSearcher>false</useColdSearcher> + </query> + + <circuitBreakers enabled="true"> + </circuitBreakers> + + + <requestDispatcher> + <httpCaching never304="true" /> + </requestDispatcher> + + <requestHandler name="/select" class="solr.SearchHandler"> + <lst name="defaults"> + <str name="echoParams">explicit</str> + <int name="rows">10</int> + <str name="df">hdr</str> + </lst> + </requestHandler> + + <requestHandler name="/query" class="solr.SearchHandler"> + <lst name="defaults"> + <str name="echoParams">explicit</str> + <str name="wt">json</str> + <str name="indent">true</str> + </lst> + </requestHandler> + + <initParams path="/update/**,/query,/select,/spell"> + <lst name="defaults"> + <str name="df">_text_</str> + </lst> + </initParams> + + <searchComponent name="spellcheck" class="solr.SpellCheckComponent"> + + <str name="queryAnalyzerFieldType">text_general</str> + + <lst name="spellchecker"> + <str name="name">default</str> + <str name="field">_text_</str> + <str name="classname">solr.DirectSolrSpellChecker</str> + <str name="distanceMeasure">internal</str> + <float name="accuracy">0.5</float> + <int name="maxEdits">2</int> + <int name="minPrefix">1</int> + <int name="maxInspections">5</int> + <int name="minQueryLength">4</int> + <float name="maxQueryFrequency">0.01</float> + </lst> + </searchComponent> + + <requestHandler name="/spell" class="solr.SearchHandler" startup="lazy"> + <lst name="defaults"> + <str name="spellcheck.dictionary">default</str> + <str name="spellcheck">on</str> + <str name="spellcheck.extendedResults">true</str> + <str name="spellcheck.count">10</str> + <str name="spellcheck.alternativeTermCount">5</str> + <str name="spellcheck.maxResultsForSuggest">5</str> + <str name="spellcheck.collate">true</str> + <str name="spellcheck.collateExtendedResults">true</str> + <str name="spellcheck.maxCollationTries">10</str> + <str name="spellcheck.maxCollations">5</str> + </lst> + <arr name="last-components"> + <str>spellcheck</str> + </arr> + </requestHandler> + + <searchComponent name="terms" class="solr.TermsComponent"/> + + <requestHandler name="/terms" class="solr.SearchHandler" startup="lazy"> + <lst name="defaults"> + <bool name="terms">true</bool> + <bool name="distrib">false</bool> + </lst> + <arr name="components"> + <str>terms</str> + </arr> + </requestHandler> + + <searchComponent class="solr.HighlightComponent" name="highlight"> + <highlighting> + <fragmenter name="gap" + default="true" + class="solr.highlight.GapFragmenter"> + <lst name="defaults"> + <int name="hl.fragsize">100</int> + </lst> + </fragmenter> + + <fragmenter name="regex" + class="solr.highlight.RegexFragmenter"> + <lst name="defaults"> + <int name="hl.fragsize">70</int> + <float name="hl.regex.slop">0.5</float> + <str name="hl.regex.pattern">[-\w ,/\n\"']{20,200}</str> + </lst> + </fragmenter> + + <formatter name="html" + default="true" + class="solr.highlight.HtmlFormatter"> + <lst name="defaults"> + <str name="hl.simple.pre"><![CDATA[<em>]]></str> + <str name="hl.simple.post"><![CDATA[</em>]]></str> + </lst> + </formatter> + + <encoder name="html" + class="solr.highlight.HtmlEncoder" /> + + <fragListBuilder name="simple" + class="solr.highlight.SimpleFragListBuilder"/> + + <fragListBuilder name="single" + class="solr.highlight.SingleFragListBuilder"/> + + <fragListBuilder name="weighted" + default="true" + class="solr.highlight.WeightedFragListBuilder"/> + + <fragmentsBuilder name="default" + default="true" + class="solr.highlight.ScoreOrderFragmentsBuilder"> + </fragmentsBuilder> + + <fragmentsBuilder name="colored" + class="solr.highlight.ScoreOrderFragmentsBuilder"> + <lst name="defaults"> + <str name="hl.tag.pre"><![CDATA[ + <b style="background:yellow">,<b style="background:lawgreen">, + <b style="background:aquamarine">,<b style="background:magenta">, + <b style="background:palegreen">,<b style="background:coral">, + <b style="background:wheat">,<b style="background:khaki">, + <b style="background:lime">,<b style="background:deepskyblue">]]></str> + <str name="hl.tag.post"><![CDATA[</b>]]></str> + </lst> + </fragmentsBuilder> + + <boundaryScanner name="default" + default="true" + class="solr.highlight.SimpleBoundaryScanner"> + <lst name="defaults"> + <str name="hl.bs.maxScan">10</str> + <str name="hl.bs.chars">.,!? 	 </str> + </lst> + </boundaryScanner> + + <boundaryScanner name="breakIterator" + class="solr.highlight.BreakIteratorBoundaryScanner"> + <lst name="defaults"> + <str name="hl.bs.type">WORD</str> + <str name="hl.bs.language">en</str> + <str name="hl.bs.country">US</str> + </lst> + </boundaryScanner> + </highlighting> + </searchComponent> + + <updateProcessor class="solr.UUIDUpdateProcessorFactory" name="uuid"/> + <updateProcessor class="solr.RemoveBlankFieldUpdateProcessorFactory" name="remove-blank"/> + <updateProcessor class="solr.FieldNameMutatingUpdateProcessorFactory" name="field-name-mutating"> + <str name="pattern">[^\w-\.]</str> + <str name="replacement">_</str> + </updateProcessor> + <updateProcessor class="solr.ParseBooleanFieldUpdateProcessorFactory" name="parse-boolean"/> + <updateProcessor class="solr.ParseLongFieldUpdateProcessorFactory" name="parse-long"/> + <updateProcessor class="solr.ParseDoubleFieldUpdateProcessorFactory" name="parse-double"/> + <updateProcessor class="solr.ParseDateFieldUpdateProcessorFactory" name="parse-date"> + <arr name="format"> + <str>yyyy-MM-dd['T'[HH:mm[:ss[.SSS]][z</str> + <str>yyyy-MM-dd['T'[HH:mm[:ss[,SSS]][z</str> + <str>yyyy-MM-dd HH:mm[:ss[.SSS]][z</str> + <str>yyyy-MM-dd HH:mm[:ss[,SSS]][z</str> + <str>[EEE, ]dd MMM yyyy HH:mm[:ss] z</str> + <str>EEEE, dd-MMM-yy HH:mm:ss z</str> + <str>EEE MMM ppd HH:mm:ss [z ]yyyy</str> + </arr> + </updateProcessor> + <updateProcessor class="solr.AddSchemaFieldsUpdateProcessorFactory" name="add-schema-fields"> + <lst name="typeMapping"> + <str name="valueClass">java.lang.String</str> + <str name="fieldType">text_general</str> + <lst name="copyField"> + <str name="dest">*_str</str> + <int name="maxChars">256</int> + </lst> + <bool name="default">true</bool> + </lst> + <lst name="typeMapping"> + <str name="valueClass">java.lang.Boolean</str> + <str name="fieldType">booleans</str> + </lst> + <lst name="typeMapping"> + <str name="valueClass">java.util.Date</str> + <str name="fieldType">pdates</str> + </lst> + <lst name="typeMapping"> + <str name="valueClass">java.lang.Long</str> + <str name="valueClass">java.lang.Integer</str> + <str name="fieldType">plongs</str> + </lst> + <lst name="typeMapping"> + <str name="valueClass">java.lang.Number</str> + <str name="fieldType">pdoubles</str> + </lst> + </updateProcessor> + + <updateRequestProcessorChain name="add-unknown-fields-to-the-schema" default="${update.autoCreateFields:true}" + processor="uuid,remove-blank,field-name-mutating,parse-boolean,parse-long,parse-double,parse-date,add-schema-fields"> + <processor class="solr.LogUpdateProcessorFactory"/> + <processor class="solr.DistributedUpdateProcessorFactory"/> + <processor class="solr.RunUpdateProcessorFactory"/> + </updateRequestProcessorChain> + + <queryResponseWriter name="json" class="solr.JSONResponseWriter"> + <str name="content-type">text/plain; charset=UTF-8</str> + </queryResponseWriter> +</config> diff --git a/files/usr/local/etc/sudoers.d/acme.smtp_server b/files/usr/local/etc/sudoers.d/acme.smtp_server new file mode 100644 index 0000000..5180fdc --- /dev/null +++ b/files/usr/local/etc/sudoers.d/acme.smtp_server @@ -0,0 +1 @@ +acme ALL=(root) NOPASSWD: /usr/sbin/service postfix reload diff --git a/files/usr/local/etc/tika/config.xml.imap_server b/files/usr/local/etc/tika/config.xml.imap_server new file mode 100644 index 0000000..22fe638 --- /dev/null +++ b/files/usr/local/etc/tika/config.xml.imap_server @@ -0,0 +1,21 @@ +<properties> + <server> + <params> + <javaPath>/usr/local/bin/java</javaPath> + <returnStackTrace>false</returnStackTrace> + <forkedJvmArgs> + <arg>-Xmx${tika_heap_size}</arg> + <arg>-XX:+UseG1GC</arg> + <arg>-XX:+PerfDisableSharedMem</arg> + <arg>-XX:+ParallelRefProcEnabled</arg> + <arg>-XX:MaxGCPauseMillis=250</arg> + <arg>-XX:+AlwaysPreTouch</arg> + <arg>-Dlog4j.configurationFile=${tika_conf_dir}/log4j2.xml</arg> + </forkedJvmArgs> + <endpoints> + <endpoint>tika</endpoint> + <endpoint>status</endpoint> + </endpoints> + </params> + </server> +</properties> diff --git a/files/usr/local/etc/tika/log4j2.xml.imap_server b/files/usr/local/etc/tika/log4j2.xml.imap_server new file mode 100644 index 0000000..cabde07 --- /dev/null +++ b/files/usr/local/etc/tika/log4j2.xml.imap_server @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration> + <Appenders> + <Console name="STDOUT" target="SYSTEM_OUT"> + <PatternLayout> + <Pattern> + %maxLen{%-5p %c %m%notEmpty{ =>%ex{short}}}{10240}%n + </Pattern> + </PatternLayout> + </Console> + </Appenders> + <Loggers> + <Root level="INFO"> + <AppenderRef ref="STDOUT"/> + </Root> + </Loggers> +</Configuration> diff --git a/files/usr/local/libexec/dovecot/quota-warning.sh.imap_server b/files/usr/local/libexec/dovecot/quota-warning.sh.imap_server new file mode 100644 index 0000000..96419f5 --- /dev/null +++ b/files/usr/local/libexec/dovecot/quota-warning.sh.imap_server @@ -0,0 +1,20 @@ +#!/bin/sh + +set -eu -o pipefail + +PERCENT=$1 +USER=$2 +FROM=$3 + +cat << EOF | /usr/libexec/dovecot/dovecot-lda -d "$USER" -o "plugin/quota=count:User quota:noenforcing" +From: ${FROM} +Subject: Mailbox quota warning + +This is an automatically generated message. + +Your mailbox is now ${PERCENT}% full. + +When your mailbox exceeds its quota, you will no longer receive new mail. + +Please delete some messages to free up space. +EOF diff --git a/files/usr/local/libexec/dovecot/sieve-pipe/report-ham.sh.imap_server b/files/usr/local/libexec/dovecot/sieve-pipe/report-ham.sh.imap_server new file mode 100644 index 0000000..e09674a --- /dev/null +++ b/files/usr/local/libexec/dovecot/sieve-pipe/report-ham.sh.imap_server @@ -0,0 +1,7 @@ +#!/bin/sh + +exec /usr/local/bin/rspamc \\ + --connect="${rspamd_host}.${domain}" \\ + --password="${rspamd_rw_password}" \\ + --key="${rspamd_pubkey}" \\ + learn_ham diff --git a/files/usr/local/libexec/dovecot/sieve-pipe/report-spam.sh.imap_server b/files/usr/local/libexec/dovecot/sieve-pipe/report-spam.sh.imap_server new file mode 100644 index 0000000..825113f --- /dev/null +++ b/files/usr/local/libexec/dovecot/sieve-pipe/report-spam.sh.imap_server @@ -0,0 +1,7 @@ +#!/bin/sh + +exec /usr/local/bin/rspamc \\ + --connect="${rspamd_host}.${domain}" \\ + --password="${rspamd_rw_password}" \\ + --key="${rspamd_pubkey}" \\ + learn_spam diff --git a/files/var/db/solr/dovecot/conf/schema.xml.imap_server b/files/var/db/solr/dovecot/conf/schema.xml.imap_server new file mode 100644 index 0000000..601a290 --- /dev/null +++ b/files/var/db/solr/dovecot/conf/schema.xml.imap_server @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<schema name="dovecot" version="2.0"> + <fieldType name="string" class="solr.StrField" omitNorms="true" sortMissingLast="true"/> + <fieldType name="long" class="solr.LongPointField" positionIncrementGap="0"/> + <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/> + + <fieldType name="text" class="solr.TextField" autoGeneratePhraseQueries="true" positionIncrementGap="100"> + <analyzer type="index"> + <tokenizer class="solr.StandardTokenizerFactory"/> + <filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/> + <filter class="solr.WordDelimiterGraphFilterFactory" catenateNumbers="1" generateNumberParts="1" splitOnCaseChange="1" generateWordParts="1" splitOnNumerics="1" catenateAll="1" catenateWords="1"/> + <filter class="solr.FlattenGraphFilterFactory"/> + <filter class="solr.LowerCaseFilterFactory"/> + <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/> + <filter class="solr.PorterStemFilterFactory"/> + </analyzer> + <analyzer type="query"> + <tokenizer class="solr.StandardTokenizerFactory"/> + <filter class="solr.SynonymGraphFilterFactory" expand="true" ignoreCase="true" synonyms="synonyms.txt"/> + <filter class="solr.FlattenGraphFilterFactory"/> + <filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/> + <filter class="solr.WordDelimiterGraphFilterFactory" catenateNumbers="1" generateNumberParts="1" splitOnCaseChange="1" generateWordParts="1" splitOnNumerics="1" catenateAll="1" catenateWords="1"/> + <filter class="solr.LowerCaseFilterFactory"/> + <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/> + <filter class="solr.PorterStemFilterFactory"/> + </analyzer> + </fieldType> + + <field name="id" type="string" indexed="true" required="true" stored="true"/> + <field name="uid" type="long" indexed="true" required="true" stored="true"/> + <field name="box" type="string" indexed="true" required="true" stored="true"/> + <field name="user" type="string" indexed="true" required="true" stored="true"/> + + <field name="hdr" type="text" indexed="true" stored="false"/> + <field name="body" type="text" indexed="true" stored="false"/> + + <field name="from" type="text" indexed="true" stored="false"/> + <field name="to" type="text" indexed="true" stored="false"/> + <field name="cc" type="text" indexed="true" stored="false"/> + <field name="bcc" type="text" indexed="true" stored="false"/> + <field name="subject" type="text" indexed="true" stored="false"/> + + <!-- Used by Solr internally: --> + <field name="_version_" type="long" indexed="true" stored="true"/> + + <uniqueKey>id</uniqueKey> +</schema> diff --git a/files/var/db/solr/dovecot/conf/solrconfig.xml.imap_server b/files/var/db/solr/dovecot/conf/solrconfig.xml.imap_server new file mode 100644 index 0000000..918a755 --- /dev/null +++ b/files/var/db/solr/dovecot/conf/solrconfig.xml.imap_server @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<config> + <luceneMatchVersion>9.3.0</luceneMatchVersion> + + <lib dir="${solr.install.dir:../../../..}/contrib/extraction/lib" regex=".*\.jar" /> + <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-cell-\d.*\.jar" /> + + <lib dir="${solr.install.dir:../../../..}/contrib/clustering/lib/" regex=".*\.jar" /> + <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-clustering-\d.*\.jar" /> + + <lib dir="${solr.install.dir:../../../..}/contrib/langid/lib/" regex=".*\.jar" /> + <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-langid-\d.*\.jar" /> + + <lib dir="${solr.install.dir:../../../..}/contrib/velocity/lib" regex=".*\.jar" /> + <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-velocity-\d.*\.jar" /> + + <dataDir>${solr.data.dir:}</dataDir> + + <updateHandler class="solr.DirectUpdateHandler2"> + + <updateLog> + <str name="dir">${solr.ulog.dir:}</str> + <int name="numVersionBuckets">${solr.ulog.numVersionBuckets:65536}</int> + </updateLog> + + <autoCommit> + <maxTime>${solr.autoCommit.maxTime:15000}</maxTime> + <openSearcher>false</openSearcher> + </autoCommit> + + <autoSoftCommit> + <maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime> + </autoSoftCommit> + + </updateHandler> + + <query> + <filterCache class="solr.CaffeineCache" + size="512" + initialSize="512" + autowarmCount="0"/> + + <queryResultCache class="solr.CaffeineCache" + size="512" + initialSize="512" + autowarmCount="0"/> + + <documentCache class="solr.CaffeineCache" + size="512" + initialSize="512" + autowarmCount="0"/> + + <cache name="perSegFilter" + class="solr.search.CaffeineCache" + size="10" + initialSize="0" + autowarmCount="10" + regenerator="solr.NoOpRegenerator" /> + + <enableLazyFieldLoading>true</enableLazyFieldLoading> + + <queryResultWindowSize>20</queryResultWindowSize> + + <queryResultMaxDocsCached>200</queryResultMaxDocsCached> + + <useColdSearcher>false</useColdSearcher> + + </query> + + <requestDispatcher> + <httpCaching never304="true" /> + </requestDispatcher> + + <requestHandler name="/select" class="solr.SearchHandler"> + <lst name="defaults"> + <str name="echoParams">explicit</str> + <int name="rows">10</int> + </lst> + </requestHandler> + + <initParams path="/update/**,/select"> + <lst name="defaults"> + <str name="df">_text_</str> + </lst> + </initParams> + + <queryResponseWriter name="xml" + default="true" + class="solr.XMLResponseWriter" /> +</config> diff --git a/files/var/db/solr/solr.xml.imap_server b/files/var/db/solr/solr.xml.imap_server new file mode 100644 index 0000000..1778390 --- /dev/null +++ b/files/var/db/solr/solr.xml.imap_server @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- + This is an example of a simple "solr.xml" file for configuring one or + more Solr Cores, as well as allowing Cores to be added, removed, and + reloaded via HTTP requests. + + More information about options available in this configuration file, + and Solr Core administration can be found online: + https://solr.apache.org/guide/solr/latest/configuration-guide/configuring-solr-xml.html +--> + +<solr> + + <int name="maxBooleanClauses">${solr.max.booleanClauses:1024}</int> + <str name="sharedLib">${solr.sharedLib:}</str> + <str name="modules">${solr.modules:}</str> + <str name="allowPaths">${solr.allowPaths:}</str> + <str name="allowUrls">${solr.allowUrls:}</str> + + <solrcloud> + + <str name="host">${host:}</str> + <int name="hostPort">${solr.port.advertise:0}</int> + <str name="hostContext">${hostContext:solr}</str> + + <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool> + + <int name="zkClientTimeout">${zkClientTimeout:30000}</int> + <int name="distribUpdateSoTimeout">${distribUpdateSoTimeout:600000}</int> + <int name="distribUpdateConnTimeout">${distribUpdateConnTimeout:60000}</int> + <str name="zkCredentialsProvider">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str> + <str name="zkACLProvider">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str> + <str name="zkCredentialsInjector">${zkCredentialsInjector:org.apache.solr.common.cloud.DefaultZkCredentialsInjector}</str> + <bool name="distributedClusterStateUpdates">${distributedClusterStateUpdates:false}</bool> + <bool name="distributedCollectionConfigSetExecution">${distributedCollectionConfigSetExecution:false}</bool> + <int name="minStateByteLenForCompression">${minStateByteLenForCompression:-1}</int> + <str name="stateCompressor">${stateCompressor:org.apache.solr.common.util.ZLibCompressor}</str> + + </solrcloud> + + <shardHandlerFactory name="shardHandlerFactory" + class="HttpShardHandlerFactory"> + <int name="socketTimeout">${socketTimeout:600000}</int> + <int name="connTimeout">${connTimeout:60000}</int> + </shardHandlerFactory> + + <metrics enabled="${metricsEnabled:true}"> + <!-- Solr computes JVM metrics for threads. Computing these metrics, esp. computing deadlocks etc., + requires potentially expensive computations, and can be avoided for every metrics call by + setting a high caching expiration interval (in seconds). + <caching> + <int name="threadsIntervalSeconds">5</int> + </caching> + --> + <!--reporter name="jmx_metrics" group="core" class="org.apache.solr.metrics.reporters.SolrJmxReporter"/--> + </metrics> + + +</solr> diff --git a/scripts/hostclass/imap_server/10-solr b/scripts/hostclass/imap_server/10-solr new file mode 100644 index 0000000..252a8c3 --- /dev/null +++ b/scripts/hostclass/imap_server/10-solr @@ -0,0 +1,78 @@ +#!/bin/sh + +: ${solr_version:='9.7.0'} + +solr_uid=161 +solr_user=solr +solr_data_dir=/var/db/solr +solr_conf_dir=/usr/local/etc/solr +solr_install_dir=/usr/local/solr +solr_heap_size=2g +solr_softcommit_ms=60000 +solr_url="https://dlcdn.apache.org/solr/solr/${solr_version}/solr-${solr_version}-slim.tgz" +solr_port=8983 + +# Install dependencies. +pkg install -y \ + curl \ + openjdk21 \ + bash + +# Add local solr user. +add_user \ + -u "$solr_uid" \ + -c 'Apache Solr' \ + -d "$solr_data_dir" \ + -s /usr/sbin/nologin \ + "$solr_user" + +# Create ZFS dataset for solr DB. +create_dataset -o "mountpoint=${solr_data_dir}" "${state_dataset}/solr" + +# Set ownership on solr DB dir. +install_directory -m 0770 -o "$solr_user" -g "$solr_user" "$solr_data_dir" + +# Create solr install/config directories. +install_directory -m 0755 \ + "$solr_install_dir" \ + "$solr_conf_dir" + +# Download and extract the solr tarball. +curl -fL "$solr_url" | tar xf - -C "$solr_install_dir" --strip-components 1 + +# Copy solr rc script. +install_file -m 0555 /usr/local/etc/rc.d/solr + +# Copy solr config files. +install_file -m 0644 \ + "${solr_conf_dir}/solrconfig.xml" \ + "${solr_conf_dir}/log4j2.xml" + +# Copy the default solr config from the distribution. +install -v -m 0644 -o "$solr_user" -g "$solr_user" \ + "${solr_install_dir}/server/solr/solr.xml" \ + "${solr_data_dir}/solr.xml" + +# Enable and start solr. +sysrc -v \ + solr_enable=YES \ + solr_heap_size="$solr_heap_size" + +# Start solr. +service solr restart + +# Create solr collection for dovecot. +if ! [ -d "${solr_data_dir}/dovecot" ]; then + log "waiting a few seconds for solr to finish starting up" + sleep 3 + JAVA_TOOL_OPTIONS='-Xmx64m' su -m "$solr_user" -c "${solr_install_dir}/bin/solr create --name dovecot --solr-url http://127.0.0.1:${solr_port}" +fi + +# Copy solr configs for dovecot. +install_file -m 0644 -o "$solr_user" -g "$solr_user" \ + "${solr_data_dir}/dovecot/conf/schema.xml" \ + "${solr_data_dir}/dovecot/conf/solrconfig.xml" +rm -f "${solr_data_dir}/dovecot/conf/managed-schema.xml" + +# Restart solr. +service solr restart diff --git a/scripts/hostclass/imap_server/20-tika b/scripts/hostclass/imap_server/20-tika new file mode 100644 index 0000000..3b4aa47 --- /dev/null +++ b/scripts/hostclass/imap_server/20-tika @@ -0,0 +1,38 @@ +#!/bin/sh + +: ${tika_version:='2.9.2'} +: ${tika_uid:='787'} + +tika_user=tika +tika_conf_dir=/usr/local/etc/tika +tika_install_dir=/usr/local/tika +tika_heap_size=2g +tika_port=9998 +tika_url="https://dlcdn.apache.org/tika/${tika_version}/tika-server-standard-${tika_version}.jar" + +# Add local tika user. +add_user \ + -u "$tika_uid" \ + -c 'Apache Tika' \ + -d /nonexistent \ + -s /usr/sbin/nologin \ + "$tika_user" + +# Create tika install/config directories. +install_directory -m 0755 \ + "$tika_install_dir" \ + "$tika_conf_dir" + +# Download tika jar file. +curl -fL -o "${tika_install_dir}/tika.jar" "$tika_url" + +# Copy tika rc script. +install_file -m 0555 /usr/local/etc/rc.d/tika + +# Copy tika config files. +install_template -m 0644 "${tika_conf_dir}/config.xml" +install_file -m 0644 "${tika_conf_dir}/log4j2.xml" + +# Enable and start tika. +sysrc -v tika_enable=YES +service tika restart diff --git a/scripts/hostclass/imap_server/30-dovecot b/scripts/hostclass/imap_server/30-dovecot new file mode 100644 index 0000000..07c089e --- /dev/null +++ b/scripts/hostclass/imap_server/30-dovecot @@ -0,0 +1,108 @@ +#!/bin/sh + +: ${dovecot_recipient_delimiter:='+'} +: ${dovecot_default_quota:='10G'} +: ${dovecot_quota_grace_percent:='5'} +: ${dovecot_quota_mail_from:="postmaster@${email_domain}"} +: ${rspamd_host:='smtp'} + +dovecot_user=dovecot +dovecot_login_user=dovenull +dovecot_vmail_user=vmail +dovecot_vmail_uid=793 +dovecot_vmail_dir=/var/db/vmail +dovecot_conf_dir=/usr/local/etc/dovecot +dovecot_script_dir=/usr/local/libexec/dovecot +dovecot_sieve_before_dir="${dovecot_conf_dir}/sieve-before.d" +dovecot_sieve_pipe_bin_dir="${dovecot_script_dir}/sieve-pipe" +dovecot_keytab="${keytab_dir}/dovecot.keytab" +dovecot_tls_cert="${dovecot_conf_dir}/dovecot.crt" +dovecot_tls_key="${dovecot_conf_dir}/dovecot.key" +dovecot_cipherlist='ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305' + +pkg install -y \ + dovecot \ + dovecot-pigeonhole \ + rspamd + +# Add local vmail user. +add_user \ + -u "$dovecot_vmail_uid" \ + -c 'Virtual Mail User' \ + -d "$dovecot_vmail_dir" \ + -s /usr/sbin/nologin \ + "$dovecot_vmail_user" + +# Create ZFS dataset for virtual maildirs. +create_dataset -o "mountpoint=${dovecot_vmail_dir}" "${state_dataset}/mailboxes" + +# Set ownership on vmail dir. +install_directory -m 0770 -o "$dovecot_vmail_user" -g "$dovecot_vmail_user" "$dovecot_vmail_dir" + +# Create service principals and keytab. +add_principal -nokey -x "containerdn=${services_basedn}" "imap/${fqdn}" +add_principal -nokey -x "containerdn=${services_basedn}" "sieve/${fqdn}" + +ktadd -k "$dovecot_keytab" "imap/${fqdn}" +ktadd -k "$dovecot_keytab" "sieve/${fqdn}" +chgrp "$dovecot_user" "$dovecot_keytab" +chmod 640 "$dovecot_keytab" + +dovecot_uid=$(id -u "$dovecot_user") +install_directory -o "$dovecot_user" -m 0700 "/var/krb5/user/${dovecot_uid}" +ln -snfv "$dovecot_keytab" "/var/krb5/user/${dovecot_uid}/keytab" +ln -snfv "$dovecot_keytab" "/var/krb5/user/${dovecot_uid}/client.keytab" + +# Create dovecot directories. +install_directory -m 0755 \ + "${dovecot_conf_dir}/conf.d" \ + "$dovecot_sieve_before_dir" \ + "$dovecot_sieve_pipe_bin_dir" + +# Generate dovecot configuration. +install_template -m 0644 \ + "${dovecot_conf_dir}/dovecot.conf" \ + "${dovecot_conf_dir}/dovecot-ldap-userdb.conf.ext" \ + "${dovecot_conf_dir}/dovecot-ldap-passdb.conf.ext" \ + "${dovecot_conf_dir}/conf.d/10-auth.conf" \ + "${dovecot_conf_dir}/conf.d/10-mail.conf" \ + "${dovecot_conf_dir}/conf.d/10-master.conf" \ + "${dovecot_conf_dir}/conf.d/10-ssl.conf" \ + "${dovecot_conf_dir}/conf.d/15-lda.conf" \ + "${dovecot_conf_dir}/conf.d/90-fts.conf" \ + "${dovecot_conf_dir}/conf.d/90-quota.conf" \ + "${dovecot_conf_dir}/conf.d/90-sieve.conf" \ + "${dovecot_conf_dir}/conf.d/90-sieve-extprograms.conf" \ + "${dovecot_conf_dir}/conf.d/auth-ldap.conf.ext" + +install_template -m 0550 -o root -g "$dovecot_user" \ + "${dovecot_sieve_pipe_bin_dir}/report-spam.sh" \ + "${dovecot_sieve_pipe_bin_dir}/report-ham.sh" \ + +install_file -m 0555 \ + "${dovecot_script_dir}/quota-warning.sh" + +install_file -m 0644 \ + "${dovecot_conf_dir}/conf.d/15-mailboxes.conf" \ + "${dovecot_conf_dir}/conf.d/20-imap.conf" \ + "${dovecot_conf_dir}/conf.d/20-lmtp.conf" \ + "${dovecot_conf_dir}/conf.d/20-managesieve.conf" \ + "${dovecot_conf_dir}/report-ham.sieve" \ + "${dovecot_conf_dir}/report-spam.sieve" \ + "${dovecot_sieve_before_dir}/10-rspamd.sieve" + +# Compile sieve scripts. +sievec "${dovecot_conf_dir}/report-ham.sieve" +sievec "${dovecot_conf_dir}/report-spam.sieve" +sievec "${dovecot_sieve_before_dir}/10-rspamd.sieve" + +# Copy TLS certificate for dovecot. +install_certificate -m 0644 -o root -g "$dovecot_user" dovecot "$dovecot_tls_cert" +install_certificate_key -m 0640 -o root -g "$dovecot_user" dovecot "$dovecot_tls_key" + +# Enable and start dovecot and dependencies. +sysrc -v dovecot_enable=YES +service dovecot restart + +# Disable rspamd log rotation (we don't actually run rspamd here). +echo '# intentionally empty' > /usr/local/etc/newsyslog.conf.d/rspamd.newsyslog.conf diff --git a/scripts/hostclass/smtp_server/20-postfix b/scripts/hostclass/smtp_server/20-postfix index 0d4830f..e224e9b 100644 --- a/scripts/hostclass/smtp_server/20-postfix +++ b/scripts/hostclass/smtp_server/20-postfix @@ -7,12 +7,8 @@ : ${postfix_recipient_delimiter:='+'} : ${postfix_message_size_limit:='67108864'} # 64 MB : ${postfix_virtual_domains:="$email_domain"} -: ${postfix_lmtp_port:='24'} -: ${postfix_quota_port:='10993'} : ${imap_host='imap'} -: ${lmtp_port='25'} -: ${quota_status_port='10993'} postfix_conf_dir=/usr/local/etc/postfix postfix_user=postfix @@ -57,7 +53,7 @@ install_certificate_key -m 0640 -o root -g "$postfix_user" postfix "$postfix_loc if [ "$postfix_public_fqdn" != "$fqdn" ]; then # Acquire public TLS certificate. install_file /usr/local/etc/sudoers.d/acme - get_acme_certificate \ + acme_install_certificate \ -c "$postfix_public_tls_cert" \ -k "$postfix_public_tls_key" \ -g "$postfix_user" \ diff --git a/scripts/os/freebsd/60-acme b/scripts/os/freebsd/60-acme new file mode 100644 index 0000000..902e674 --- /dev/null +++ b/scripts/os/freebsd/60-acme @@ -0,0 +1,75 @@ +#!/bin/sh + +[ "${acme:-}" = true ] || return 0 + +: ${acme_email:="root@${email_domain}"} +: ${acme_keylength:='ec-256'} + +acme_cert_dir=/usr/local/etc/ssl/acme +acme_standalone_port=9080 +acme_user=acme +acme_home=/var/db/acme +acme_webroot=/usr/local/www/acme + +pkg install -y acme.sh + +install_directory -m 0775 -o root -g "$acme_user" "$acme_cert_dir" +install_template -m 0644 /etc/cron.d/acme + +if [ -n "${acme_eab_kid:-}" ]; then + su -m "$acme_user" -c "acme.sh --home ${acme_home} --register-account --eab-kid ${acme_eab_kid} --eab-hmac-key ${acme_eab_hmac_key}" +else + su -m "$acme_user" -c "acme.sh --home ${acme_home} --register-account --email ${acme_email}" +fi + +acme_install_certificate(){ + _aic_group=0 + _aic_cert_path= + _aic_key_path= + _aic_reload_cmd= + + while getopts c:g:k:r: _aic_opt; do + case $_aic_opt in + c) _aic_cert_path=$OPTARG ;; + g) _aic_group=$OPTARG ;; + k) _aic_key_path=$OPTARG ;; + r) _aic_reload_cmd=$OPTARG ;; + esac + done + + shift $((OPTIND - 1)) + _aic_name=$1 + + # Acquire the certificate via HTTP ACME challenge. + _aic_domain_args='' + for _aic_domain; do + _aic_domain_args="${_aic_domain_args} -d ${_aic_domain}" + done + + if [ -n "${acme_standalone:-}" ]; then + su -m "$acme_user" -c "acme.sh --home ${acme_home} --issue --keylength ${acme_keylength} --standalone --httport ${acme_standalone_port} ${_aic_domain_args}" && _aic_rc=$? || _aic_rc=$? + else + install_directory -o root -g "$acme_user" -m 0775 "$acme_webroot" + su -m "$acme_user" -c "acme.sh --home ${acme_home} --issue --keylength ${acme_keylength} -w ${acme_webroot} ${_aic_domain_args}" && _aic_rc=$? || _aic_rc=$? + fi + + case $_aic_rc in + 0) ;; # New cert was issued. + 2) ;; # Cert was unchanged. + *) die "failed to issue ACME certificate for: $*" ;; + esac + + # Install the certificate to the requested location. + if [ -f "$_aic_key_path" ]; then + chmod 640 "$_aic_key_path" + chown "${acme_user}:${_aic_group}" "$_aic_key_path" + else + install -o "$acme_user" -g "$_aic_group" -m 0640 /dev/null "$_aic_key_path" + fi + + if [ -n "$_aic_reload_cmd" ]; then + su -m "$acme_user" -c "acme.sh --home ${acme_home} --install-cert --domain ${_aic_name} --key-file ${_aic_key_path} --fullchain-file ${_aic_cert_path} --reloadcmd '${_aic_reload_cmd}'" + else + su -m "$acme_user" -c "acme.sh --home ${acme_home} --install-cert --domain ${_aic_name} --key-file ${_aic_key_path} --fullchain-file ${_aic_cert_path}" + fi +} diff --git a/vars/common b/vars/common index 7c54673..18d0e52 100644 --- a/vars/common +++ b/vars/common @@ -39,6 +39,8 @@ graphics_type=intel boxconf_username='s-boxconf' host_keytab_groupname=hostkeytab host_keytab_gid=788 +lmtp_port=25 +quota_status_port=10993 krb5_ticket_lifetime=24h krb5_renew_lifetime=7d nslcd_min_uid=1000 diff --git a/vars/hostclass/imap_server b/vars/hostclass/imap_server new file mode 100644 index 0000000..c1467b2 --- /dev/null +++ b/vars/hostclass/imap_server @@ -0,0 +1,3 @@ +#!/bin/sh + +allowed_tcp_ports="ssh imaps ${lmtp_port} ${quota_status_port}" diff --git a/vars/hostclass/smtp_server b/vars/hostclass/smtp_server index d68e28a..fd05469 100644 --- a/vars/hostclass/smtp_server +++ b/vars/hostclass/smtp_server @@ -2,5 +2,5 @@ allowed_tcp_ports="ssh smtp submission ${rspamd_port} http https" postfix_mynetworks='127.0.0.1/8' +acme=true nginx_gssapi=true -nginx_acme=true diff --git a/vars/hostname/imap1 b/vars/hostname/imap1 new file mode 100644 index 0000000..09de713 --- /dev/null +++ b/vars/hostname/imap1 @@ -0,0 +1,3 @@ +#!/bin/sh + +cnames=imap diff --git a/vars/os/freebsd b/vars/os/freebsd index 8b0afb3..12d3938 100644 --- a/vars/os/freebsd +++ b/vars/os/freebsd @@ -9,11 +9,6 @@ intel_epp=50 see_other_uids=0 export ASSUME_ALWAYS_YES=yes -acme_cert_dir=/usr/local/etc/ssl/acme -acme_standalone_port=9080 -acme_uid=169 -acme_webroot=/usr/local/www/acme -apache_version=24 keytab_dir=/var/db/keytabs nfscbd_port=7745 nginx_user=www |