From 6b1e055d3dcc91b6aabdcaab39552821a230008a Mon Sep 17 00:00:00 2001 From: Evilham Date: Wed, 27 May 2020 18:34:06 +0200 Subject: [PATCH 01/54] [__root_mail_dma] Add new role to manage local root mail. This type has been tested in FreeBSD and Debian-like systems (Debian, Devuan, Ubuntu). --- type/__root_mail_dma/files/aliases | 68 ++++++++ type/__root_mail_dma/gencode-remote | 20 +++ type/__root_mail_dma/man.rst | 83 ++++++++++ type/__root_mail_dma/manifest | 153 ++++++++++++++++++ type/__root_mail_dma/parameter/boolean | 1 + type/__root_mail_dma/parameter/optional | 1 + type/__root_mail_dma/parameter/required | 1 + .../parameter/required_multiple | 1 + type/__root_mail_dma/singleton | 0 9 files changed, 328 insertions(+) create mode 100644 type/__root_mail_dma/files/aliases create mode 100755 type/__root_mail_dma/gencode-remote create mode 100644 type/__root_mail_dma/man.rst create mode 100755 type/__root_mail_dma/manifest create mode 100644 type/__root_mail_dma/parameter/boolean create mode 100644 type/__root_mail_dma/parameter/optional create mode 100644 type/__root_mail_dma/parameter/required create mode 100644 type/__root_mail_dma/parameter/required_multiple create mode 100644 type/__root_mail_dma/singleton diff --git a/type/__root_mail_dma/files/aliases b/type/__root_mail_dma/files/aliases new file mode 100644 index 0000000..d341318 --- /dev/null +++ b/type/__root_mail_dma/files/aliases @@ -0,0 +1,68 @@ +# Based off FreeBSD's /etc/aliases +# +# >>>>>>>>>> The program "newaliases" must be run after +# >> NOTE >> this file is updated for any changes to +# >>>>>>>>>> show through to sendmail. +# +# +# See also RFC 2142, `MAILBOX NAMES FOR COMMON SERVICES, ROLES +# AND FUNCTIONS', May 1997 +# http://tools.ietf.org/html/rfc2142 + +# Pretty much everything else in this file points to "root", so +# you would do well in either reading root's mailbox or forwarding +# root's email from here. + +# root: me@my.domain + + +# Basic system aliases -- these MUST be present +MAILER-DAEMON: postmaster +postmaster: root + +# General redirections for pseudo accounts +_dhcp: root +_pflogd: root +auditdistd: root +bin: root +bind: root +daemon: root +games: root +hast: root +kmem: root +mailnull: postmaster +man: root +news: root +nobody: root +operator: root +pop: root +proxy: root +smmsp: postmaster +sshd: root +system: root +toor: root +tty: root +usenet: news +uucp: root + +# Well-known aliases -- these should be filled in! +manager: root +dumper: root + +# BUSINESS-RELATED MAILBOX NAMES +info: root +marketing: root +sales: root +support: root + +# NETWORK OPERATIONS MAILBOX NAMES +abuse: root +noc: root +security: root + +# SUPPORT MAILBOX NAMES FOR SPECIFIC INTERNET SERVICES +ftp: root +ftp-bugs: ftp +hostmaster: root +webmaster: root +www: webmaster diff --git a/type/__root_mail_dma/gencode-remote b/type/__root_mail_dma/gencode-remote new file mode 100755 index 0000000..2961c09 --- /dev/null +++ b/type/__root_mail_dma/gencode-remote @@ -0,0 +1,20 @@ +#!/bin/sh -e + +if [ -f "${__object}/parameter/send-test-email" ]; then + SEND_EMAIL="YES" +fi + +if [ "${SEND_EMAIL}" != "YES" ]; then + exit 0 +fi + +cat <`_ +- `DragonFly Handbook MTA `_ + + +AUTHORS +------- +Evilham + + +COPYING +------- +Copyright \(C) 2020 Evilham. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/type/__root_mail_dma/manifest b/type/__root_mail_dma/manifest new file mode 100755 index 0000000..abcaa5b --- /dev/null +++ b/type/__root_mail_dma/manifest @@ -0,0 +1,153 @@ +#!/bin/sh -e + +os="$(cat "${__global}/explorer/os")" + +root_email="$(tr '\n' ',' < "${__object}/parameter/root-email" | sed -E 's/,+$//')" +smart_host="$(cat "${__object}/parameter/smart-host")" + +if [ -f "${__object}/parameter/mailname" ]; then + mailname="$(cat "${__object}/parameter/mailname")" +else + # default mailname behaviour is different in certain systems + case ${os} in + debian|devuan|ubuntu) + # Debian-like default to /etc/mailname + mailname="/etc/mailname" + ;; + *) + # Otherwise let's use the hostname + mailname="${__target_host}" + ;; + esac +fi + +aliases_file=/etc/mail/aliases +case ${os} in + debian|devuan|ubuntu) + # Debian-like requires installing DMA + __package dma + # Moving forward without DMA doesn't make much sense + export require="__package/dma" + aliases_file=/etc/aliases + ;; + freebsd) + # Disable sendmail + stop if necessary + __key_value \ + --file "/etc/rc.conf" \ + --comment "# Disable sendmail " \ + --key "sendmail_enable" \ + --delimiter "=" \ + --value "NONE" \ + --onchange "service sendmail onestop || true" \ + "sendmail_enable" + # Setup mailwrapper accordingly + __file /etc/mail/mailer.conf \ + --mode 0644 \ + --source '-' < /dev/stderr < Date: Fri, 29 May 2020 17:33:23 +0200 Subject: [PATCH 02/54] Rename __root_mail_dma to __dma to prepare for new types --- type/{__root_mail_dma => __dma}/files/aliases | 0 type/{__root_mail_dma => __dma}/gencode-remote | 0 type/{__root_mail_dma => __dma}/man.rst | 6 +++--- type/{__root_mail_dma => __dma}/manifest | 0 type/{__root_mail_dma => __dma}/parameter/boolean | 0 type/{__root_mail_dma => __dma}/parameter/optional | 0 type/{__root_mail_dma => __dma}/parameter/required | 0 type/{__root_mail_dma => __dma}/parameter/required_multiple | 0 type/{__root_mail_dma => __dma}/singleton | 0 9 files changed, 3 insertions(+), 3 deletions(-) rename type/{__root_mail_dma => __dma}/files/aliases (100%) rename type/{__root_mail_dma => __dma}/gencode-remote (100%) rename type/{__root_mail_dma => __dma}/man.rst (94%) rename type/{__root_mail_dma => __dma}/manifest (100%) rename type/{__root_mail_dma => __dma}/parameter/boolean (100%) rename type/{__root_mail_dma => __dma}/parameter/optional (100%) rename type/{__root_mail_dma => __dma}/parameter/required (100%) rename type/{__root_mail_dma => __dma}/parameter/required_multiple (100%) rename type/{__root_mail_dma => __dma}/singleton (100%) diff --git a/type/__root_mail_dma/files/aliases b/type/__dma/files/aliases similarity index 100% rename from type/__root_mail_dma/files/aliases rename to type/__dma/files/aliases diff --git a/type/__root_mail_dma/gencode-remote b/type/__dma/gencode-remote similarity index 100% rename from type/__root_mail_dma/gencode-remote rename to type/__dma/gencode-remote diff --git a/type/__root_mail_dma/man.rst b/type/__dma/man.rst similarity index 94% rename from type/__root_mail_dma/man.rst rename to type/__dma/man.rst index ecf2885..a10c6c2 100644 --- a/type/__root_mail_dma/man.rst +++ b/type/__dma/man.rst @@ -1,9 +1,9 @@ -cdist-type__root_mail_dma(7) +cdist-type__dma(7) ============================ NAME ---- -cdist-type__root_mail_dma - Setup root email with the DragonFly Mail Agent +cdist-type__dma - Setup the DragonFly Mail Agent as the MTA. DESCRIPTION @@ -57,7 +57,7 @@ EXAMPLES # Send root email to both our BOFH and the nice-admin. # That way they can figure things out together. - __root_mail_dma \ + __dma \ --root-email bofh@domain.tld \ --root-email nice-admin@domain.tld \ --smart-host mx1.domain.tld \ diff --git a/type/__root_mail_dma/manifest b/type/__dma/manifest similarity index 100% rename from type/__root_mail_dma/manifest rename to type/__dma/manifest diff --git a/type/__root_mail_dma/parameter/boolean b/type/__dma/parameter/boolean similarity index 100% rename from type/__root_mail_dma/parameter/boolean rename to type/__dma/parameter/boolean diff --git a/type/__root_mail_dma/parameter/optional b/type/__dma/parameter/optional similarity index 100% rename from type/__root_mail_dma/parameter/optional rename to type/__dma/parameter/optional diff --git a/type/__root_mail_dma/parameter/required b/type/__dma/parameter/required similarity index 100% rename from type/__root_mail_dma/parameter/required rename to type/__dma/parameter/required diff --git a/type/__root_mail_dma/parameter/required_multiple b/type/__dma/parameter/required_multiple similarity index 100% rename from type/__root_mail_dma/parameter/required_multiple rename to type/__dma/parameter/required_multiple diff --git a/type/__root_mail_dma/singleton b/type/__dma/singleton similarity index 100% rename from type/__root_mail_dma/singleton rename to type/__dma/singleton From a491e8739efb6753d52ede2ee58ba5a93247d1b4 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Fri, 29 May 2020 17:33:40 +0200 Subject: [PATCH 03/54] Add __dma_auth type --- type/__dma_auth/gencode-remote | 20 +++++++++++++ type/__dma_auth/man.rst | 53 ++++++++++++++++++++++++++++++++++ type/__dma_auth/manifest | 30 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100755 type/__dma_auth/gencode-remote create mode 100644 type/__dma_auth/man.rst create mode 100755 type/__dma_auth/manifest diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote new file mode 100755 index 0000000..77ad9d2 --- /dev/null +++ b/type/__dma_auth/gencode-remote @@ -0,0 +1,20 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + diff --git a/type/__dma_auth/man.rst b/type/__dma_auth/man.rst new file mode 100644 index 0000000..cd4f647 --- /dev/null +++ b/type/__dma_auth/man.rst @@ -0,0 +1,53 @@ +cdist-type__dma_auth(7) +======================= + +NAME +---- +cdist-type__dma_auth - TODO + + +DESCRIPTION +----------- +This space intentionally left blank. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +None. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # TODO + __dma_auth + + +SEE ALSO +-------- +:strong:`TODO`\ (7) + + +AUTHORS +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2020 Dennis Camera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/type/__dma_auth/manifest b/type/__dma_auth/manifest new file mode 100755 index 0000000..e0c809a --- /dev/null +++ b/type/__dma_auth/manifest @@ -0,0 +1,30 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + + +os=$(cat "$__global/explorer/os") + +case "$os" in + *) + printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + ;; +esac From 98496aa8e5c8463b59263f20daa162bed9b63dfa Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Fri, 29 May 2020 17:33:52 +0200 Subject: [PATCH 04/54] Add __mail_alias type --- type/__mail_alias/gencode-remote | 20 ++++++++++++ type/__mail_alias/man.rst | 53 ++++++++++++++++++++++++++++++++ type/__mail_alias/manifest | 30 ++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100755 type/__mail_alias/gencode-remote create mode 100644 type/__mail_alias/man.rst create mode 100755 type/__mail_alias/manifest diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote new file mode 100755 index 0000000..77ad9d2 --- /dev/null +++ b/type/__mail_alias/gencode-remote @@ -0,0 +1,20 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + diff --git a/type/__mail_alias/man.rst b/type/__mail_alias/man.rst new file mode 100644 index 0000000..d9563a6 --- /dev/null +++ b/type/__mail_alias/man.rst @@ -0,0 +1,53 @@ +cdist-type__mail_alias(7) +========================= + +NAME +---- +cdist-type__mail_alias - TODO + + +DESCRIPTION +----------- +This space intentionally left blank. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +None. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # TODO + __mail_alias + + +SEE ALSO +-------- +:strong:`TODO`\ (7) + + +AUTHORS +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2020 Dennis Camera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/type/__mail_alias/manifest b/type/__mail_alias/manifest new file mode 100755 index 0000000..e0c809a --- /dev/null +++ b/type/__mail_alias/manifest @@ -0,0 +1,30 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + + +os=$(cat "$__global/explorer/os") + +case "$os" in + *) + printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + ;; +esac From 3adc4f160998776033934d65ba22f1666c9e0b4b Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sat, 30 May 2020 17:24:26 +0200 Subject: [PATCH 05/54] [type/__mail_alias] Implement type --- type/__mail_alias/explorer/aliases | 59 ++++++++++ .../{manifest => explorer/aliases_file} | 38 ++++-- type/__mail_alias/gencode-remote | 108 ++++++++++++++++++ type/__mail_alias/man.rst | 21 +++- type/__mail_alias/parameter/default/state | 1 + type/__mail_alias/parameter/optional | 1 + type/__mail_alias/parameter/optional_multiple | 1 + 7 files changed, 215 insertions(+), 14 deletions(-) create mode 100755 type/__mail_alias/explorer/aliases rename type/__mail_alias/{manifest => explorer/aliases_file} (56%) mode change 100755 => 100644 create mode 100644 type/__mail_alias/parameter/default/state create mode 100644 type/__mail_alias/parameter/optional create mode 100644 type/__mail_alias/parameter/optional_multiple diff --git a/type/__mail_alias/explorer/aliases b/type/__mail_alias/explorer/aliases new file mode 100755 index 0000000..ce1a439 --- /dev/null +++ b/type/__mail_alias/explorer/aliases @@ -0,0 +1,59 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# Find aliases for a given name and print the aliases line separated + +aliases_file=$("${__type_explorer}/aliases_file") +test -r "${aliases_file}" || exit 0 + +name=$__object_id + +awk -F ':[[:blank:]]*' ' +function print_aliases (aliases, matches) { + split(aliases, matches, /,[[:blank:]]*/) + for (i in matches) { + gsub(/^[[:blank:]]*|[[:blank:]]*$/, "", matches[i]) + print matches[i] + } +} + +/^#/ { + # comment + select = 0; cont = 0; next +} + +{ + cont = ($0 ~ /\\$/) + if (cont) sub(/[[:blank:]]*\\$/, "", $0) +} + +/^[[:blank:]]/ || cont { + # continuation line + if (select) print_aliases($0) + next +} + +$1 == ENVIRON["__object_id"] { + select = 1 + print_aliases($2) + next +} + +{ select = 0 } +' "${aliases_file}" diff --git a/type/__mail_alias/manifest b/type/__mail_alias/explorer/aliases_file old mode 100755 new mode 100644 similarity index 56% rename from type/__mail_alias/manifest rename to type/__mail_alias/explorer/aliases_file index e0c809a..f7c4596 --- a/type/__mail_alias/manifest +++ b/type/__mail_alias/explorer/aliases_file @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -17,14 +17,36 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # +# This explorer tries to find the correct aliases file. +found() { echo "$*"; exit 0; } -os=$(cat "$__global/explorer/os") +check_file() { + if test -f "$1" + then + found "$1" + fi +} -case "$os" in - *) - printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2 - printf "Please contribute an implementation for it if you can.\n" >&2 - exit 1 - ;; +case $("$__explorer/os") +in + (freebsd|openbsd|solaris) + check_file /etc/mail/aliases + + # default + found /etc/mail/aliases + ;; + (debian|devuan|ubuntu) + check_file /etc/aliases + + # default + found /etc/aliases + ;; + (*) + check_file /etc/mail/aliases + check_file /etc/aliases + + # default + found /etc/mail/aliases + ;; esac diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index 77ad9d2..a93dff2 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -18,3 +18,111 @@ # along with cdist. If not, see . # +state_should=$(cat "${__object}/parameter/state") + +case $state_should +in + (present) + if cmp "${__object}/explorer/aliases" "${__object}/parameter/alias" + then + # all good! + exit 0 + fi + + echo "set aliases" >>"$__messages_out" + mode=1 + ;; + (absent) + # nothing to do if no aliases found. + test -s "${__object}/explorer/aliases" || exit 0 + + echo "delete aliases" >>"$__messages_out" + mode=0 + ;; + (*) + printf 'Invalid --state given: %s\n' "$state_should" >&2 + exit 1 +esac + +aliases_file=$(cat "${__object}/explorer/aliases_file") + +if test -z "${aliases_file}" +then + echo 'Could not determine aliases file path.' >&2 + exit 1 +fi + +# "export" variables to remote +printf 'mode=%u\n' "${mode}" +printf "aliases_file='%s'\n" "${aliases_file}" + +cat <<'EOF' +awk -F ':[[:blank:]]*' -v mode="${mode}" ' +function sepafter(f, default, _) { + _ = substr($0, length($f) + 1, index(substr($0, length($f)+1), $(f+1)) - 1) + if (_) return _ + else return default +} + +function write_aliases() { + if (aliases_written) return + + printf "%s%s", ENVIRON["__object_id"], sepafter(1, ": ") + while ((getline < aliases_should_file) > 0) { + if (aliases_written) printf ", " + printf "%s", $0 + aliases_written = 1 + } + printf "\n" + close(aliases_should_file) +} + +BEGIN { + aliases_should_file = (ENVIRON["__object"] "/parameter/alias") +} + +/^#/ { + # comment + select = 0; cont = 0 + print + next +} + +{ + cont = ($0 ~ /\\$/) + if (cont) sub(/[[:blank:]]*\\$/, "", $0) +} + +/^[[:blank:]]/ || cont { + # continuation line + if (select) next +} + +$1 == ENVIRON["__object_id"] { + in_list = 1 + if (mode) write_aliases() + next +} + +{ + in_list = 0 + print +} + +END { + # if the last line as an alias definition, the separator will be reused + if (mode && !aliases_written) write_aliases() +} +' <"${aliases_file}" >"${aliases_file}.tmp" || { + echo 'Generating new aliases file failed!' >&2 + exit 1 +} + +if ! cmp "${aliases_file}" "${aliases_file}.tmp" +then + mv "${aliases_file}.tmp" "${aliases_file}" + newaliases +else + rm "${aliases_file}.tmp" +fi +EOF diff --git a/type/__mail_alias/man.rst b/type/__mail_alias/man.rst index d9563a6..d6c7873 100644 --- a/type/__mail_alias/man.rst +++ b/type/__mail_alias/man.rst @@ -3,12 +3,12 @@ cdist-type__mail_alias(7) NAME ---- -cdist-type__mail_alias - TODO +cdist-type__mail_alias - Manage mail aliases. DESCRIPTION ----------- -This space intentionally left blank. +This cdist type allows you to configure mail aliases (/etc/mail/aliases). REQUIRED PARAMETERS @@ -18,7 +18,14 @@ None. OPTIONAL PARAMETERS ------------------- -None. +state + 'present' or 'absent', defaults to 'present' +alias + the aliases where mail for the given user should be redirected to. + This parameter can be specified multiple times to redirect to more than one + recipient. + See the `aliases(5)` man page for the different forms this parameter can + take.. BOOLEAN PARAMETERS @@ -31,13 +38,15 @@ EXAMPLES .. code-block:: sh - # TODO - __mail_alias + # Redirect root mail to a "real" email address + __mail_alias root --alias admin@example.com + # Disable redirection of mail for joe + __mail_alias joe --state absent SEE ALSO -------- -:strong:`TODO`\ (7) +:strong:`aliases`\ (5) AUTHORS diff --git a/type/__mail_alias/parameter/default/state b/type/__mail_alias/parameter/default/state new file mode 100644 index 0000000..e7f6134 --- /dev/null +++ b/type/__mail_alias/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/type/__mail_alias/parameter/optional b/type/__mail_alias/parameter/optional new file mode 100644 index 0000000..ff72b5c --- /dev/null +++ b/type/__mail_alias/parameter/optional @@ -0,0 +1 @@ +state diff --git a/type/__mail_alias/parameter/optional_multiple b/type/__mail_alias/parameter/optional_multiple new file mode 100644 index 0000000..d077ed8 --- /dev/null +++ b/type/__mail_alias/parameter/optional_multiple @@ -0,0 +1 @@ +alias From a5f3f3cdafe77f5aae075d1a0c053a9a36475463 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sat, 30 May 2020 18:27:13 +0200 Subject: [PATCH 06/54] [type/__dma_auth] Implement type --- type/__dma_auth/{manifest => explorer/logins} | 29 ++++++--- type/__dma_auth/gencode-remote | 62 +++++++++++++++++++ type/__dma_auth/man.rst | 27 +++++--- type/__dma_auth/parameter/default/state | 1 + type/__dma_auth/parameter/optional | 2 + type/__dma_auth/parameter/required | 2 + 6 files changed, 106 insertions(+), 17 deletions(-) rename type/__dma_auth/{manifest => explorer/logins} (55%) mode change 100755 => 100644 create mode 100644 type/__dma_auth/parameter/default/state create mode 100644 type/__dma_auth/parameter/optional create mode 100644 type/__dma_auth/parameter/required diff --git a/type/__dma_auth/manifest b/type/__dma_auth/explorer/logins old mode 100755 new mode 100644 similarity index 55% rename from type/__dma_auth/manifest rename to type/__dma_auth/explorer/logins index e0c809a..0ed6bc1 --- a/type/__dma_auth/manifest +++ b/type/__dma_auth/explorer/logins @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -17,14 +17,25 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # +# This explorer lines matching user + server in /etc/dma/auth.conf and reports +# their cksum. +test -r /etc/dma/auth.conf || exit 0 -os=$(cat "$__global/explorer/os") +if test -f "${__object}/parameter/login" +then + login=$(cat "${__object}/parameter/login") +else + login=$__object_id +fi +server=$(cat "${__object}/parameter/server") -case "$os" in - *) - printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2 - printf "Please contribute an implementation for it if you can.\n" >&2 - exit 1 - ;; -esac +regex=$(printf '^%s|%s:' "$login" "$server") + +grep -e "${regex}" /etc/dma/auth.conf \ +| while read -r line + do + echo "${line}" \ + | cksum - \ + | cut -d ' ' -f 1 + done diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index 77ad9d2..0951c16 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -18,3 +18,65 @@ # along with cdist. If not, see . # +logins=$(cat "${__object}/explorer/logins") +state_should=$(cat "${__object}/parameter/state") + +if test -f "${__object}/parameter/login" +then + login=$(cat "${__object}/parameter/login") +else + login=$__object_id +fi +password=$(cat "${__object}/parameter/password") +server=$(cat "${__object}/parameter/server") + +case $state_should +in + (present) + conf_line=$(printf '%s|%s:%s\n' "${login}" "${server}" "${password}") + cksum_should=$(echo "${conf_line}" | cksum - | cut -d ' ' -f 1) + if echo "$logins" | grep -qxF "${cksum_should}" + then + # correct line already present -> nothing to do + exit 0 + fi + + mode=1 + ;; + (absent) + if test -z "$logins" + then + # no logins present -> nothing to do + exit 0 + fi + + # NOTE: password is not needed to delete + conf_line=$(printf '%s|%s:%s\n' "${login}" "${server}" "") + + mode=0 + ;; + (*) + printf 'Invalid --state: %s' "${state_should}" >&2 + exit 1 + ;; +esac + +cat </etc/dma/auth.conf.tmp \ +&& mv /etc/dma/auth.conf.tmp /etc/dma/auth.conf +EOF diff --git a/type/__dma_auth/man.rst b/type/__dma_auth/man.rst index cd4f647..bd077d6 100644 --- a/type/__dma_auth/man.rst +++ b/type/__dma_auth/man.rst @@ -3,23 +3,29 @@ cdist-type__dma_auth(7) NAME ---- -cdist-type__dma_auth - TODO +cdist-type__dma_auth - Configure SMTP logins for the DragonFly Mail Agent MTA. DESCRIPTION ----------- -This space intentionally left blank. +This cdist type allows you to set up credentials to log in to remote SMTP +servers. REQUIRED PARAMETERS ------------------- -None. +password + The user's password (in plain text.) +server + The SMTP server on which the login is valid. OPTIONAL PARAMETERS ------------------- -None. - +login + The user's LOGIN name on the SMTP server. Defaults to `__object_id`. +state + Either `present` or `absent`. Defaults to `present`. BOOLEAN PARAMETERS ------------------ @@ -31,13 +37,18 @@ EXAMPLES .. code-block:: sh - # TODO - __dma_auth + # Set the password for smarthost + __dma_auth joe --server smarthost --password hunter2 + # Set credentials for user at an external provider + __dma_auth paul@example.com --server mail.provider.com --password letmein + + # Delete credentials for example.com + __dma_auth paul --server example.com --state absent SEE ALSO -------- -:strong:`TODO`\ (7) +:strong:`cdist-type__dma`\ (7), :strong:`dma`\ (8) AUTHORS diff --git a/type/__dma_auth/parameter/default/state b/type/__dma_auth/parameter/default/state new file mode 100644 index 0000000..e7f6134 --- /dev/null +++ b/type/__dma_auth/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/type/__dma_auth/parameter/optional b/type/__dma_auth/parameter/optional new file mode 100644 index 0000000..c35dbef --- /dev/null +++ b/type/__dma_auth/parameter/optional @@ -0,0 +1,2 @@ +login +state diff --git a/type/__dma_auth/parameter/required b/type/__dma_auth/parameter/required new file mode 100644 index 0000000..8f1a1c9 --- /dev/null +++ b/type/__dma_auth/parameter/required @@ -0,0 +1,2 @@ +password +server From 988f277ad63c8c3b465b5e31571e470009c6ee9a Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sat, 30 May 2020 22:07:20 +0200 Subject: [PATCH 07/54] [type/__mail_alias] Fixes (mostly mawk compatibility) --- type/__mail_alias/explorer/aliases | 12 +++++------- type/__mail_alias/explorer/aliases_file | 0 type/__mail_alias/gencode-remote | 11 ++++++----- 3 files changed, 11 insertions(+), 12 deletions(-) mode change 100644 => 100755 type/__mail_alias/explorer/aliases_file diff --git a/type/__mail_alias/explorer/aliases b/type/__mail_alias/explorer/aliases index ce1a439..66940d5 100755 --- a/type/__mail_alias/explorer/aliases +++ b/type/__mail_alias/explorer/aliases @@ -22,13 +22,11 @@ aliases_file=$("${__type_explorer}/aliases_file") test -r "${aliases_file}" || exit 0 -name=$__object_id - -awk -F ':[[:blank:]]*' ' -function print_aliases (aliases, matches) { - split(aliases, matches, /,[[:blank:]]*/) +awk -F ':[ \t]*' ' +function print_aliases(aliases, matches) { + split(aliases, matches, /,[ \t]*/) for (i in matches) { - gsub(/^[[:blank:]]*|[[:blank:]]*$/, "", matches[i]) + gsub(/^[ \t]*|[ \t]*$/, "", matches[i]) print matches[i] } } @@ -40,7 +38,7 @@ function print_aliases (aliases, matches) { { cont = ($0 ~ /\\$/) - if (cont) sub(/[[:blank:]]*\\$/, "", $0) + if (cont) sub(/[ \t]*\\$/, "", $0) } /^[[:blank:]]/ || cont { diff --git a/type/__mail_alias/explorer/aliases_file b/type/__mail_alias/explorer/aliases_file old mode 100644 new mode 100755 diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index a93dff2..7778536 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -23,7 +23,7 @@ state_should=$(cat "${__object}/parameter/state") case $state_should in (present) - if cmp "${__object}/explorer/aliases" "${__object}/parameter/alias" + if cmp -s "${__object}/explorer/aliases" "${__object}/parameter/alias" then # all good! exit 0 @@ -57,7 +57,8 @@ printf 'mode=%u\n' "${mode}" printf "aliases_file='%s'\n" "${aliases_file}" cat <<'EOF' -awk -F ':[[:blank:]]*' -v mode="${mode}" ' +test -f "${aliases_file}" || touch "${aliases_file}" +awk -F ':[ \t]*' -v mode="${mode}" ' function sepafter(f, default, _) { _ = substr($0, length($f) + 1, index(substr($0, length($f)+1), $(f+1)) - 1) if (_) return _ @@ -90,10 +91,10 @@ BEGIN { { cont = ($0 ~ /\\$/) - if (cont) sub(/[[:blank:]]*\\$/, "", $0) + if (cont) sub(/[ \t]*\\$/, "", $0) } -/^[[:blank:]]/ || cont { +/^[ \t]/ || cont { # continuation line if (select) next } @@ -118,7 +119,7 @@ END { exit 1 } -if ! cmp "${aliases_file}" "${aliases_file}.tmp" +if ! cmp -s "${aliases_file}" "${aliases_file}.tmp" then mv "${aliases_file}.tmp" "${aliases_file}" newaliases From 59059a200a27cd750794439544e3aa5a45b65f09 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 31 May 2020 11:57:54 +0200 Subject: [PATCH 08/54] [type/__dma_auth] Use host as key --- .../__dma_auth/explorer/{logins => auth_conf} | 48 +++++++++-------- type/__dma_auth/explorer/authusers | 51 +++++++++++++++++++ type/__dma_auth/gencode-remote | 26 +++++----- type/__dma_auth/man.rst | 16 +++--- type/__dma_auth/parameter/optional | 2 +- type/__dma_auth/parameter/required | 2 +- 6 files changed, 102 insertions(+), 43 deletions(-) rename type/__dma_auth/explorer/{logins => auth_conf} (54%) mode change 100644 => 100755 create mode 100755 type/__dma_auth/explorer/authusers diff --git a/type/__dma_auth/explorer/logins b/type/__dma_auth/explorer/auth_conf old mode 100644 new mode 100755 similarity index 54% rename from type/__dma_auth/explorer/logins rename to type/__dma_auth/explorer/auth_conf index 0ed6bc1..cef0aca --- a/type/__dma_auth/explorer/logins +++ b/type/__dma_auth/explorer/auth_conf @@ -17,25 +17,33 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# This explorer lines matching user + server in /etc/dma/auth.conf and reports -# their cksum. +# This explorer determines the path of dma's auth.conf file -test -r /etc/dma/auth.conf || exit 0 +# No dma.conf -> use default +test -f /etc/dma/dma.conf || { + echo /etc/dma/auth.conf + exit 0 +} +test -r /etc/dma/dma.conf || { + echo 'Cannot read /etc/dma/dma.conf' >&2 + exit 1 +} -if test -f "${__object}/parameter/login" -then - login=$(cat "${__object}/parameter/login") -else - login=$__object_id -fi -server=$(cat "${__object}/parameter/server") - -regex=$(printf '^%s|%s:' "$login" "$server") - -grep -e "${regex}" /etc/dma/auth.conf \ -| while read -r line - do - echo "${line}" \ - | cksum - \ - | cut -d ' ' -f 1 - done +# Get AUTHPATH from dma.conf +awk -F'[ \t]' ' +{ + sub(/#.*$/, "", $0) # remove comments + if (!$0) next # ignore empty lines +} +$1 == "AUTHPATH" { + # Store authpath. In dma conf parsing last wins. + if ($2) authpath = substr($0, index($0, " ") + 1) +} +END { + if (authpath) { + print authpath + exit 0 + } else exit 1 +} +' /etc/dma/dma.conf \ +|| echo /etc/dma/auth.conf # default diff --git a/type/__dma_auth/explorer/authusers b/type/__dma_auth/explorer/authusers new file mode 100755 index 0000000..5fc6b4e --- /dev/null +++ b/type/__dma_auth/explorer/authusers @@ -0,0 +1,51 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# This explorer looks for lines matching the server parameter in dma's auth.conf +# and reports the login and server fields (password is stripped off) + +auth_conf=$("${__type_explorer}/auth_conf") +test -r "${auth_conf}" || exit 0 + +if test -f "${__object}/parameter/server" +then + server=$(cat "${__object}/parameter/server") +else + server=$__object_id +fi + +awk -F'\n' -v server="${server}" ' +BEGIN { + DP = "[: \t]" # copied from dma/conf.c +} + +# skip comments and empty lines +/^#/ || /^$/ { next } + +{ + login = substr($0, 1, index($0, "|") - 1) + host = substr($0, length(login) + 2) + if (match(host, DP)) { + host = substr(host, 1, RSTART - 1) + endpos = length(login) + RSTART + } else endpos = length +} + +host == server { print substr($0, 0, endpos) } +' "${auth_conf}" diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index 0951c16..989e176 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -18,24 +18,24 @@ # along with cdist. If not, see . # -logins=$(cat "${__object}/explorer/logins") +authusers=$(cat "${__object}/explorer/authusers") state_should=$(cat "${__object}/parameter/state") -if test -f "${__object}/parameter/login" +if test -f "${__object}/parameter/server" then - login=$(cat "${__object}/parameter/login") + server=$(cat "${__object}/parameter/server") else - login=$__object_id + server=$__object_id fi +login=$(cat "${__object}/parameter/login") password=$(cat "${__object}/parameter/password") -server=$(cat "${__object}/parameter/server") case $state_should in (present) conf_line=$(printf '%s|%s:%s\n' "${login}" "${server}" "${password}") cksum_should=$(echo "${conf_line}" | cksum - | cut -d ' ' -f 1) - if echo "$logins" | grep -qxF "${cksum_should}" + if echo "$authusers" | grep -qxF "${cksum_should}" then # correct line already present -> nothing to do exit 0 @@ -44,11 +44,8 @@ in mode=1 ;; (absent) - if test -z "$logins" - then - # no logins present -> nothing to do - exit 0 - fi + # no logins present -> nothing to do + test -n "$authusers" || exit 0 # NOTE: password is not needed to delete conf_line=$(printf '%s|%s:%s\n' "${login}" "${server}" "") @@ -66,11 +63,14 @@ read -r CONF_LINE <<'EOL' ${conf_line} EOL export CONF_LINE +export mode=${mode} +EOF -awk -F: -v print=$mode ' +cat <<'EOF' +awk -F: -v mode=$mode ' BEGIN { split(ENVIRON["CONF_LINE"], conf, ":") } $1 == conf[1] { - if (print && !found) { + if (mode && !found) { # remove duplicates print ENVIRON["CONF_LINE"] found = 1 diff --git a/type/__dma_auth/man.rst b/type/__dma_auth/man.rst index bd077d6..9c3ad7a 100644 --- a/type/__dma_auth/man.rst +++ b/type/__dma_auth/man.rst @@ -14,16 +14,16 @@ servers. REQUIRED PARAMETERS ------------------- +login + The user's LOGIN name on the SMTP server. password The user's password (in plain text.) -server - The SMTP server on which the login is valid. OPTIONAL PARAMETERS ------------------- -login - The user's LOGIN name on the SMTP server. Defaults to `__object_id`. +server + The SMTP server on which the login is valid. Defaults to `__object_id`. state Either `present` or `absent`. Defaults to `present`. @@ -38,13 +38,13 @@ EXAMPLES .. code-block:: sh # Set the password for smarthost - __dma_auth joe --server smarthost --password hunter2 + __dma_auth smarthost.example.com --login joe --password hunter2 # Set credentials for user at an external provider - __dma_auth paul@example.com --server mail.provider.com --password letmein + __dma_auth mail.provider.com --login paul@example.com --password letmein - # Delete credentials for example.com - __dma_auth paul --server example.com --state absent + # Delete credentials for example.com (for all users) + __dma_auth example.com --login '' --password '' --state absent SEE ALSO -------- diff --git a/type/__dma_auth/parameter/optional b/type/__dma_auth/parameter/optional index c35dbef..3e42ed3 100644 --- a/type/__dma_auth/parameter/optional +++ b/type/__dma_auth/parameter/optional @@ -1,2 +1,2 @@ -login +server state diff --git a/type/__dma_auth/parameter/required b/type/__dma_auth/parameter/required index 8f1a1c9..ae3c622 100644 --- a/type/__dma_auth/parameter/required +++ b/type/__dma_auth/parameter/required @@ -1,2 +1,2 @@ +login password -server From b848fca9299befe36b78624cf622d6df286db053 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 31 May 2020 15:01:40 +0200 Subject: [PATCH 09/54] [type/__dma_auth] Finish code to rewrite auth.conf --- type/__dma_auth/explorer/authusers | 12 +++- type/__dma_auth/gencode-remote | 90 +++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/type/__dma_auth/explorer/authusers b/type/__dma_auth/explorer/authusers index 5fc6b4e..db83482 100755 --- a/type/__dma_auth/explorer/authusers +++ b/type/__dma_auth/explorer/authusers @@ -18,7 +18,7 @@ # along with cdist. If not, see . # # This explorer looks for lines matching the server parameter in dma's auth.conf -# and reports the login and server fields (password is stripped off) +# and reports the login and server fields (password is cksummed) auth_conf=$("${__type_explorer}/auth_conf") test -r "${auth_conf}" || exit 0 @@ -47,5 +47,11 @@ BEGIN { } else endpos = length } -host == server { print substr($0, 0, endpos) } -' "${auth_conf}" +host == server { print endpos, $0 } +' "${auth_conf}" \ +| while read pos line + do + printf '%s:%s\n' \ + "$(printf '%s' "$line" | cut -c $((-pos)))" \ + "$(printf '%s' "$line" | cut -c $((pos+2))- | cksum | cut -d' ' -f1)" + done diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index 989e176..d75d611 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -18,7 +18,6 @@ # along with cdist. If not, see . # -authusers=$(cat "${__object}/explorer/authusers") state_should=$(cat "${__object}/parameter/state") if test -f "${__object}/parameter/server" @@ -28,27 +27,26 @@ else server=$__object_id fi login=$(cat "${__object}/parameter/login") -password=$(cat "${__object}/parameter/password") case $state_should in (present) - conf_line=$(printf '%s|%s:%s\n' "${login}" "${server}" "${password}") - cksum_should=$(echo "${conf_line}" | cksum - | cut -d ' ' -f 1) - if echo "$authusers" | grep -qxF "${cksum_should}" + line_should=$(printf '%s|%s:%s\n' \ + "${login}" "${server}" \ + "$(cksum "${__object}/parameter/password" | cut -d' ' -f1)") + if grep -qxF "${line_should}" "${__object}/explorer/authusers" then # correct line already present -> nothing to do exit 0 fi + test -n "${login}" || { echo '--login must be non-empty' >&2; exit 1 } + mode=1 ;; (absent) - # no logins present -> nothing to do - test -n "$authusers" || exit 0 - - # NOTE: password is not needed to delete - conf_line=$(printf '%s|%s:%s\n' "${login}" "${server}" "") + # no matching logins present -> nothing to do + test -s "${__object}/explorer/authusers" || exit 0 mode=0 ;; @@ -58,25 +56,65 @@ in ;; esac +auth_conf=$(cat "${__object}/explorer/auth_conf") + +if test -z "${auth_conf}" +then + echo 'Cannot determine path of dma auth.conf' >&2 + exit 1 +fi + cat </etc/dma/auth.conf.tmp \ -&& mv /etc/dma/auth.conf.tmp /etc/dma/auth.conf + +BEGIN { + DP = "[: \t]" # copied from dma/conf.c +} + +# skip comments and empty lines +/^#/ || /^$/ { print; next } + +{ + login = substr($0, 1, index($0, "|") - 1) + host = substr($0, length(login) + 2) + if (match(host, DP)) { + host = substr(host, 1, RSTART - 1) + endpos = length(login) + RSTART + } else endpos = length +} + +host == ENVIRON["server"] { + if (mode) { + if (login == ENVIRON["login"] && !written) { + printf "%s%s\n", substr($0, 1, endpos+1), getpw() + written = 1 + next + } + } else if (!ENVIRON["login"] || login == ENVIRON["login"]) next +} + +{ print } + +END { + if (mode && !written) { + printf "%s|%s:%s\n", ENVIRON["login"], ENVIRON["server"], getpw() + } +} +' <"${auth_conf}" >"${auth_conf}.tmp" \ + && mv "${auth_conf}.tmp" "${auth_conf}" EOF From 3f72ca134108163f3208b709f84037312d460e5c Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 31 May 2020 15:18:11 +0200 Subject: [PATCH 10/54] [type/__dma_auth] Send messages --- type/__dma/gencode-remote | 45 +++++++++++++++++++++------------- type/__dma_auth/gencode-remote | 11 ++++++++- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 2961c09..2e3a80d 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -1,20 +1,31 @@ #!/bin/sh -e -if [ -f "${__object}/parameter/send-test-email" ]; then - SEND_EMAIL="YES" +if test -f "${__object}/parameter/send-test-email" +then + modified=false + + if grep -q '^__mail_alias/root:' "${__messages_in}" + then + modified=true + elif grep -q '^__dma_auth/' "${__messages_in}" + then + modified=true + elif grep -q '^__dma/' "${__messages_in}" + then + modified=true + fi + + if $modified + then + cat <<-EOF + sendmail root <&2; exit 1 } mode=1 - ;; + + if test -s "${__object}/explorer/authusers" + then + printf 'set authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" + else + printf 'add authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" + fi + ;; (absent) # no matching logins present -> nothing to do test -s "${__object}/explorer/authusers" || exit 0 mode=0 + + printf 'delete authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" ;; (*) printf 'Invalid --state: %s' "${state_should}" >&2 From b87b67597efb8349709e8f5153bb73bb08f39337 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 31 May 2020 15:06:06 +0200 Subject: [PATCH 11/54] [type/__dma] Remove mail aliases functionality --- type/__dma/files/aliases | 68 -------------------------- type/__dma/man.rst | 22 ++------- type/__dma/manifest | 22 +-------- type/__dma/parameter/required_multiple | 1 - 4 files changed, 6 insertions(+), 107 deletions(-) delete mode 100644 type/__dma/files/aliases delete mode 100644 type/__dma/parameter/required_multiple diff --git a/type/__dma/files/aliases b/type/__dma/files/aliases deleted file mode 100644 index d341318..0000000 --- a/type/__dma/files/aliases +++ /dev/null @@ -1,68 +0,0 @@ -# Based off FreeBSD's /etc/aliases -# -# >>>>>>>>>> The program "newaliases" must be run after -# >> NOTE >> this file is updated for any changes to -# >>>>>>>>>> show through to sendmail. -# -# -# See also RFC 2142, `MAILBOX NAMES FOR COMMON SERVICES, ROLES -# AND FUNCTIONS', May 1997 -# http://tools.ietf.org/html/rfc2142 - -# Pretty much everything else in this file points to "root", so -# you would do well in either reading root's mailbox or forwarding -# root's email from here. - -# root: me@my.domain - - -# Basic system aliases -- these MUST be present -MAILER-DAEMON: postmaster -postmaster: root - -# General redirections for pseudo accounts -_dhcp: root -_pflogd: root -auditdistd: root -bin: root -bind: root -daemon: root -games: root -hast: root -kmem: root -mailnull: postmaster -man: root -news: root -nobody: root -operator: root -pop: root -proxy: root -smmsp: postmaster -sshd: root -system: root -toor: root -tty: root -usenet: news -uucp: root - -# Well-known aliases -- these should be filled in! -manager: root -dumper: root - -# BUSINESS-RELATED MAILBOX NAMES -info: root -marketing: root -sales: root -support: root - -# NETWORK OPERATIONS MAILBOX NAMES -abuse: root -noc: root -security: root - -# SUPPORT MAILBOX NAMES FOR SPECIFIC INTERNET SERVICES -ftp: root -ftp-bugs: ftp -hostmaster: root -webmaster: root -www: webmaster diff --git a/type/__dma/man.rst b/type/__dma/man.rst index a10c6c2..af9298e 100644 --- a/type/__dma/man.rst +++ b/type/__dma/man.rst @@ -19,18 +19,9 @@ email server configured in the `smart-host` parameter. REQUIRED PARAMETERS ------------------- smart-host - The destination email server. The addresses passed in `root-email` must be - either local to the `smart-host` or it must be configured to act as a relay - for the host being configured by this type. - - -REQUIRED MULTIPLE PARAMETERS ----------------------------- -root-email - Destination email address. Can be specified multiple times or just once - with each address separated by commas. - This will be setup in `/etc/aliases` as the destination for the local - root mailbox. + The email server used to send email. + It must be configured to act as a relay for the host being configured by + this type so that mail can be sent to users non-local to the smart-host. BOOLEAN PARAMETERS @@ -55,11 +46,7 @@ EXAMPLES .. code-block:: sh - # Send root email to both our BOFH and the nice-admin. - # That way they can figure things out together. __dma \ - --root-email bofh@domain.tld \ - --root-email nice-admin@domain.tld \ --smart-host mx1.domain.tld \ --send-test-email @@ -73,11 +60,12 @@ SEE ALSO AUTHORS ------- Evilham +Dennis Camera COPYING ------- -Copyright \(C) 2020 Evilham. You can redistribute it +Copyright \(C) 2020 Evilham and Dennis Camera. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. diff --git a/type/__dma/manifest b/type/__dma/manifest index abcaa5b..e07fbfc 100755 --- a/type/__dma/manifest +++ b/type/__dma/manifest @@ -1,8 +1,7 @@ #!/bin/sh -e -os="$(cat "${__global}/explorer/os")" +os=$(cat "${__global}/explorer/os") -root_email="$(tr '\n' ',' < "${__object}/parameter/root-email" | sed -E 's/,+$//')" smart_host="$(cat "${__object}/parameter/smart-host")" if [ -f "${__object}/parameter/mailname" ]; then @@ -21,14 +20,12 @@ else esac fi -aliases_file=/etc/mail/aliases case ${os} in debian|devuan|ubuntu) # Debian-like requires installing DMA __package dma # Moving forward without DMA doesn't make much sense export require="__package/dma" - aliases_file=/etc/aliases ;; freebsd) # Disable sendmail + stop if necessary @@ -134,20 +131,3 @@ EOF __file /etc/dma/dma.conf --mode 0644 --source '-' < Date: Mon, 1 Jun 2020 17:07:35 +0200 Subject: [PATCH 12/54] [type/__dma] Implement config file updates --- type/__dma/explorer/conf | 30 ++++ type/__dma/gencode-remote | 197 ++++++++++++++++++++++++- type/__dma/man.rst | 24 +++ type/__dma/manifest | 165 ++++++--------------- type/__dma/parameter/default/security | 1 + type/__dma/parameter/optional | 2 + type/__dma/parameter/optional_multiple | 1 + 7 files changed, 302 insertions(+), 118 deletions(-) create mode 100755 type/__dma/explorer/conf create mode 100644 type/__dma/parameter/default/security create mode 100644 type/__dma/parameter/optional_multiple diff --git a/type/__dma/explorer/conf b/type/__dma/explorer/conf new file mode 100755 index 0000000..129e3c3 --- /dev/null +++ b/type/__dma/explorer/conf @@ -0,0 +1,30 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# This explorer looks for lines matching the server parameter in dma's auth.conf +# and reports the login and server fields (password is cksummed) + +CONF_PATH=/etc/dma # set in Makefile +dma_conf="${CONF_PATH:?}/dma.conf" + +test -f "${dma_conf}" || exit 0 + +grep -v -e '^[ \t]*#\|^$' "${dma_conf}" \ +| sed -e 's/[ \t]*#.*$//' \ +| sort -s -k 1,1 diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 2e3a80d..1091aeb 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -1,5 +1,200 @@ #!/bin/sh -e +CONF_PATH=/etc/dma # set in Makefile + +# Generate config +conf_should=$( + if test -s "${__object}/parameter/smart-host" + then + printf 'SMARTHOST %s\n' "$(cat "${__object}/parameter/smart-host")" + #printf 'AUTHPATH %s\n' "$(cat "${__type}/ + fi + + case $(cat "${__object}/parameter/security") + in + (ssl|tls) + default_smtp_port=465 + echo 'SECURETRANSFER' + ;; + (starttls) + default_smtp_port=587 + echo 'SECURETRANSFER' + echo 'STARTTLS' + ;; + (opportunistic) + default_smtp_port=25 # XXX: correct? + echo 'SECURETRANSFER' + echo 'STARTTLS' + echo 'OPPORTUNISTIC_TLS' + ;; + (insecure) + default_smtp_port=25 + echo 'INSECURE' + ;; + esac + + if test -s "${__object}/parameter/port" + then + printf 'PORT %u\n' "$(cat "${__object}/parameter/port")" + elif test "${default_smtp_port}" -ne 25 + then + printf 'PORT %u\n' "${default_smtp_port}" + fi + + if test -f "${__object}/parameter/masquerade" + then + while read -r line + do + printf 'MASQUERADE %s\n' "${line}" + done <"${__object}/parameter/masquerade" + fi + + +) +conf_should=$(echo "$conf_should" | sort -s -k 1,1) + +config_updated=false +if ! echo "$conf_should" | cmp -s "${__object}/explorer/conf" - +then + # config needs to be updated + echo "dma_conf='${CONF_PATH:?}/dma.conf'" + cat <<'EOF' +awk -F '\n' ' +function comment_line(line) { return match(line, /^[ \t]*#+[ \t]*/) } +function empty_line(line) { return match(line, /^[ \t]*$/) } +function is_word(s) { return s ~ /^[A-Z_]+$/ } + +function first(line, sep) { + if (!sep) sep = SUBSEP + return index(line, sep) ? substr(line, 0, index(line, sep)) : line +} + +function rest(line, sep) { + if (!sep) sep = SUBSEP + if (index(line, sep)) + return substr(line, index(line, sep) + 1) +} + +function conf_pop(word, value) { + if (!(word in conf)) return 0 + if (!value) { + if (index(conf[word], SUBSEP)) # more than one element? + value = substr(conf[word], 0, index(conf[word], SUBSEP)) + else + value = conf[word] + } + + if (index(conf[word], SUBSEP)) { + if (index(conf[word], value SUBSEP) != 1) return 0 + conf[word] = substr(conf[word], length(value) + 2) + } else { + if (conf[word] != value) return 0 + delete conf[word] + } + return value +} + +function print_conf(word, value) { + printf "%s", word + if (value) printf " %s", value + printf "\n" +} + +function print_confs(word, value) { + if (!(word in conf)) return + if (conf[word]) { + while (value = conf_pop(word)) + print_conf(word, value) + } else { + print_conf(word) + delete conf[word] + } +} + +BEGIN { + while (getline < "/dev/stdin") { + word = first($0, " ") + if ((word in conf)) + conf[word] = conf[word] SUBSEP rest($0, " ") + else + conf[word] = rest($0, " ") + } +} + +# first pass, gather information +NR == FNR { + if (comment_line($0)) { + # comment line + word = first(substr($0, RLENGTH + 1), " ") + if (is_word(word)) last_occ["#" word] = FNR + } else { + word = first($0, " ") + if (is_word(word)) last_occ[word] = FNR + } +} + +NR > FNR && FNR == 1 { + # before second pass prepare hashes + + for (k in last_occ) + if (k ~ /^\#/ && (substr(k, 2) in last_occ)) + delete last_occ[k] + + for (k in last_occ) { + line_map[last_occ[k]] = k + } +} + +# second pass, output new config +NR > FNR { + if (comment_line($0) || empty_line($0)) { + # comment or empty line + print + + if ((FNR in line_map)) { + if (line_map[FNR] ~ /^\#/) { + # the "matching" comment line is here + k = substr(line_map[FNR], 2) + if ((k in conf)) print_confs(k) + } + + if (("INSECURE" in conf) && line_map[FNR] ~ /^\#?SECURE$/) { + # INSECURE goes where SECURE comment is + print_confs("INSECURE") + } + } + } else { + sub(/[ \t]*\#.*$/, "", $0) # ignore comments + word = first($0, " ") + + if ((word in conf) && rest($0, " ") == first(conf[word])) { + # keep config options we want + conf_pop(word) + print + } + + if ((FNR in line_map) && line_map[FNR] == word) { + # rest of config options should be here + print_confs(word) + } + } +} + +END { + # print rest of config options + for (word in conf) print_confs(word) +} +' "${dma_conf}" "${dma_conf}" <<'EOF' >"${dma_conf}.tmp" \ + && mv "${dma_conf}.tmp" "${dma_conf}" +EOF + echo "${conf_should}" + echo 'EOF' + + config_updated=true + echo 'config updated' >>"${__messages_out}" +fi + + if test -f "${__object}/parameter/send-test-email" then modified=false @@ -10,7 +205,7 @@ then elif grep -q '^__dma_auth/' "${__messages_in}" then modified=true - elif grep -q '^__dma/' "${__messages_in}" + elif $config_updated then modified=true fi diff --git a/type/__dma/man.rst b/type/__dma/man.rst index af9298e..cb3c35f 100644 --- a/type/__dma/man.rst +++ b/type/__dma/man.rst @@ -39,7 +39,31 @@ mailname If not defined, it defaults to `/etc/mailname` on Debian-derived Operating Systems and to `__target_host` otherwise. See `dma(8)` for more information. +masquerade + Masquerade the envelope-from addresses with this address/hostname. + Use this setting if mails are not accepted by destination mail servers + because your sender domain is invalid. + This option can be used multiple times. + For more information see the `dma(8)` man page. +port + The port on which to deliver email. + If not provided, a sensible default port will be used based on the + `--security` argument. +security + Configures whether and how DMA should use secure connections. + ssl/tls + Enable TLS/SSL secured transfer. + starttls + Use STARTTLS to establish a secure connection. + opportunistic (default) + Will try to establish a secure connection using STARTTLS, but allow + unencrypted transfer if STARTTLS fails. + Most useful when dma is used without a smarthost, delivering remote + messages directly to the outside mail exchangers. + insecure + allow plain text SMTP login over an insecure connection. + Should really not be used anymore! EXAMPLES -------- diff --git a/type/__dma/manifest b/type/__dma/manifest index e07fbfc..814e3ef 100755 --- a/type/__dma/manifest +++ b/type/__dma/manifest @@ -2,54 +2,57 @@ os=$(cat "${__global}/explorer/os") -smart_host="$(cat "${__object}/parameter/smart-host")" - -if [ -f "${__object}/parameter/mailname" ]; then - mailname="$(cat "${__object}/parameter/mailname")" +# mailname: default behaviour is different on certain systems +if test -f "${__object}/parameter/mailname" +then + mailname=$(cat "${__object}/parameter/mailname") else - # default mailname behaviour is different in certain systems - case ${os} in - debian|devuan|ubuntu) - # Debian-like default to /etc/mailname - mailname="/etc/mailname" - ;; - *) - # Otherwise let's use the hostname - mailname="${__target_host}" - ;; - esac + # Otherwise use the hostname + mailname=$(cat "${__global}/explorer/hostname") fi -case ${os} in - debian|devuan|ubuntu) - # Debian-like requires installing DMA - __package dma - # Moving forward without DMA doesn't make much sense - export require="__package/dma" - ;; - freebsd) - # Disable sendmail + stop if necessary - __key_value \ - --file "/etc/rc.conf" \ - --comment "# Disable sendmail " \ - --key "sendmail_enable" \ - --delimiter "=" \ - --value "NONE" \ - --onchange "service sendmail onestop || true" \ - "sendmail_enable" +case $os +in + (debian|devuan|ubuntu) + # On Debian-like systems use /etc/mailname + if test -f "${__object}/parameter/mailname" + then + echo "$mailname" | __file '/etc/mailname' --state present \ + --mode 0644 --owner root --group root --source - + fi + + mailname='/etc/mailname' + ;; +esac + +# Install DMA +case $os +in + (debian|devuan|ubuntu) + __package dma --state present + export require='__package/dma' + ;; + (freebsd) + # Stop sendmail if necessary + __process 'sendmail' --name 'sendmail.*' --state absent \ + --stop '/etc/rc.d/sendmail onestop' + + # ... and disable it + __key_value 'rcconf-sendmail-enable' --file '/etc/rc.conf' \ + --key 'sendmail_enable' --delimiter '=' --value '"NONE"' \ + --exact_delimiter + # Setup mailwrapper accordingly - __file /etc/mail/mailer.conf \ - --mode 0644 \ - --source '-' < /dev/stderr <&2 Your OS (${os}) is not supported yet. Maybe adding support is as simple as adapting the packages or allowing it, @@ -57,77 +60,5 @@ we highly encourage you to open a PR with the necessary changes. See: https://code.ungleich.ch/ungleich-public/cdist-contrib/ EOF exit 1 - ;; + ;; esac - -DMA_CONF="$(cat < Date: Mon, 1 Jun 2020 17:16:04 +0200 Subject: [PATCH 13/54] [type/__dma] Detect AUTHPATH --- type/__dma/explorer/auth_conf | 1 + type/__dma/gencode-remote | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 120000 type/__dma/explorer/auth_conf diff --git a/type/__dma/explorer/auth_conf b/type/__dma/explorer/auth_conf new file mode 120000 index 0000000..db038ae --- /dev/null +++ b/type/__dma/explorer/auth_conf @@ -0,0 +1 @@ +../../__dma_auth/explorer/auth_conf \ No newline at end of file diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 1091aeb..105edba 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -49,7 +49,10 @@ conf_should=$( done <"${__object}/parameter/masquerade" fi - + if test -s "${__object}/explorer/auth_conf" + then + printf "AUTHPATH %s\n" "$(cat "${__object}/explorer/auth_conf")" + fi ) conf_should=$(echo "$conf_should" | sort -s -k 1,1) From 4fdddfd738dac87a1e33e359e1ede6ee22a16918 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 1 Jun 2020 17:23:31 +0200 Subject: [PATCH 14/54] [type/__dma] Add --defer, --full-bounce, and --null-client --- type/__dma/gencode-remote | 20 ++++++++++++++++++++ type/__dma/man.rst | 8 ++++++++ type/__dma/parameter/boolean | 3 +++ 3 files changed, 31 insertions(+) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 105edba..1f252a4 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -49,6 +49,26 @@ conf_should=$( done <"${__object}/parameter/masquerade" fi + if test -f "${__object}/parameter/defer" + then + echo 'DEFER' + fi + + if test -f "${__object}/parameter/full-bounce" + then + echo 'FULLBOUNCE' + fi + + if test -f "${__object}/parameter/null-client" + then + test -s "${__object}/parameter/smart-host" || { + echo '--null-client requires a --smart-host to be defined' >&2 + exit 1 + } + + echo 'NULLCLIENT' + fi + if test -s "${__object}/explorer/auth_conf" then printf "AUTHPATH %s\n" "$(cat "${__object}/explorer/auth_conf")" diff --git a/type/__dma/man.rst b/type/__dma/man.rst index cb3c35f..a8ba546 100644 --- a/type/__dma/man.rst +++ b/type/__dma/man.rst @@ -26,6 +26,14 @@ smart-host BOOLEAN PARAMETERS ------------------ +defer + If enabled, the mail queue has to be manually flushed with the `-q` option. +full-bounce + Enable if the bounce message should include the complete original message, + not just the headers. +null-client + Enable to bypass aliases and local delivery, and instead forward all mails + to the defined `--smart-host`. send-test-email If present, after setup this type will send an email to root, to allow you to easily test your setup. diff --git a/type/__dma/parameter/boolean b/type/__dma/parameter/boolean index d1af563..ede7dda 100644 --- a/type/__dma/parameter/boolean +++ b/type/__dma/parameter/boolean @@ -1 +1,4 @@ +defer +full-bounce +null-client send-test-email From 99d58672c4987757429fc783b66d96eb6fa90f2b Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 1 Jun 2020 19:20:05 +0200 Subject: [PATCH 15/54] [type/__dma_auth] Add semicolon --- type/__dma_auth/gencode-remote | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index 48e7dce..b6b7f63 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -40,7 +40,7 @@ in exit 0 fi - test -n "${login}" || { echo '--login must be non-empty' >&2; exit 1 } + test -n "${login}" || { echo '--login must be non-empty' >&2; exit 1; } mode=1 From 0657ac4f115ee575f0bb7780fa9ac759c472ec97 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 1 Jun 2020 19:21:41 +0200 Subject: [PATCH 16/54] [type/__dma] Fix mailname --- type/__dma/gencode-remote | 31 +++++++++++++++++++++++++------ type/__dma/manifest | 27 +-------------------------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 1f252a4..4100d39 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -2,12 +2,36 @@ CONF_PATH=/etc/dma # set in Makefile +# Determine mailname +if test -f "${__object}/parameter/mailname" +then + mailname=$(cat "${__object}/parameter/mailname") +else + case $(cat "${__global}/explorer/os") + in + (debian|devuan|ubuntu) + # On Debian-like systems use /etc/mailname unless --mailname is used + mailname='/etc/mailname' + ;; + (*) + mailname=$__target_fqdn + ;; + esac +fi + + # Generate config conf_should=$( if test -s "${__object}/parameter/smart-host" then printf 'SMARTHOST %s\n' "$(cat "${__object}/parameter/smart-host")" - #printf 'AUTHPATH %s\n' "$(cat "${__type}/ + fi + + printf 'MAILNAME %s\n' "${mailname}" + + if test -s "${__object}/explorer/auth_conf" + then + printf "AUTHPATH %s\n" "$(cat "${__object}/explorer/auth_conf")" fi case $(cat "${__object}/parameter/security") @@ -68,11 +92,6 @@ conf_should=$( echo 'NULLCLIENT' fi - - if test -s "${__object}/explorer/auth_conf" - then - printf "AUTHPATH %s\n" "$(cat "${__object}/explorer/auth_conf")" - fi ) conf_should=$(echo "$conf_should" | sort -s -k 1,1) diff --git a/type/__dma/manifest b/type/__dma/manifest index 814e3ef..60038b1 100755 --- a/type/__dma/manifest +++ b/type/__dma/manifest @@ -1,32 +1,7 @@ #!/bin/sh -e -os=$(cat "${__global}/explorer/os") - -# mailname: default behaviour is different on certain systems -if test -f "${__object}/parameter/mailname" -then - mailname=$(cat "${__object}/parameter/mailname") -else - # Otherwise use the hostname - mailname=$(cat "${__global}/explorer/hostname") -fi - -case $os -in - (debian|devuan|ubuntu) - # On Debian-like systems use /etc/mailname - if test -f "${__object}/parameter/mailname" - then - echo "$mailname" | __file '/etc/mailname' --state present \ - --mode 0644 --owner root --group root --source - - fi - - mailname='/etc/mailname' - ;; -esac - # Install DMA -case $os +case $(cat "${__global}/explorer/os") in (debian|devuan|ubuntu) __package dma --state present From 7183bb3cd191dfece36e32db86eb6ee42b49bb5d Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 1 Jun 2020 19:24:32 +0200 Subject: [PATCH 17/54] [type/__dma] Fixes for FreeBSD --- type/__dma/gencode-remote | 13 ++++++++----- type/__dma/manifest | 3 +++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 4100d39..1b00f04 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -108,7 +108,7 @@ function is_word(s) { return s ~ /^[A-Z_]+$/ } function first(line, sep) { if (!sep) sep = SUBSEP - return index(line, sep) ? substr(line, 0, index(line, sep)) : line + return index(line, sep) ? substr(line, 1, index(line, sep) - 1) : line } function rest(line, sep) { @@ -121,7 +121,7 @@ function conf_pop(word, value) { if (!(word in conf)) return 0 if (!value) { if (index(conf[word], SUBSEP)) # more than one element? - value = substr(conf[word], 0, index(conf[word], SUBSEP)) + value = substr(conf[word], 1, index(conf[word], SUBSEP) - 1) else value = conf[word] } @@ -256,10 +256,13 @@ then then cat <<-EOF sendmail root < Date: Mon, 1 Jun 2020 20:25:10 +0200 Subject: [PATCH 18/54] [type/__dma_auth] Fix SC2162 --- type/__dma_auth/explorer/authusers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/type/__dma_auth/explorer/authusers b/type/__dma_auth/explorer/authusers index db83482..c76667b 100755 --- a/type/__dma_auth/explorer/authusers +++ b/type/__dma_auth/explorer/authusers @@ -49,7 +49,7 @@ BEGIN { host == server { print endpos, $0 } ' "${auth_conf}" \ -| while read pos line +| while read -r pos line do printf '%s:%s\n' \ "$(printf '%s' "$line" | cut -c $((-pos)))" \ From bf822f3f8ca53852554750003296b9163122c424 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 1 Jun 2020 20:26:52 +0200 Subject: [PATCH 19/54] [type/__dma] Fix SC2154 --- type/__dma/manifest | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/type/__dma/manifest b/type/__dma/manifest index 4a78dab..7abd7c8 100755 --- a/type/__dma/manifest +++ b/type/__dma/manifest @@ -1,7 +1,9 @@ #!/bin/sh -e +os=$(cat "${__global}/explorer/os") + # Install DMA -case $(cat "${__global}/explorer/os") +case $os in (debian|devuan|ubuntu) __package dma --state present From de4508cb0695cf0f7a6561ac7d8b92add1bc313d Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sat, 6 Jun 2020 21:45:40 +0200 Subject: [PATCH 20/54] Mark __dma_auth and __mail_alias as nonparallel Both types modify a single file, so they shouldn't be run at the same time. --- type/__dma_auth/nonparallel | 0 type/__mail_alias/nonparallel | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 type/__dma_auth/nonparallel create mode 100644 type/__mail_alias/nonparallel diff --git a/type/__dma_auth/nonparallel b/type/__dma_auth/nonparallel new file mode 100644 index 0000000..e69de29 diff --git a/type/__mail_alias/nonparallel b/type/__mail_alias/nonparallel new file mode 100644 index 0000000..e69de29 From 0cd19b3a5dc71fa3fed263fe14eb05aafa58b0aa Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Tue, 9 Jun 2020 14:44:54 +0200 Subject: [PATCH 21/54] [type/__dma] Use "smarthost" spelling to be consistent with DMA --- type/__dma/gencode-remote | 8 ++++---- type/__dma/man.rst | 14 ++++++-------- type/__dma/parameter/required | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 1b00f04..e2bb405 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -22,9 +22,9 @@ fi # Generate config conf_should=$( - if test -s "${__object}/parameter/smart-host" + if test -s "${__object}/parameter/smarthost" then - printf 'SMARTHOST %s\n' "$(cat "${__object}/parameter/smart-host")" + printf 'SMARTHOST %s\n' "$(cat "${__object}/parameter/smarthost")" fi printf 'MAILNAME %s\n' "${mailname}" @@ -85,8 +85,8 @@ conf_should=$( if test -f "${__object}/parameter/null-client" then - test -s "${__object}/parameter/smart-host" || { - echo '--null-client requires a --smart-host to be defined' >&2 + test -s "${__object}/parameter/smarthost" || { + echo '--null-client requires a --smarthost to be defined' >&2 exit 1 } diff --git a/type/__dma/man.rst b/type/__dma/man.rst index a8ba546..cbc1c0c 100644 --- a/type/__dma/man.rst +++ b/type/__dma/man.rst @@ -13,15 +13,15 @@ mails from locally installed Mail User Agents (MUA) and deliver the mails to a remote destination. Remote delivery happens over TLS to one or more mailboxes that are local to the -email server configured in the `smart-host` parameter. +mail server configured in the ``smarthost`` parameter. REQUIRED PARAMETERS ------------------- -smart-host - The email server used to send email. +smarthost + The mail server used to send email. It must be configured to act as a relay for the host being configured by - this type so that mail can be sent to users non-local to the smart-host. + this type so that mail can be sent to users non-local to the smarthost. BOOLEAN PARAMETERS @@ -33,7 +33,7 @@ full-bounce not just the headers. null-client Enable to bypass aliases and local delivery, and instead forward all mails - to the defined `--smart-host`. + to the defined ``--smarthost``. send-test-email If present, after setup this type will send an email to root, to allow you to easily test your setup. @@ -78,9 +78,7 @@ EXAMPLES .. code-block:: sh - __dma \ - --smart-host mx1.domain.tld \ - --send-test-email + __dma --smarthost mx1.domain.tld --send-test-email SEE ALSO diff --git a/type/__dma/parameter/required b/type/__dma/parameter/required index 262568f..0753fb6 100644 --- a/type/__dma/parameter/required +++ b/type/__dma/parameter/required @@ -1 +1 @@ -smart-host +smarthost From 45b10f3e098f3e06dccdc2483c36e56527e0b9a1 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Tue, 9 Jun 2020 14:51:11 +0200 Subject: [PATCH 22/54] [type/__dma] Update parameters to match config names in DMA --- type/__dma/gencode-remote | 6 +++--- type/__dma/parameter/boolean | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index e2bb405..e4760d8 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -78,15 +78,15 @@ conf_should=$( echo 'DEFER' fi - if test -f "${__object}/parameter/full-bounce" + if test -f "${__object}/parameter/fullbounce" then echo 'FULLBOUNCE' fi - if test -f "${__object}/parameter/null-client" + if test -f "${__object}/parameter/nullclient" then test -s "${__object}/parameter/smarthost" || { - echo '--null-client requires a --smarthost to be defined' >&2 + echo '--nullclient requires a --smarthost to be defined' >&2 exit 1 } diff --git a/type/__dma/parameter/boolean b/type/__dma/parameter/boolean index ede7dda..523bb97 100644 --- a/type/__dma/parameter/boolean +++ b/type/__dma/parameter/boolean @@ -1,4 +1,4 @@ defer -full-bounce -null-client -send-test-email +fullbounce +nullclient +send-test-mail From 67b989a717d1c2c817b99220712bab7e78a679e3 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Tue, 9 Jun 2020 20:53:01 +0200 Subject: [PATCH 23/54] [type/__dma_auth] Simplify code and add more comments --- type/__dma_auth/explorer/{authusers => state} | 51 ++++++++--- type/__dma_auth/gencode-remote | 84 ++++++++++++------- 2 files changed, 91 insertions(+), 44 deletions(-) rename type/__dma_auth/explorer/{authusers => state} (65%) diff --git a/type/__dma_auth/explorer/authusers b/type/__dma_auth/explorer/state similarity index 65% rename from type/__dma_auth/explorer/authusers rename to type/__dma_auth/explorer/state index c76667b..18e13ce 100755 --- a/type/__dma_auth/explorer/authusers +++ b/type/__dma_auth/explorer/state @@ -31,27 +31,54 @@ else fi awk -F'\n' -v server="${server}" ' +function getvalue(path) { + getline < path + close(path) + return $0 +} + BEGIN { DP = "[: \t]" # copied from dma/conf.c + + parameter_dir = ENVIRON["__object"] "/parameter/" + + host_param = getvalue(parameter_dir "server") + if (!host_param) host_param = ENVIRON["__object_id"] + login_param = getvalue(parameter_dir "login") + passwd_param = getvalue(parameter_dir "password") + + state = "absent" } -# skip comments and empty lines -/^#/ || /^$/ { next } +/^#/ || /^$/ { + # skip comments and empty lines + next +} { + # parse line + login = substr($0, 1, index($0, "|") - 1) + if (!login) { login = $0 } # if no "|" found + host = substr($0, length(login) + 2) + if (match(host, DP)) { + passwd = substr(host, RSTART) host = substr(host, 1, RSTART - 1) - endpos = length(login) + RSTART - } else endpos = length + } else { + passwd = "" + } } -host == server { print endpos, $0 } -' "${auth_conf}" \ -| while read -r pos line - do - printf '%s:%s\n' \ - "$(printf '%s' "$line" | cut -c $((-pos)))" \ - "$(printf '%s' "$line" | cut -c $((pos+2))- | cksum | cut -d' ' -f1)" - done +host == host_param && login == login_param { + if (passwd == passwd_param) + state = "present" + else + state = "different_password" +} + +END { + print state +} +' "${auth_conf}" diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index b6b7f63..262a17a 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -18,6 +18,7 @@ # along with cdist. If not, see . # +state_is=$(cat "${__object}/explorer/state") state_should=$(cat "${__object}/parameter/state") if test -f "${__object}/parameter/server" @@ -28,33 +29,27 @@ else fi login=$(cat "${__object}/parameter/login") +if test "${state_is}" = "${state_should}" +then + # state is as it should + exit 0 +fi + case $state_should in (present) - line_should=$(printf '%s|%s:%s\n' \ - "${login}" "${server}" \ - "$(cksum "${__object}/parameter/password" | cut -d' ' -f1)") - if grep -qxF "${line_should}" "${__object}/explorer/authusers" - then - # correct line already present -> nothing to do - exit 0 - fi - test -n "${login}" || { echo '--login must be non-empty' >&2; exit 1; } mode=1 - if test -s "${__object}/explorer/authusers" + if test "${state_is}" = 'absent' then - printf 'set authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" - else printf 'add authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" + else + printf 'set authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" fi ;; (absent) - # no matching logins present -> nothing to do - test -s "${__object}/explorer/authusers" || exit 0 - mode=0 printf 'delete authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" @@ -67,16 +62,14 @@ esac auth_conf=$(cat "${__object}/explorer/auth_conf") -if test -z "${auth_conf}" -then +test -n "${auth_conf}" || { echo 'Cannot determine path of dma auth.conf' >&2 exit 1 -fi +} + cat < drop all lines for this host + next + } + } } +# leave other lines alone { print } END { if (mode && !written) { - printf "%s|%s:%s\n", ENVIRON["login"], ENVIRON["server"], getpw() + # append line if no match to replace was found + print_should() } } ' <"${auth_conf}" >"${auth_conf}.tmp" \ From 96fcccf5294e1ed381096b731207674807ce6222 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Tue, 9 Jun 2020 21:39:28 +0200 Subject: [PATCH 24/54] [type/__mail_alias] Improve documentation --- type/__mail_alias/explorer/aliases | 43 ++++++++++++------ type/__mail_alias/explorer/aliases_file | 2 +- type/__mail_alias/gencode-remote | 59 ++++++++++++++++--------- 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/type/__mail_alias/explorer/aliases b/type/__mail_alias/explorer/aliases index 66940d5..0a8da94 100755 --- a/type/__mail_alias/explorer/aliases +++ b/type/__mail_alias/explorer/aliases @@ -32,26 +32,41 @@ function print_aliases(aliases, matches) { } /^#/ { - # comment - select = 0; cont = 0; next -} - -{ - cont = ($0 ~ /\\$/) - if (cont) sub(/[ \t]*\\$/, "", $0) -} - -/^[[:blank:]]/ || cont { - # continuation line - if (select) print_aliases($0) + # comment line (ignore) + select = 0; cont = 0 # comments terminate alias lists and continuations next } -$1 == ENVIRON["__object_id"] { +/^[ \t]/ || cont { + # continuation line (either the previous line ended in a backslash or the + # line starts with whitespace) + + if (select) + print_aliases($0) +} + +{ + # detect if the line is a line to be continued (ends with a backslash) + cont = ($0 ~ /\\$/) + + # if it is, we drop the backslash from the line and skip to next line + # (the contents have been printed above if they should) + if (cont) { + sub(/[ \t]*\\$/, "", $0) + next + } +} + +$1 == ENVIRON["__object_id"] && !select { + # "target" user -> print alias list + # (only if !select; because of whitespacecontinuation lines) select = 1 print_aliases($2) next } -{ select = 0 } +{ + # other user + select = 0 +} ' "${aliases_file}" diff --git a/type/__mail_alias/explorer/aliases_file b/type/__mail_alias/explorer/aliases_file index f7c4596..2710792 100755 --- a/type/__mail_alias/explorer/aliases_file +++ b/type/__mail_alias/explorer/aliases_file @@ -17,7 +17,7 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# This explorer tries to find the correct aliases file. +# This explorer finds the aliases file to modify. found() { echo "$*"; exit 0; } diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index 7778536..f4cbf46 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -29,7 +29,13 @@ in exit 0 fi - echo "set aliases" >>"$__messages_out" + if test -s "${__object}/explorer/aliases" + then + echo "update aliases" >>"$__messages_out" + else + echo "add aliases" >>"$__messages_out" + fi + mode=1 ;; (absent) @@ -37,6 +43,7 @@ in test -s "${__object}/explorer/aliases" || exit 0 echo "delete aliases" >>"$__messages_out" + mode=0 ;; (*) @@ -46,11 +53,10 @@ esac aliases_file=$(cat "${__object}/explorer/aliases_file") -if test -z "${aliases_file}" -then +test -n "${aliases_file}" || { echo 'Could not determine aliases file path.' >&2 exit 1 -fi +} # "export" variables to remote printf 'mode=%u\n' "${mode}" @@ -58,16 +64,18 @@ printf "aliases_file='%s'\n" "${aliases_file}" cat <<'EOF' test -f "${aliases_file}" || touch "${aliases_file}" -awk -F ':[ \t]*' -v mode="${mode}" ' -function sepafter(f, default, _) { + +awk -F ':[ \t]*' -v mode=$mode ' +function sepafter(f, default, _) { + # finds the separator between field $f and $(f+1) _ = substr($0, length($f) + 1, index(substr($0, length($f)+1), $(f+1)) - 1) - if (_) return _ - else return default + return _ ? _ : default } function write_aliases() { if (aliases_written) return + # print aliases line printf "%s%s", ENVIRON["__object_id"], sepafter(1, ": ") while ((getline < aliases_should_file) > 0) { if (aliases_written) printf ", " @@ -83,36 +91,45 @@ BEGIN { } /^#/ { - # comment - select = 0; cont = 0 + # comment line (leave alone) + select = 0; cont = 0 # comments terminate alias lists and continuations print next } -{ - cont = ($0 ~ /\\$/) - if (cont) sub(/[ \t]*\\$/, "", $0) -} - /^[ \t]/ || cont { - # continuation line + # continuation line (either the previous line ended in a backslash or the + # line starts with whitespace) + + # if in the alias list of the "target" user, we drop the line as it has been + # rewritten previously if (select) next } +{ + # detect if the line is a line to be continued (ends with a backslash) + cont = ($0 ~ /\\$/) + # if it is, we drop the backslash from the line. + if (cont) sub(/[ \t]*\\$/, "", $0) +} + $1 == ENVIRON["__object_id"] { - in_list = 1 + # "target" user -> rewrite aliases list + select = 1 if (mode) write_aliases() next } { - in_list = 0 + # other user + select = 0 print } END { - # if the last line as an alias definition, the separator will be reused - if (mode && !aliases_written) write_aliases() + # if the last line was an alias, the separator will be reused (looks better) + if (mode && !aliases_written) + write_aliases() } ' <"${aliases_file}" >"${aliases_file}.tmp" || { echo 'Generating new aliases file failed!' >&2 @@ -121,9 +138,11 @@ END { if ! cmp -s "${aliases_file}" "${aliases_file}.tmp" then + # aliases file was modified, replace and run `newaliases` mv "${aliases_file}.tmp" "${aliases_file}" newaliases else + # no modifications were made, delete the temp file. rm "${aliases_file}.tmp" fi EOF From ca9e011d50ff2296fa0e4db74d99ecadbcee680f Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 11 Jun 2020 15:09:51 +0200 Subject: [PATCH 25/54] [type/__dma_auth] Fix off-by-one error --- type/__dma_auth/explorer/state | 2 +- type/__dma_auth/gencode-remote | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/type/__dma_auth/explorer/state b/type/__dma_auth/explorer/state index 18e13ce..0e6f7be 100755 --- a/type/__dma_auth/explorer/state +++ b/type/__dma_auth/explorer/state @@ -64,7 +64,7 @@ BEGIN { host = substr($0, length(login) + 2) if (match(host, DP)) { - passwd = substr(host, RSTART) + passwd = substr(host, RSTART + 1) host = substr(host, 1, RSTART - 1) } else { passwd = "" diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index 262a17a..c49779f 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -99,7 +99,10 @@ BEGIN { } # skip comments and empty lines -/^#/ || /^$/ { print; next } +/^#/ || /^$/ { + print + next +} { # parse line @@ -110,7 +113,7 @@ BEGIN { host = substr($0, length(login) + 2) if (match(host, DP)) { - passwd = substr(host, RSTART) + passwd = substr(host, RSTART + 1) host = substr(host, 1, RSTART - 1) } else { passwd = "" From 5b8ae33b4e0bad18f8b76b3cb326884dc16b9550 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 11 Jun 2020 15:21:35 +0200 Subject: [PATCH 26/54] [type/__dma_auth] Improve documentation and handle duplicate lines better The state explorer gained a new value "multiple" (it is not used anywhere, just informative). The code will only write a "should" line once and drop duplicate lines. --- type/__dma_auth/explorer/state | 22 ++++++++++++++++------ type/__dma_auth/gencode-remote | 10 +++++++--- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/type/__dma_auth/explorer/state b/type/__dma_auth/explorer/state index 0e6f7be..668b50f 100755 --- a/type/__dma_auth/explorer/state +++ b/type/__dma_auth/explorer/state @@ -17,8 +17,13 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# This explorer looks for lines matching the server parameter in dma's auth.conf -# and reports the login and server fields (password is cksummed) +# This explorer looks for a line matching the login and server parameters +# in dma's auth.conf and reports: +# present: a line matching login + host + password exists +# absent: no line matching login + host exists +# different_password: a line exists but with a different pasword +# multiple: multiple lines matching login + host exist +# (should never happen) auth_conf=$("${__type_explorer}/auth_conf") test -r "${auth_conf}" || exit 0 @@ -32,6 +37,7 @@ fi awk -F'\n' -v server="${server}" ' function getvalue(path) { + # Reads the first line of the file located at path and returns it. getline < path close(path) return $0 @@ -42,6 +48,7 @@ BEGIN { parameter_dir = ENVIRON["__object"] "/parameter/" + # Read the parameters of this object host_param = getvalue(parameter_dir "server") if (!host_param) host_param = ENVIRON["__object_id"] login_param = getvalue(parameter_dir "login") @@ -72,10 +79,13 @@ BEGIN { } host == host_param && login == login_param { - if (passwd == passwd_param) - state = "present" - else - state = "different_password" + # a match… + if (state == "absent") { + state = ((passwd == passwd_param) ? "present" : "different_password") + } else { + # report "multiple" to that the type can remove the duplicates. + state = "multiple" + } } END { diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index c49779f..46d9f31 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -78,6 +78,7 @@ test -f "${auth_conf}" || touch "${auth_conf}" awk -F '\n' -v mode=$mode ' function getvalue(path) { + # Reads the first line of the file located at path and returns it. getline < path close(path) return $0 @@ -124,11 +125,12 @@ host == host_param { if (mode) { # state_should == present if (login == login_param && !written) { - # replace line if host and login match + # replace line if host and login match (but only if no line has + # been written already -> no duplicates) print_should() written = 1 - next } + next } else { # state_should == absent if (!login_param || login == login_param) { @@ -139,7 +141,9 @@ host == host_param { } # leave other lines alone -{ print } +{ + print +} END { if (mode && !written) { From 193b1780dee58be94010e06bf3f3e296f7b4b283 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 11 Jun 2020 15:38:34 +0200 Subject: [PATCH 27/54] Improve error message when invalid --state is used. --- type/__dma_auth/gencode-remote | 3 ++- type/__mail_alias/gencode-remote | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index 46d9f31..c57e5cc 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -55,7 +55,8 @@ in printf 'delete authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" ;; (*) - printf 'Invalid --state: %s' "${state_should}" >&2 + printf 'Invalid --state: %s.\n' "${state_should}" >&2 + printf 'Acceptable values are: present, absent.\n' >&2 exit 1 ;; esac diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index f4cbf46..22ae89b 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -47,7 +47,8 @@ in mode=0 ;; (*) - printf 'Invalid --state given: %s\n' "$state_should" >&2 + printf 'Invalid --state: %s.\n' "$state_should" >&2 + printf 'Acceptable values are: present, absent.\n' >&2 exit 1 esac From 551348509717b4b44394a59bd11034e6363d5fbd Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 11 Jun 2020 17:01:22 +0200 Subject: [PATCH 28/54] [type/__dma] Improve documentation --- type/__dma/explorer/conf | 8 +++- type/__dma/gencode-remote | 96 ++++++++++++++++++++++++++------------- type/__dma/man.rst | 7 ++- 3 files changed, 75 insertions(+), 36 deletions(-) diff --git a/type/__dma/explorer/conf b/type/__dma/explorer/conf index 129e3c3..b4d6d26 100755 --- a/type/__dma/explorer/conf +++ b/type/__dma/explorer/conf @@ -17,8 +17,12 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# This explorer looks for lines matching the server parameter in dma's auth.conf -# and reports the login and server fields (password is cksummed) +# This explorer returns a sorted list of "active" (= non-commented) lines +# in the dma.conf file. +# "Trailing" line comments are stripped off. +# +# NOTE: This explorer assumes that the sort(1) utility supports the non-POXIX +# -s (stable sort) option. CONF_PATH=/etc/dma # set in Makefile dma_conf="${CONF_PATH:?}/dma.conf" diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index e4760d8..01537bf 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -20,7 +20,7 @@ else fi -# Generate config +# Generate "should" values for config conf_should=$( if test -s "${__object}/parameter/smarthost" then @@ -60,7 +60,7 @@ conf_should=$( if test -s "${__object}/parameter/port" then printf 'PORT %u\n' "$(cat "${__object}/parameter/port")" - elif test "${default_smtp_port}" -ne 25 + elif test "${default_smtp_port}" -ne 25 # DMA uses port 25 by default then printf 'PORT %u\n' "${default_smtp_port}" fi @@ -93,6 +93,7 @@ conf_should=$( echo 'NULLCLIENT' fi ) +# Sort conf_should to compare against "conf_is" conf_should=$(echo "$conf_should" | sort -s -k 1,1) config_updated=false @@ -100,24 +101,55 @@ if ! echo "$conf_should" | cmp -s "${__object}/explorer/conf" - then # config needs to be updated echo "dma_conf='${CONF_PATH:?}/dma.conf'" + + # The following AWK script will output the new config file to be stored on + # disk. To do so it reads the current dma.conf file and the config options + # that should be set (from stdin). + # Note that the path to the current dma.conf is passed to AWK twice, because + # the new file cannot be generated in one pass. + + # The logic tries to place options at a sensible location, that is: + # a) if the option is already used in the config file: + # group all similar options (e.g. MASQUERADE) at one place in the order + # they are listed in stdin. + # b) if it is a new option and a "default comment" (e.g. "#PORT 25") exists: + # place options grouped directly after the comment (the comment is left + # alone) + # c) otherwise: + # options are grouped by word (the first word in the line) and appended + # at the end of the file. + cat <<'EOF' awk -F '\n' ' -function comment_line(line) { return match(line, /^[ \t]*#+[ \t]*/) } -function empty_line(line) { return match(line, /^[ \t]*$/) } -function is_word(s) { return s ~ /^[A-Z_]+$/ } +function comment_line(line) { + # returns the position in line at which the comment's text starts + # (0 if the line is not a comment) + match(line, /^[ \t]*\#+[ \t]*/) + return RSTART ? (RLENGTH + 1) : 0 +} +function empty_line(line) { return line ~ /^[ \t]*$/ } +function is_word(s) { return s ~ /^[A-Z_]+$/ } # "looks like a plausible word" function first(line, sep) { + # returns the part of the line until sep is found + # (or the whole line if sep is not found) if (!sep) sep = SUBSEP return index(line, sep) ? substr(line, 1, index(line, sep) - 1) : line } function rest(line, sep) { + # returns the part of the line after the first occurrence of sep is found. + # (or nothing if sep is not found) if (!sep) sep = SUBSEP if (index(line, sep)) return substr(line, index(line, sep) + 1) } function conf_pop(word, value) { + # returns the next value for the config `word` and delete it from the list. + # if value is set, this function will only return value if it is the first + # option in the list, otherwise it returns 0. + if (!(word in conf)) return 0 if (!value) { if (index(conf[word], SUBSEP)) # more than one element? @@ -137,12 +169,14 @@ function conf_pop(word, value) { } function print_conf(word, value) { + # print a config line with the given parameters printf "%s", word if (value) printf " %s", value printf "\n" } function print_confs(word, value) { + # print config lines for all values stored in conf[word]. if (!(word in conf)) return if (conf[word]) { while (value = conf_pop(word)) @@ -154,6 +188,7 @@ function print_confs(word, value) { } BEGIN { + # read the "should" state into the `conf` array. while (getline < "/dev/stdin") { word = first($0, " ") if ((word in conf)) @@ -163,11 +198,12 @@ BEGIN { } } -# first pass, gather information +# first pass, gather information about where which information is stored in the +# current config file. This information will be used in the second pass. NR == FNR { if (comment_line($0)) { # comment line - word = first(substr($0, RLENGTH + 1), " ") + word = first(substr($0, comment_line($0) + 1), " ") if (is_word(word)) last_occ["#" word] = FNR } else { word = first($0, " ") @@ -175,19 +211,22 @@ NR == FNR { } } +# before second pass prepare hashes containing location information to be used +# in the second pass. NR > FNR && FNR == 1 { - # before second pass prepare hashes - + # First we drop the locations of commented-out options if a non-commented + # option is available. If a non-commented option is available, we will + # append new config options there to have them all at one place. for (k in last_occ) if (k ~ /^\#/ && (substr(k, 2) in last_occ)) delete last_occ[k] - for (k in last_occ) { - line_map[last_occ[k]] = k - } + # Reverse the option => line mapping. The line_map allows for easier lookups + # in the second pass. + for (k in last_occ) line_map[last_occ[k]] = k } -# second pass, output new config +# second pass, generate and output new config NR > FNR { if (comment_line($0) || empty_line($0)) { # comment or empty line @@ -195,21 +234,24 @@ NR > FNR { if ((FNR in line_map)) { if (line_map[FNR] ~ /^\#/) { - # the "matching" comment line is here + # This line contains a commented config option. If the conf hash + # contains options to be set, we output them here because this + # option is not used in the current config. k = substr(line_map[FNR], 2) if ((k in conf)) print_confs(k) } if (("INSECURE" in conf) && line_map[FNR] ~ /^\#?SECURE$/) { - # INSECURE goes where SECURE comment is + # INSECURE goes where SECURE comment is. print_confs("INSECURE") } } } else { - sub(/[ \t]*\#.*$/, "", $0) # ignore comments word = first($0, " ") + value = rest($0, " ") + sub(/[ \t]*\#.*$/, "", value) # ignore comments in value - if ((word in conf) && rest($0, " ") == first(conf[word])) { + if ((word in conf) && value == first(conf[word])) { # keep config options we want conf_pop(word) print @@ -223,12 +265,13 @@ NR > FNR { } END { - # print rest of config options + # print rest of config options ( for (word in conf) print_confs(word) } ' "${dma_conf}" "${dma_conf}" <<'EOF' >"${dma_conf}.tmp" \ && mv "${dma_conf}.tmp" "${dma_conf}" EOF + # Pass in "conf_should" via stdin echo "${conf_should}" echo 'EOF' @@ -239,20 +282,9 @@ fi if test -f "${__object}/parameter/send-test-email" then - modified=false - - if grep -q '^__mail_alias/root:' "${__messages_in}" - then - modified=true - elif grep -q '^__dma_auth/' "${__messages_in}" - then - modified=true - elif $config_updated - then - modified=true - fi - - if $modified + if grep -q '^__mail_alias/root:' "${__messages_in}" \ + || grep -q '^__dma_auth/' "${__messages_in}" \ + || $config_updated then cat <<-EOF sendmail root < Date: Thu, 11 Jun 2020 18:07:28 +0200 Subject: [PATCH 29/54] [type/__dma] Use EQS to split config lines --- type/__dma/gencode-remote | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 01537bf..cb2cdbe 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -130,19 +130,20 @@ function comment_line(line) { function empty_line(line) { return line ~ /^[ \t]*$/ } function is_word(s) { return s ~ /^[A-Z_]+$/ } # "looks like a plausible word" -function first(line, sep) { +function first(line, sep_re) { # returns the part of the line until sep is found # (or the whole line if sep is not found) - if (!sep) sep = SUBSEP - return index(line, sep) ? substr(line, 1, index(line, sep) - 1) : line + if (!sep_re) sep_re = "[" SUBSEP "]" + match(line, sep_re) + return RSTART ? substr(line, 1, RSTART - 1) : line } -function rest(line, sep) { +function rest(line, sep_re) { # returns the part of the line after the first occurrence of sep is found. # (or nothing if sep is not found) - if (!sep) sep = SUBSEP - if (index(line, sep)) - return substr(line, index(line, sep) + 1) + if (!sep_re) sep_re = "[" SUBSEP "]" + if (match(line, sep_re)) + return substr(line, RSTART + RLENGTH + 1) } function conf_pop(word, value) { @@ -188,13 +189,15 @@ function print_confs(word, value) { } BEGIN { + EQS = /[ \t]/ # copied from dma/conf.c + # read the "should" state into the `conf` array. while (getline < "/dev/stdin") { - word = first($0, " ") + word = first($0, EQS) if ((word in conf)) - conf[word] = conf[word] SUBSEP rest($0, " ") + conf[word] = conf[word] SUBSEP rest($0, EQS) else - conf[word] = rest($0, " ") + conf[word] = rest($0, EQS) } } @@ -203,10 +206,10 @@ BEGIN { NR == FNR { if (comment_line($0)) { # comment line - word = first(substr($0, comment_line($0) + 1), " ") + word = first(substr($0, comment_line($0) + 1), /[ ]/) if (is_word(word)) last_occ["#" word] = FNR } else { - word = first($0, " ") + word = first($0, EQS) if (is_word(word)) last_occ[word] = FNR } } @@ -247,8 +250,8 @@ NR > FNR { } } } else { - word = first($0, " ") - value = rest($0, " ") + word = first($0, EQS) + value = rest($0, EQS) sub(/[ \t]*\#.*$/, "", value) # ignore comments in value if ((word in conf) && value == first(conf[word])) { From 27102340de8bfde2fa03ebf8dfca7157dbe8ea9f Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 11 Jun 2020 20:50:42 +0200 Subject: [PATCH 30/54] [type/__mail_alias] Add bug notice about commas --- type/__mail_alias/man.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/type/__mail_alias/man.rst b/type/__mail_alias/man.rst index d6c7873..3782ffb 100644 --- a/type/__mail_alias/man.rst +++ b/type/__mail_alias/man.rst @@ -44,6 +44,15 @@ EXAMPLES # Disable redirection of mail for joe __mail_alias joe --state absent + +BUGS +---- +- Quoted strings are not parsed by this type. As a result, email addresses + containing ``,`` (commas) are treated incorrectly (they are treated as two + addresses/aliases.) + Make sure that email addresses do not contain commas. + + SEE ALSO -------- :strong:`aliases`\ (5) From c777a2b1c27ca81f5296c3b4395fa22db74478e1 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 11 Jun 2020 21:58:47 +0200 Subject: [PATCH 31/54] [type/__mail_alias] Some fixes in continuation line processing --- type/__mail_alias/explorer/aliases | 25 ++++++++++++++----------- type/__mail_alias/gencode-remote | 25 +++++++++++++------------ 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/type/__mail_alias/explorer/aliases b/type/__mail_alias/explorer/aliases index 0a8da94..4fffd3b 100755 --- a/type/__mail_alias/explorer/aliases +++ b/type/__mail_alias/explorer/aliases @@ -17,13 +17,15 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# Find aliases for a given name and print the aliases line separated +# Find aliases for a given user name and print the aliases (each one on a +# separate line) aliases_file=$("${__type_explorer}/aliases_file") test -r "${aliases_file}" || exit 0 awk -F ':[ \t]*' ' function print_aliases(aliases, matches) { + # prints comma-separated aliases (one per line) split(aliases, matches, /,[ \t]*/) for (i in matches) { gsub(/^[ \t]*|[ \t]*$/, "", matches[i]) @@ -37,15 +39,11 @@ function print_aliases(aliases, matches) { next } -/^[ \t]/ || cont { - # continuation line (either the previous line ended in a backslash or the - # line starts with whitespace) - - if (select) - print_aliases($0) -} - { + # is this line a continuation line? + # (the prev. line ended in a backslash or the line starts with whitespace) + is_cont = /^[ \t]/ || cont + # detect if the line is a line to be continued (ends with a backslash) cont = ($0 ~ /\\$/) @@ -57,9 +55,14 @@ function print_aliases(aliases, matches) { } } -$1 == ENVIRON["__object_id"] && !select { +is_cont { + # if in the alias list of the "target" user, we also print these aliases. + if (select) print_aliases($0) + next +} + +$1 == ENVIRON["__object_id"] { # "target" user -> print alias list - # (only if !select; because of whitespacecontinuation lines) select = 1 print_aliases($2) next diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index 22ae89b..cc5fc42 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -69,7 +69,7 @@ test -f "${aliases_file}" || touch "${aliases_file}" awk -F ':[ \t]*' -v mode=$mode ' function sepafter(f, default, _) { # finds the separator between field $f and $(f+1) - _ = substr($0, length($f) + 1, index(substr($0, length($f)+1), $(f+1)) - 1) + _ = substr($0, length($f)+1, index(substr($0, length($f)+1), $(f+1))-1) return _ ? _ : default } @@ -91,29 +91,30 @@ BEGIN { aliases_should_file = (ENVIRON["__object"] "/parameter/alias") } -/^#/ { +/^[ \t]*\#/ { # comment line (leave alone) select = 0; cont = 0 # comments terminate alias lists and continuations print next } -/^[ \t]/ || cont { - # continuation line (either the previous line ended in a backslash or the - # line starts with whitespace) - - # if in the alias list of the "target" user, we drop the line as it has been - # rewritten previously - if (select) next -} - { + # is this line a continuation line? + # (the prev. line ended in a backslash or the line starts with whitespace) + is_cont = /^[ \t]/ || cont + # detect if the line is a line to be continued (ends with a backslash) cont = ($0 ~ /\\$/) # if it is, we drop the backslash from the line. if (cont) sub(/[ \t]*\\$/, "", $0) } +is_cont { + # we ignore the line as it has been rewritten previously or is not + # interesting + next +} + $1 == ENVIRON["__object_id"] { # "target" user -> rewrite aliases list select = 1 @@ -139,7 +140,7 @@ END { if ! cmp -s "${aliases_file}" "${aliases_file}.tmp" then - # aliases file was modified, replace and run `newaliases` + # aliases file was modified, replace and run `newaliases`. mv "${aliases_file}.tmp" "${aliases_file}" newaliases else From 0f81b89f709e26a98a369d6e8b0797eb3ca85909 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 22 Jun 2020 13:29:28 +0200 Subject: [PATCH 32/54] [type/__dma] Make --smarthost optional --- type/__dma/parameter/optional | 1 + type/__dma/parameter/required | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 type/__dma/parameter/required diff --git a/type/__dma/parameter/optional b/type/__dma/parameter/optional index 3f6cb5d..615c189 100644 --- a/type/__dma/parameter/optional +++ b/type/__dma/parameter/optional @@ -1,3 +1,4 @@ mailname port security +smarthost diff --git a/type/__dma/parameter/required b/type/__dma/parameter/required deleted file mode 100644 index 0753fb6..0000000 --- a/type/__dma/parameter/required +++ /dev/null @@ -1 +0,0 @@ -smarthost From 27b832f2127fc0b475a6390cacb9219b9516328f Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 22 Jun 2020 14:02:13 +0200 Subject: [PATCH 33/54] [type/__dma] Add support for Alpine Linux requires the testing repository, currently. --- type/__dma/gencode-remote | 8 ++++---- type/__dma/manifest | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index cb2cdbe..a6aca0d 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -122,7 +122,7 @@ then cat <<'EOF' awk -F '\n' ' function comment_line(line) { - # returns the position in line at which the comment's text starts + # returns the position in line at which the comment'\''s text starts # (0 if the line is not a comment) match(line, /^[ \t]*\#+[ \t]*/) return RSTART ? (RLENGTH + 1) : 0 @@ -143,7 +143,7 @@ function rest(line, sep_re) { # (or nothing if sep is not found) if (!sep_re) sep_re = "[" SUBSEP "]" if (match(line, sep_re)) - return substr(line, RSTART + RLENGTH + 1) + return substr(line, RSTART + RLENGTH) } function conf_pop(word, value) { @@ -189,7 +189,7 @@ function print_confs(word, value) { } BEGIN { - EQS = /[ \t]/ # copied from dma/conf.c + EQS = "[ \t]" # copied from dma/conf.c # read the "should" state into the `conf` array. while (getline < "/dev/stdin") { @@ -206,7 +206,7 @@ BEGIN { NR == FNR { if (comment_line($0)) { # comment line - word = first(substr($0, comment_line($0) + 1), /[ ]/) + word = first(substr($0, comment_line($0)), " ") if (is_word(word)) last_occ["#" word] = FNR } else { word = first($0, EQS) diff --git a/type/__dma/manifest b/type/__dma/manifest index 7abd7c8..75e42d7 100755 --- a/type/__dma/manifest +++ b/type/__dma/manifest @@ -5,6 +5,10 @@ os=$(cat "${__global}/explorer/os") # Install DMA case $os in + (alpine) + __package dma --state present + export require='__package/dma' + ;; (debian|devuan|ubuntu) __package dma --state present export require='__package/dma' From aa605cada45d5a91da5c4153fa822d95a8214d80 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 22 Jun 2020 14:02:42 +0200 Subject: [PATCH 34/54] [type/__mail_aliases] Add support for Alpine Linux Alpine's DMA package has a typo and installs "newailases" instead of "newaliases". I adjusted the code-remote to only run newaliases if it is available. Otherwise, tough luck, user gotta either fix his system or run manually. --- type/__mail_alias/explorer/aliases_file | 4 ++-- type/__mail_alias/gencode-remote | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/type/__mail_alias/explorer/aliases_file b/type/__mail_alias/explorer/aliases_file index 2710792..90bccde 100755 --- a/type/__mail_alias/explorer/aliases_file +++ b/type/__mail_alias/explorer/aliases_file @@ -28,7 +28,7 @@ check_file() { fi } -case $("$__explorer/os") +case $("${__explorer}/os") in (freebsd|openbsd|solaris) check_file /etc/mail/aliases @@ -36,7 +36,7 @@ in # default found /etc/mail/aliases ;; - (debian|devuan|ubuntu) + (alpine|debian|devuan|ubuntu) check_file /etc/aliases # default diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index cc5fc42..3eaad75 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -142,7 +142,9 @@ if ! cmp -s "${aliases_file}" "${aliases_file}.tmp" then # aliases file was modified, replace and run `newaliases`. mv "${aliases_file}.tmp" "${aliases_file}" - newaliases + + # run newaliases if present + command -v newaliases >/dev/null 2>&1 && newaliases || true else # no modifications were made, delete the temp file. rm "${aliases_file}.tmp" From 43c59985d0aba80ae6b41a56143e3247423c23a5 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 25 Jun 2020 18:07:51 +0200 Subject: [PATCH 35/54] [type/__mail_alias] Fallback to /etc/aliases instead of /etc/mail/aliases --- type/__mail_alias/explorer/aliases_file | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/type/__mail_alias/explorer/aliases_file b/type/__mail_alias/explorer/aliases_file index 90bccde..a59bb99 100755 --- a/type/__mail_alias/explorer/aliases_file +++ b/type/__mail_alias/explorer/aliases_file @@ -47,6 +47,6 @@ in check_file /etc/aliases # default - found /etc/mail/aliases + found /etc/aliases ;; esac From 49d39eaee50bc848840cb4fda91e2ed0cb4a14f1 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 26 Aug 2020 18:01:52 +0200 Subject: [PATCH 36/54] [type/__mail_alias] Fix error with some AWK implementations Some AWK implementations seem to have a problem with parameters named default. awk: cmd. line:2: function sepafter(f, default, _) { awk: cmd. line:2: ^ syntax error awk: cmd. line:5: return _ ? _ : default awk: cmd. line:5: ^ syntax error In addition the temp file is removed if an error occurs. --- type/__mail_alias/gencode-remote | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index 3eaad75..9f4af1b 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -67,10 +67,10 @@ cat <<'EOF' test -f "${aliases_file}" || touch "${aliases_file}" awk -F ':[ \t]*' -v mode=$mode ' -function sepafter(f, default, _) { +function sepafter(f, def, _) { # finds the separator between field $f and $(f+1) _ = substr($0, length($f)+1, index(substr($0, length($f)+1), $(f+1))-1) - return _ ? _ : default + return _ ? _ : def } function write_aliases() { @@ -134,6 +134,7 @@ END { write_aliases() } ' <"${aliases_file}" >"${aliases_file}.tmp" || { + rm -f "${aliases_file}.tmp" echo 'Generating new aliases file failed!' >&2 exit 1 } From 445bc75deba18b8fa978e7d5d8ed8189908457d6 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 28 Sep 2020 10:59:13 +0200 Subject: [PATCH 37/54] [type/__dma_auth] Drop --server parameter Currently, dma does not differentiate between login users on the SMTP server. It will pick whatever entry it finds first (https://github.com/corecode/dma/blob/v0.13/net.c#L531). As a result, the --server parameter only adds confusion. --- type/__dma_auth/explorer/state | 29 +++++++++++++---------------- type/__dma_auth/gencode-remote | 24 +++++++++--------------- type/__dma_auth/man.rst | 6 ++++-- type/__dma_auth/parameter/optional | 1 - 4 files changed, 26 insertions(+), 34 deletions(-) diff --git a/type/__dma_auth/explorer/state b/type/__dma_auth/explorer/state index 668b50f..621e5a2 100755 --- a/type/__dma_auth/explorer/state +++ b/type/__dma_auth/explorer/state @@ -17,25 +17,18 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# This explorer looks for a line matching the login and server parameters +# This explorer looks for a line matching the server parameter # in dma's auth.conf and reports: # present: a line matching login + host + password exists # absent: no line matching login + host exists -# different_password: a line exists but with a different pasword -# multiple: multiple lines matching login + host exist -# (should never happen) +# different_login: a line exists but with a different login user +# different_password: a line exists but with a different password +# multiple: multiple lines matching host exist (should not happen) auth_conf=$("${__type_explorer}/auth_conf") test -r "${auth_conf}" || exit 0 -if test -f "${__object}/parameter/server" -then - server=$(cat "${__object}/parameter/server") -else - server=$__object_id -fi - -awk -F'\n' -v server="${server}" ' +awk -F'\n' ' function getvalue(path) { # Reads the first line of the file located at path and returns it. getline < path @@ -49,8 +42,7 @@ BEGIN { parameter_dir = ENVIRON["__object"] "/parameter/" # Read the parameters of this object - host_param = getvalue(parameter_dir "server") - if (!host_param) host_param = ENVIRON["__object_id"] + host_param = ENVIRON["__object_id"] login_param = getvalue(parameter_dir "login") passwd_param = getvalue(parameter_dir "password") @@ -78,10 +70,15 @@ BEGIN { } } -host == host_param && login == login_param { +host == host_param { # a match… if (state == "absent") { - state = ((passwd == passwd_param) ? "present" : "different_password") + if (login != login_param) + state = "different_login" + else if (passwd != passwd_param) + state = "different_password" + else + state = "present" } else { # report "multiple" to that the type can remove the duplicates. state = "multiple" diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index c57e5cc..e73c424 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -21,14 +21,16 @@ state_is=$(cat "${__object}/explorer/state") state_should=$(cat "${__object}/parameter/state") -if test -f "${__object}/parameter/server" -then - server=$(cat "${__object}/parameter/server") -else - server=$__object_id -fi +server=$__object_id login=$(cat "${__object}/parameter/login") + +auth_conf=$(cat "${__object}/explorer/auth_conf") +test -n "${auth_conf}" || { + echo 'Cannot determine path of dma auth.conf' >&2 + exit 1 +} + if test "${state_is}" = "${state_should}" then # state is as it should @@ -61,13 +63,6 @@ in ;; esac -auth_conf=$(cat "${__object}/explorer/auth_conf") - -test -n "${auth_conf}" || { - echo 'Cannot determine path of dma auth.conf' >&2 - exit 1 -} - cat < Date: Mon, 28 Sep 2020 16:34:12 +0200 Subject: [PATCH 38/54] Move auth_conf explorer from __dma_auth to __dma --- type/__dma/explorer/auth_conf | 50 +++++++++++++++++++++++++++++- type/__dma_auth/explorer/auth_conf | 50 +----------------------------- 2 files changed, 50 insertions(+), 50 deletions(-) mode change 120000 => 100755 type/__dma/explorer/auth_conf mode change 100755 => 120000 type/__dma_auth/explorer/auth_conf diff --git a/type/__dma/explorer/auth_conf b/type/__dma/explorer/auth_conf deleted file mode 120000 index db038ae..0000000 --- a/type/__dma/explorer/auth_conf +++ /dev/null @@ -1 +0,0 @@ -../../__dma_auth/explorer/auth_conf \ No newline at end of file diff --git a/type/__dma/explorer/auth_conf b/type/__dma/explorer/auth_conf new file mode 100755 index 0000000..cef0aca --- /dev/null +++ b/type/__dma/explorer/auth_conf @@ -0,0 +1,49 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# This explorer determines the path of dma's auth.conf file + +# No dma.conf -> use default +test -f /etc/dma/dma.conf || { + echo /etc/dma/auth.conf + exit 0 +} +test -r /etc/dma/dma.conf || { + echo 'Cannot read /etc/dma/dma.conf' >&2 + exit 1 +} + +# Get AUTHPATH from dma.conf +awk -F'[ \t]' ' +{ + sub(/#.*$/, "", $0) # remove comments + if (!$0) next # ignore empty lines +} +$1 == "AUTHPATH" { + # Store authpath. In dma conf parsing last wins. + if ($2) authpath = substr($0, index($0, " ") + 1) +} +END { + if (authpath) { + print authpath + exit 0 + } else exit 1 +} +' /etc/dma/dma.conf \ +|| echo /etc/dma/auth.conf # default diff --git a/type/__dma_auth/explorer/auth_conf b/type/__dma_auth/explorer/auth_conf deleted file mode 100755 index cef0aca..0000000 --- a/type/__dma_auth/explorer/auth_conf +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh -e -# -# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) -# -# This file is part of cdist. -# -# cdist is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# cdist is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with cdist. If not, see . -# -# This explorer determines the path of dma's auth.conf file - -# No dma.conf -> use default -test -f /etc/dma/dma.conf || { - echo /etc/dma/auth.conf - exit 0 -} -test -r /etc/dma/dma.conf || { - echo 'Cannot read /etc/dma/dma.conf' >&2 - exit 1 -} - -# Get AUTHPATH from dma.conf -awk -F'[ \t]' ' -{ - sub(/#.*$/, "", $0) # remove comments - if (!$0) next # ignore empty lines -} -$1 == "AUTHPATH" { - # Store authpath. In dma conf parsing last wins. - if ($2) authpath = substr($0, index($0, " ") + 1) -} -END { - if (authpath) { - print authpath - exit 0 - } else exit 1 -} -' /etc/dma/dma.conf \ -|| echo /etc/dma/auth.conf # default diff --git a/type/__dma_auth/explorer/auth_conf b/type/__dma_auth/explorer/auth_conf new file mode 120000 index 0000000..e89de93 --- /dev/null +++ b/type/__dma_auth/explorer/auth_conf @@ -0,0 +1 @@ +../../__dma/explorer/auth_conf \ No newline at end of file From 3feaea1d96a361bf16893f1a2442ce4338ac6c89 Mon Sep 17 00:00:00 2001 From: Marko Seric Date: Mon, 28 Sep 2020 16:43:31 +0200 Subject: [PATCH 39/54] [type/__dma_auth] Externalise AWK update script to separate file --- type/__dma_auth/files/update_dma_auth.awk | 93 +++++++++++++++++++++++ type/__dma_auth/gencode-remote | 90 ++-------------------- 2 files changed, 98 insertions(+), 85 deletions(-) create mode 100644 type/__dma_auth/files/update_dma_auth.awk diff --git a/type/__dma_auth/files/update_dma_auth.awk b/type/__dma_auth/files/update_dma_auth.awk new file mode 100644 index 0000000..c50198b --- /dev/null +++ b/type/__dma_auth/files/update_dma_auth.awk @@ -0,0 +1,93 @@ +#!/usr/bin/awk -f +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +function getvalue(path) { + # Reads the first line of the file located at path and returns it. + getline < path + close(path) + return $0 +} + +function print_should() { + printf "%s|%s:%s\n", login_param, host_param, passwd_param +} + +BEGIN { + FS = "\n" + DP = "[: \t]" # copied from dma/conf.c + + parameter_dir = ENVIRON["__object"] "/parameter/" + + mode = (getvalue(parameter_dir "state") != "absent") + + host_param = ENVIRON["__object_id"] + login_param = getvalue(parameter_dir "login") + passwd_param = getvalue(parameter_dir "password") +} + +# skip comments and empty lines +/^#/ || /^$/ { + print + next +} + +{ + # parse line (like dma/conf.c would) + + login = substr($0, 1, index($0, "|") - 1) + if (!login) { login = $0 } # if no "|" found + + host = substr($0, length(login) + 2) + + if (match(host, DP)) { + passwd = substr(host, RSTART + 1) + host = substr(host, 1, RSTART - 1) + } else { + passwd = "" + } +} + +host == host_param { + if (mode) { + # state_should == present + if (!written) { + # replace first line if host matches (but only if no line has + # been written already -> no duplicates) + print_should() + written = 1 + } + next + } else { + # state_should == absent + next + } +} + +# leave other lines alone +{ + print +} + +END { + if (mode && !written) { + # append line if no match to replace was found + print_should() + } +} diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index e73c424..d8be7e8 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -18,6 +18,8 @@ # along with cdist. If not, see . # +drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } + state_is=$(cat "${__object}/explorer/state") state_should=$(cat "${__object}/parameter/state") @@ -42,8 +44,6 @@ in (present) test -n "${login}" || { echo '--login must be non-empty' >&2; exit 1; } - mode=1 - if test "${state_is}" = 'absent' then printf 'add authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" @@ -52,8 +52,6 @@ in fi ;; (absent) - mode=0 - printf 'delete authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" ;; (*) @@ -65,87 +63,9 @@ esac cat < no duplicates) - print_should() - written = 1 - } - next - } else { - # state_should == absent - if (!login_param || login == login_param) { - # empty --login -> drop all lines for this host - next - } - } -} - -# leave other lines alone -{ - print -} - -END { - if (mode && !written) { - # append line if no match to replace was found - print_should() - } -} -' <"${auth_conf}" >"${auth_conf}.tmp" \ - && mv "${auth_conf}.tmp" "${auth_conf}" +awk '$(drop_awk_comments "${__type}/files/update_dma_auth.awk")' <'${auth_conf}' >'${auth_conf}.tmp' \ +&& cat '${auth_conf}.tmp' >'${auth_conf}' +rm -f '${auth_conf}.tmp' EOF From 6ae08085607f1a73832b80a20ad4671950e5c82e Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 28 Sep 2020 16:54:21 +0200 Subject: [PATCH 40/54] [type/__dma] Externalise AWK update script to separate file --- type/__dma/files/update_dma_conf.awk | 170 +++++++++++++++++++++++++++ type/__dma/gencode-remote | 167 ++------------------------ 2 files changed, 179 insertions(+), 158 deletions(-) create mode 100644 type/__dma/files/update_dma_conf.awk diff --git a/type/__dma/files/update_dma_conf.awk b/type/__dma/files/update_dma_conf.awk new file mode 100644 index 0000000..67661fd --- /dev/null +++ b/type/__dma/files/update_dma_conf.awk @@ -0,0 +1,170 @@ +#!/usr/bin/awk -f +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . + +function comment_line(line) { + # returns the position in line at which the comment'\''s text starts + # (0 if the line is not a comment) + match(line, /^[ \t]*\#+[ \t]*/) + return RSTART ? (RLENGTH + 1) : 0 +} +function empty_line(line) { return line ~ /^[ \t]*$/ } +function is_word(s) { return s ~ /^[A-Z_]+$/ } # "looks like a plausible word" + +function first(line, sep_re) { + # returns the part of the line until sep is found + # (or the whole line if sep is not found) + if (!sep_re) sep_re = "[" SUBSEP "]" + match(line, sep_re) + return RSTART ? substr(line, 1, RSTART - 1) : line +} + +function rest(line, sep_re) { + # returns the part of the line after the first occurrence of sep is found. + # (or nothing if sep is not found) + if (!sep_re) sep_re = "[" SUBSEP "]" + if (match(line, sep_re)) + return substr(line, RSTART + RLENGTH) +} + +function conf_pop(word, value) { + # returns the next value for the config `word` and delete it from the list. + # if value is set, this function will only return value if it is the first + # option in the list, otherwise it returns 0. + + if (!(word in conf)) return 0 + if (!value) { + if (index(conf[word], SUBSEP)) # more than one element? + value = substr(conf[word], 1, index(conf[word], SUBSEP) - 1) + else + value = conf[word] + } + + if (index(conf[word], SUBSEP)) { + if (index(conf[word], value SUBSEP) != 1) return 0 + conf[word] = substr(conf[word], length(value) + 2) + } else { + if (conf[word] != value) return 0 + delete conf[word] + } + return value +} + +function print_conf(word, value) { + # print a config line with the given parameters + printf "%s", word + if (value) printf " %s", value + printf "\n" +} + +function print_confs(word, value) { + # print config lines for all values stored in conf[word]. + if (!(word in conf)) return + if (conf[word]) { + while (value = conf_pop(word)) + print_conf(word, value) + } else { + print_conf(word) + delete conf[word] + } +} + +BEGIN { + FS = "\n" + EQS = "[ \t]" # copied from dma/conf.c + + # read the "should" state into the `conf` array. + while (getline < "/dev/stdin") { + word = first($0, EQS) + if ((word in conf)) + conf[word] = conf[word] SUBSEP rest($0, EQS) + else + conf[word] = rest($0, EQS) + } +} + +# first pass, gather information about where which information is stored in the +# current config file. This information will be used in the second pass. +NR == FNR { + if (comment_line($0)) { + # comment line + word = first(substr($0, comment_line($0)), " ") + if (is_word(word)) last_occ["#" word] = FNR + } else { + word = first($0, EQS) + if (is_word(word)) last_occ[word] = FNR + } +} + +# before second pass prepare hashes containing location information to be used +# in the second pass. +NR > FNR && FNR == 1 { + # First we drop the locations of commented-out options if a non-commented + # option is available. If a non-commented option is available, we will + # append new config options there to have them all at one place. + for (k in last_occ) + if (k ~ /^\#/ && (substr(k, 2) in last_occ)) + delete last_occ[k] + + # Reverse the option => line mapping. The line_map allows for easier lookups + # in the second pass. + for (k in last_occ) line_map[last_occ[k]] = k +} + +# second pass, generate and output new config +NR > FNR { + if (comment_line($0) || empty_line($0)) { + # comment or empty line + print + + if ((FNR in line_map)) { + if (line_map[FNR] ~ /^\#/) { + # This line contains a commented config option. If the conf hash + # contains options to be set, we output them here because this + # option is not used in the current config. + k = substr(line_map[FNR], 2) + if ((k in conf)) print_confs(k) + } + + if (("INSECURE" in conf) && line_map[FNR] ~ /^\#?SECURE$/) { + # INSECURE goes where SECURE comment is. + print_confs("INSECURE") + } + } + } else { + word = first($0, EQS) + value = rest($0, EQS) + sub(/[ \t]*\#.*$/, "", value) # ignore comments in value + + if ((word in conf) && value == first(conf[word])) { + # keep config options we want + conf_pop(word) + print + } + + if ((FNR in line_map) && line_map[FNR] == word) { + # rest of config options should be here + print_confs(word) + } + } +} + +END { + # print rest of config options ( + for (word in conf) print_confs(word) +} diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index a6aca0d..8177de9 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -1,5 +1,7 @@ #!/bin/sh -e +drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } + CONF_PATH=/etc/dma # set in Makefile # Determine mailname @@ -100,7 +102,7 @@ config_updated=false if ! echo "$conf_should" | cmp -s "${__object}/explorer/conf" - then # config needs to be updated - echo "dma_conf='${CONF_PATH:?}/dma.conf'" + dma_conf="${CONF_PATH:?}/dma.conf" # The following AWK script will output the new config file to be stored on # disk. To do so it reads the current dma.conf file and the config options @@ -119,164 +121,13 @@ then # options are grouped by word (the first word in the line) and appended # at the end of the file. - cat <<'EOF' -awk -F '\n' ' -function comment_line(line) { - # returns the position in line at which the comment'\''s text starts - # (0 if the line is not a comment) - match(line, /^[ \t]*\#+[ \t]*/) - return RSTART ? (RLENGTH + 1) : 0 -} -function empty_line(line) { return line ~ /^[ \t]*$/ } -function is_word(s) { return s ~ /^[A-Z_]+$/ } # "looks like a plausible word" - -function first(line, sep_re) { - # returns the part of the line until sep is found - # (or the whole line if sep is not found) - if (!sep_re) sep_re = "[" SUBSEP "]" - match(line, sep_re) - return RSTART ? substr(line, 1, RSTART - 1) : line -} - -function rest(line, sep_re) { - # returns the part of the line after the first occurrence of sep is found. - # (or nothing if sep is not found) - if (!sep_re) sep_re = "[" SUBSEP "]" - if (match(line, sep_re)) - return substr(line, RSTART + RLENGTH) -} - -function conf_pop(word, value) { - # returns the next value for the config `word` and delete it from the list. - # if value is set, this function will only return value if it is the first - # option in the list, otherwise it returns 0. - - if (!(word in conf)) return 0 - if (!value) { - if (index(conf[word], SUBSEP)) # more than one element? - value = substr(conf[word], 1, index(conf[word], SUBSEP) - 1) - else - value = conf[word] - } - - if (index(conf[word], SUBSEP)) { - if (index(conf[word], value SUBSEP) != 1) return 0 - conf[word] = substr(conf[word], length(value) + 2) - } else { - if (conf[word] != value) return 0 - delete conf[word] - } - return value -} - -function print_conf(word, value) { - # print a config line with the given parameters - printf "%s", word - if (value) printf " %s", value - printf "\n" -} - -function print_confs(word, value) { - # print config lines for all values stored in conf[word]. - if (!(word in conf)) return - if (conf[word]) { - while (value = conf_pop(word)) - print_conf(word, value) - } else { - print_conf(word) - delete conf[word] - } -} - -BEGIN { - EQS = "[ \t]" # copied from dma/conf.c - - # read the "should" state into the `conf` array. - while (getline < "/dev/stdin") { - word = first($0, EQS) - if ((word in conf)) - conf[word] = conf[word] SUBSEP rest($0, EQS) - else - conf[word] = rest($0, EQS) - } -} - -# first pass, gather information about where which information is stored in the -# current config file. This information will be used in the second pass. -NR == FNR { - if (comment_line($0)) { - # comment line - word = first(substr($0, comment_line($0)), " ") - if (is_word(word)) last_occ["#" word] = FNR - } else { - word = first($0, EQS) - if (is_word(word)) last_occ[word] = FNR - } -} - -# before second pass prepare hashes containing location information to be used -# in the second pass. -NR > FNR && FNR == 1 { - # First we drop the locations of commented-out options if a non-commented - # option is available. If a non-commented option is available, we will - # append new config options there to have them all at one place. - for (k in last_occ) - if (k ~ /^\#/ && (substr(k, 2) in last_occ)) - delete last_occ[k] - - # Reverse the option => line mapping. The line_map allows for easier lookups - # in the second pass. - for (k in last_occ) line_map[last_occ[k]] = k -} - -# second pass, generate and output new config -NR > FNR { - if (comment_line($0) || empty_line($0)) { - # comment or empty line - print - - if ((FNR in line_map)) { - if (line_map[FNR] ~ /^\#/) { - # This line contains a commented config option. If the conf hash - # contains options to be set, we output them here because this - # option is not used in the current config. - k = substr(line_map[FNR], 2) - if ((k in conf)) print_confs(k) - } - - if (("INSECURE" in conf) && line_map[FNR] ~ /^\#?SECURE$/) { - # INSECURE goes where SECURE comment is. - print_confs("INSECURE") - } - } - } else { - word = first($0, EQS) - value = rest($0, EQS) - sub(/[ \t]*\#.*$/, "", value) # ignore comments in value - - if ((word in conf) && value == first(conf[word])) { - # keep config options we want - conf_pop(word) - print - } - - if ((FNR in line_map) && line_map[FNR] == word) { - # rest of config options should be here - print_confs(word) - } - } -} - -END { - # print rest of config options ( - for (word in conf) print_confs(word) -} -' "${dma_conf}" "${dma_conf}" <<'EOF' >"${dma_conf}.tmp" \ - && mv "${dma_conf}.tmp" "${dma_conf}" + cat <'${dma_conf}.tmp' \ +&& cat '${dma_conf}.tmp' >'${dma_conf}' +${conf_should} EOF - # Pass in "conf_should" via stdin - echo "${conf_should}" - echo 'EOF' +rm '${dma_conf}.tmp' +CODE config_updated=true echo 'config updated' >>"${__messages_out}" From b48b48e4047d8855e9ee2d635a0c345cad007997 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 28 Sep 2020 17:29:41 +0200 Subject: [PATCH 41/54] [type/__mail_alias] Externalise AWK update script to separate file --- type/__mail_alias/files/update_aliases.awk | 98 ++++++++++++++++++ type/__mail_alias/gencode-remote | 115 ++++----------------- 2 files changed, 119 insertions(+), 94 deletions(-) create mode 100644 type/__mail_alias/files/update_aliases.awk diff --git a/type/__mail_alias/files/update_aliases.awk b/type/__mail_alias/files/update_aliases.awk new file mode 100644 index 0000000..87ea202 --- /dev/null +++ b/type/__mail_alias/files/update_aliases.awk @@ -0,0 +1,98 @@ +#!/usr/bin/awk -f +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +function getvalue(path) { + # Reads the first line of the file located at path and returns it. + getline < path + close(path) + return $0 +} + +function sepafter(f, def, _) { + # finds the separator between field $f and $(f+1) + _ = substr($0, length($f)+1, index(substr($0, length($f)+1), $(f+1))-1) + return _ ? _ : def +} + +function write_aliases() { + if (aliases_written) return + + # print aliases line + printf "%s%s", ENVIRON["__object_id"], sepafter(1, ": ") + while ((getline < aliases_should_file) > 0) { + if (aliases_written) printf ", " + printf "%s", $0 + aliases_written = 1 + } + printf "\n" + close(aliases_should_file) +} + +BEGIN { + FS = ":[ \t]*" + + parameter_dir = ENVIRON["__object"] "/parameter/" + + mode = (getvalue(parameter_dir "state") != "absent") + aliases_should_file = (parameter_dir "/alias") +} + +/^[ \t]*\#/ { + # comment line (leave alone) + select = 0; cont = 0 # comments terminate alias lists and continuations + print + next +} + +{ + # is this line a continuation line? + # (the prev. line ended in a backslash or the line starts with whitespace) + is_cont = /^[ \t]/ || cont + + # detect if the line is a line to be continued (ends with a backslash) + cont = ($0 ~ /\\$/) + # if it is, we drop the backslash from the line. + if (cont) sub(/[ \t]*\\$/, "", $0) +} + +is_cont { + # we ignore the line as it has been rewritten previously or is not + # interesting + next +} + +$1 == ENVIRON["__object_id"] { + # "target" user -> rewrite aliases list + select = 1 + if (mode) write_aliases() + next +} + +{ + # other user + select = 0 + print +} + +END { + # if the last line was an alias, the separator will be reused (looks better) + if (mode && !aliases_written) + write_aliases() +} diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index 9f4af1b..e5bc2b7 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -18,6 +18,16 @@ # along with cdist. If not, see . # +drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } + +aliases_file=$(cat "${__object}/explorer/aliases_file") + +test -n "${aliases_file}" || { + echo 'Could not determine aliases file path.' >&2 + exit 1 +} + + state_should=$(cat "${__object}/parameter/state") case $state_should @@ -35,119 +45,36 @@ in else echo "add aliases" >>"$__messages_out" fi - - mode=1 ;; (absent) # nothing to do if no aliases found. test -s "${__object}/explorer/aliases" || exit 0 echo "delete aliases" >>"$__messages_out" - - mode=0 ;; (*) - printf 'Invalid --state: %s.\n' "$state_should" >&2 + printf 'Invalid --state: %s.\n' "${state_should}" >&2 printf 'Acceptable values are: present, absent.\n' >&2 exit 1 esac -aliases_file=$(cat "${__object}/explorer/aliases_file") +cat <&2 - exit 1 -} - -# "export" variables to remote -printf 'mode=%u\n' "${mode}" -printf "aliases_file='%s'\n" "${aliases_file}" - -cat <<'EOF' -test -f "${aliases_file}" || touch "${aliases_file}" - -awk -F ':[ \t]*' -v mode=$mode ' -function sepafter(f, def, _) { - # finds the separator between field $f and $(f+1) - _ = substr($0, length($f)+1, index(substr($0, length($f)+1), $(f+1))-1) - return _ ? _ : def -} - -function write_aliases() { - if (aliases_written) return - - # print aliases line - printf "%s%s", ENVIRON["__object_id"], sepafter(1, ": ") - while ((getline < aliases_should_file) > 0) { - if (aliases_written) printf ", " - printf "%s", $0 - aliases_written = 1 - } - printf "\n" - close(aliases_should_file) -} - -BEGIN { - aliases_should_file = (ENVIRON["__object"] "/parameter/alias") -} - -/^[ \t]*\#/ { - # comment line (leave alone) - select = 0; cont = 0 # comments terminate alias lists and continuations - print - next -} - -{ - # is this line a continuation line? - # (the prev. line ended in a backslash or the line starts with whitespace) - is_cont = /^[ \t]/ || cont - - # detect if the line is a line to be continued (ends with a backslash) - cont = ($0 ~ /\\$/) - # if it is, we drop the backslash from the line. - if (cont) sub(/[ \t]*\\$/, "", $0) -} - -is_cont { - # we ignore the line as it has been rewritten previously or is not - # interesting - next -} - -$1 == ENVIRON["__object_id"] { - # "target" user -> rewrite aliases list - select = 1 - if (mode) write_aliases() - next -} - -{ - # other user - select = 0 - print -} - -END { - # if the last line was an alias, the separator will be reused (looks better) - if (mode && !aliases_written) - write_aliases() -} -' <"${aliases_file}" >"${aliases_file}.tmp" || { - rm -f "${aliases_file}.tmp" +awk '$(drop_awk_comments "${__type}/files/update_aliases.awk")' <'${aliases_file}' >'${aliases_file}.tmp' \ +|| { + rm -f '${aliases_file}.tmp' echo 'Generating new aliases file failed!' >&2 exit 1 } -if ! cmp -s "${aliases_file}" "${aliases_file}.tmp" +if ! cmp -s '${aliases_file}' '${aliases_file}.tmp' then - # aliases file was modified, replace and run `newaliases`. - mv "${aliases_file}.tmp" "${aliases_file}" + # aliases file was modified, replace: + cat '${aliases_file}.tmp' >'${aliases_file}' - # run newaliases if present + # then, run newaliases if present ("missing" on Alpine Linux because of typo) command -v newaliases >/dev/null 2>&1 && newaliases || true -else - # no modifications were made, delete the temp file. - rm "${aliases_file}.tmp" fi +rm -f '${aliases_file}.tmp' EOF From 2270c32ddb7d2fa2c9bd904755e20fd0fbae13dc Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 28 Sep 2020 17:30:53 +0200 Subject: [PATCH 42/54] [type/__dma] Add missing license headers --- type/__dma/gencode-remote | 18 ++++++++++++++++++ type/__dma/manifest | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 8177de9..bcd4530 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -1,4 +1,22 @@ #!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } diff --git a/type/__dma/manifest b/type/__dma/manifest index 75e42d7..df62308 100755 --- a/type/__dma/manifest +++ b/type/__dma/manifest @@ -1,4 +1,22 @@ #!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# os=$(cat "${__global}/explorer/os") From 231f96de18cad4b53232baa504b8bf266bd015de Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 28 Sep 2020 17:37:41 +0200 Subject: [PATCH 43/54] Error if expected environment variables are unset --- type/__dma/gencode-remote | 58 ++++++++++++------------- type/__dma/manifest | 2 +- type/__dma_auth/gencode-remote | 18 ++++---- type/__mail_alias/explorer/aliases | 4 +- type/__mail_alias/explorer/aliases_file | 2 +- type/__mail_alias/gencode-remote | 18 ++++---- 6 files changed, 52 insertions(+), 50 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index bcd4530..e254323 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -23,18 +23,18 @@ drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } CONF_PATH=/etc/dma # set in Makefile # Determine mailname -if test -f "${__object}/parameter/mailname" +if test -f "${__object:?}/parameter/mailname" then - mailname=$(cat "${__object}/parameter/mailname") + mailname=$(cat "${__object:?}/parameter/mailname") else - case $(cat "${__global}/explorer/os") + case $(cat "${__global:?}/explorer/os") in (debian|devuan|ubuntu) # On Debian-like systems use /etc/mailname unless --mailname is used mailname='/etc/mailname' ;; (*) - mailname=$__target_fqdn + mailname=${__target_fqdn:?} ;; esac fi @@ -42,19 +42,19 @@ fi # Generate "should" values for config conf_should=$( - if test -s "${__object}/parameter/smarthost" + if test -s "${__object:?}/parameter/smarthost" then - printf 'SMARTHOST %s\n' "$(cat "${__object}/parameter/smarthost")" + printf 'SMARTHOST %s\n' "$(cat "${__object:?}/parameter/smarthost")" fi printf 'MAILNAME %s\n' "${mailname}" - if test -s "${__object}/explorer/auth_conf" + if test -s "${__object:?}/explorer/auth_conf" then - printf "AUTHPATH %s\n" "$(cat "${__object}/explorer/auth_conf")" + printf "AUTHPATH %s\n" "$(cat "${__object:?}/explorer/auth_conf")" fi - case $(cat "${__object}/parameter/security") + case $(cat "${__object:?}/parameter/security") in (ssl|tls) default_smtp_port=465 @@ -77,35 +77,35 @@ conf_should=$( ;; esac - if test -s "${__object}/parameter/port" + if test -s "${__object:?}/parameter/port" then - printf 'PORT %u\n' "$(cat "${__object}/parameter/port")" + printf 'PORT %u\n' "$(cat "${__object:?}/parameter/port")" elif test "${default_smtp_port}" -ne 25 # DMA uses port 25 by default then printf 'PORT %u\n' "${default_smtp_port}" fi - if test -f "${__object}/parameter/masquerade" + if test -f "${__object:?}/parameter/masquerade" then while read -r line do printf 'MASQUERADE %s\n' "${line}" - done <"${__object}/parameter/masquerade" + done <"${__object:?}/parameter/masquerade" fi - if test -f "${__object}/parameter/defer" + if test -f "${__object:?}/parameter/defer" then echo 'DEFER' fi - if test -f "${__object}/parameter/fullbounce" + if test -f "${__object:?}/parameter/fullbounce" then echo 'FULLBOUNCE' fi - if test -f "${__object}/parameter/nullclient" + if test -f "${__object:?}/parameter/nullclient" then - test -s "${__object}/parameter/smarthost" || { + test -s "${__object:?}/parameter/smarthost" || { echo '--nullclient requires a --smarthost to be defined' >&2 exit 1 } @@ -114,10 +114,10 @@ conf_should=$( fi ) # Sort conf_should to compare against "conf_is" -conf_should=$(echo "$conf_should" | sort -s -k 1,1) +conf_should=$(echo "${conf_should}" | sort -s -k 1,1) config_updated=false -if ! echo "$conf_should" | cmp -s "${__object}/explorer/conf" - +if ! echo "${conf_should}" | cmp -s "${__object:?}/explorer/conf" - then # config needs to be updated dma_conf="${CONF_PATH:?}/dma.conf" @@ -140,7 +140,7 @@ then # at the end of the file. cat <'${dma_conf}.tmp' \ +awk '$(drop_awk_comments "${__type:?}/files/update_dma_conf.awk")' '${dma_conf}' '${dma_conf}' <<'EOF' >'${dma_conf}.tmp' \ && cat '${dma_conf}.tmp' >'${dma_conf}' ${conf_should} EOF @@ -148,28 +148,28 @@ rm '${dma_conf}.tmp' CODE config_updated=true - echo 'config updated' >>"${__messages_out}" + echo 'config updated' >>"${__messages_out:?}" fi -if test -f "${__object}/parameter/send-test-email" +if test -f "${__object:?}/parameter/send-test-email" then - if grep -q '^__mail_alias/root:' "${__messages_in}" \ - || grep -q '^__dma_auth/' "${__messages_in}" \ + if grep -q '^__mail_alias/root:' "${__messages_in:?}" \ + || grep -q '^__dma_auth/' "${__messages_in:?}" \ || $config_updated then - cat <<-EOF - sendmail root <. # -os=$(cat "${__global}/explorer/os") +os=$(cat "${__global:?}/explorer/os") # Install DMA case $os diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index d8be7e8..aee4d7f 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -20,14 +20,14 @@ drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } -state_is=$(cat "${__object}/explorer/state") -state_should=$(cat "${__object}/parameter/state") +state_is=$(cat "${__object:?}/explorer/state") +state_should=$(cat "${__object:?}/parameter/state") -server=$__object_id -login=$(cat "${__object}/parameter/login") +server=${__object_id:?} +login=$(cat "${__object:?}/parameter/login") -auth_conf=$(cat "${__object}/explorer/auth_conf") +auth_conf=$(cat "${__object:?}/explorer/auth_conf") test -n "${auth_conf}" || { echo 'Cannot determine path of dma auth.conf' >&2 exit 1 @@ -46,13 +46,13 @@ in if test "${state_is}" = 'absent' then - printf 'add authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" + printf 'add authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out:?}" else - printf 'set authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" + printf 'set authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out:?}" fi ;; (absent) - printf 'delete authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out}" + printf 'delete authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out:?}" ;; (*) printf 'Invalid --state: %s.\n' "${state_should}" >&2 @@ -65,7 +65,7 @@ esac cat <'${auth_conf}.tmp' \ +awk '$(drop_awk_comments "${__type:?}/files/update_dma_auth.awk")' <'${auth_conf}' >'${auth_conf}.tmp' \ && cat '${auth_conf}.tmp' >'${auth_conf}' rm -f '${auth_conf}.tmp' EOF diff --git a/type/__mail_alias/explorer/aliases b/type/__mail_alias/explorer/aliases index 4fffd3b..5b5d68d 100755 --- a/type/__mail_alias/explorer/aliases +++ b/type/__mail_alias/explorer/aliases @@ -20,9 +20,11 @@ # Find aliases for a given user name and print the aliases (each one on a # separate line) -aliases_file=$("${__type_explorer}/aliases_file") +aliases_file=$("${__type_explorer:?}/aliases_file") test -r "${aliases_file}" || exit 0 +: "${__object_id:?}" # assert __object_id is set, because it is used in AWK + awk -F ':[ \t]*' ' function print_aliases(aliases, matches) { # prints comma-separated aliases (one per line) diff --git a/type/__mail_alias/explorer/aliases_file b/type/__mail_alias/explorer/aliases_file index a59bb99..7f09f88 100755 --- a/type/__mail_alias/explorer/aliases_file +++ b/type/__mail_alias/explorer/aliases_file @@ -28,7 +28,7 @@ check_file() { fi } -case $("${__explorer}/os") +case $("${__explorer:?}/os") in (freebsd|openbsd|solaris) check_file /etc/mail/aliases diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index e5bc2b7..7ef2f7c 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -20,7 +20,7 @@ drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } -aliases_file=$(cat "${__object}/explorer/aliases_file") +aliases_file=$(cat "${__object:?}/explorer/aliases_file") test -n "${aliases_file}" || { echo 'Could not determine aliases file path.' >&2 @@ -28,29 +28,29 @@ test -n "${aliases_file}" || { } -state_should=$(cat "${__object}/parameter/state") +state_should=$(cat "${__object:?}/parameter/state") case $state_should in (present) - if cmp -s "${__object}/explorer/aliases" "${__object}/parameter/alias" + if cmp -s "${__object:?}/explorer/aliases" "${__object:?}/parameter/alias" then # all good! exit 0 fi - if test -s "${__object}/explorer/aliases" + if test -s "${__object:?}/explorer/aliases" then - echo "update aliases" >>"$__messages_out" + echo "update aliases" >>"${__messages_out:?}" else - echo "add aliases" >>"$__messages_out" + echo "add aliases" >>"${__messages_out:?}" fi ;; (absent) # nothing to do if no aliases found. - test -s "${__object}/explorer/aliases" || exit 0 + test -s "${__object:?}/explorer/aliases" || exit 0 - echo "delete aliases" >>"$__messages_out" + echo "delete aliases" >>"${__messages_out:?}" ;; (*) printf 'Invalid --state: %s.\n' "${state_should}" >&2 @@ -61,7 +61,7 @@ esac cat <'${aliases_file}.tmp' \ +awk '$(drop_awk_comments "${__type:?}/files/update_aliases.awk")' <'${aliases_file}' >'${aliases_file}.tmp' \ || { rm -f '${aliases_file}.tmp' echo 'Generating new aliases file failed!' >&2 From 161e1e85f4b7696036bc1684426e33962d713fa7 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 28 Sep 2020 17:54:35 +0200 Subject: [PATCH 44/54] [scripts/run-shellcheck.sh] Do not shellcheck AWK and Python scripts --- scripts/run-shellcheck.sh | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/scripts/run-shellcheck.sh b/scripts/run-shellcheck.sh index 769f853..fcca722 100755 --- a/scripts/run-shellcheck.sh +++ b/scripts/run-shellcheck.sh @@ -1,21 +1,29 @@ -#!/bin/sh +#!/bin/sh -eu -SHELLCHECKCMD="shellcheck -s sh -f gcc -x" +SHELLCHECKCMD='shellcheck -s sh -f gcc -x' # Skip SC2154 for variables starting with __ since such variables are cdist # environment variables. SHELLCHECK_SKIP=': __.*is referenced but not assigned.*\[SC2154\]' -SHELLCHECKTMP=".shellcheck.tmp" +SHELLCHECKTMP='.shellcheck.tmp' # Move to top-level cdist-contrib directory. -cd $(dirname $0)/.. +cd "$(dirname $0)"/.. -check () { - find type/ -type f $1 $2 -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" - test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } +check() { + find type/ -type f "$@" -exec ${SHELLCHECKCMD} {} + \ + | grep -v "${SHELLCHECK_SKIP}" >>"${SHELLCHECKTMP}" || true } -check -path "*/explorer/*" -check -path "*/files/*" +rm -f "${SHELLCHECKTMP}" + +check -path '*/explorer/*' +check -path '*/files/*' ! -name '*.awk' ! -name '*.py' check -name manifest check -name gencode-local check -name gencode-remote + +if test -s "${SHELLCHECKTMP}" +then + cat "${SHELLCHECKTMP}" >&2 + exit 1 +fi From c6b795b3f9ac5472b00818fd53b6da02feabb52a Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 1 Oct 2020 12:55:38 +0200 Subject: [PATCH 45/54] [type/__mail_alias] Update man.rst and make --alias required --- type/__mail_alias/gencode-remote | 6 ++++++ type/__mail_alias/man.rst | 15 ++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index 7ef2f7c..3eea452 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -39,6 +39,12 @@ in exit 0 fi + test -s "${__object:?}/parameter/alias" || { + printf 'The --alias parameter is required if --state present.\n' >&2 + printf 'Use --state absent to remove all aliases.\n' >&2 + exit 1 + } + if test -s "${__object:?}/explorer/aliases" then echo "update aliases" >>"${__messages_out:?}" diff --git a/type/__mail_alias/man.rst b/type/__mail_alias/man.rst index 3782ffb..d6d9742 100644 --- a/type/__mail_alias/man.rst +++ b/type/__mail_alias/man.rst @@ -8,7 +8,7 @@ cdist-type__mail_alias - Manage mail aliases. DESCRIPTION ----------- -This cdist type allows you to configure mail aliases (/etc/mail/aliases). +This cdist type allows you to configure mail aliases (/etc/aliases). REQUIRED PARAMETERS @@ -21,11 +21,12 @@ OPTIONAL PARAMETERS state 'present' or 'absent', defaults to 'present' alias - the aliases where mail for the given user should be redirected to. - This parameter can be specified multiple times to redirect to more than one - recipient. - See the `aliases(5)` man page for the different forms this parameter can - take.. + an alias, i.e. a mail address where mail for the user should be redirected + to. + This parameter can be specified multiple times to redirect to multiple + recipients. + If ``--state`` is ``present`` this parameter is required. + See `aliases(5)` for the different forms this parameter can take. BOOLEAN PARAMETERS @@ -51,7 +52,7 @@ BUGS containing ``,`` (commas) are treated incorrectly (they are treated as two addresses/aliases.) Make sure that email addresses do not contain commas. - +- ``:include:`` directives in the aliases file are ignored by this type. SEE ALSO -------- From f202d11124347783dbd679ae521f1bcb09e73cbb Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 1 Oct 2020 13:45:20 +0200 Subject: [PATCH 46/54] [type/__mail_alias] Fix continuation line processing --- type/__mail_alias/explorer/aliases | 12 ++++-------- type/__mail_alias/files/update_aliases.awk | 8 +++----- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/type/__mail_alias/explorer/aliases b/type/__mail_alias/explorer/aliases index 5b5d68d..ac13d7c 100755 --- a/type/__mail_alias/explorer/aliases +++ b/type/__mail_alias/explorer/aliases @@ -31,7 +31,7 @@ function print_aliases(aliases, matches) { split(aliases, matches, /,[ \t]*/) for (i in matches) { gsub(/^[ \t]*|[ \t]*$/, "", matches[i]) - print matches[i] + if (matches[i]) print matches[i] } } @@ -47,14 +47,10 @@ function print_aliases(aliases, matches) { is_cont = /^[ \t]/ || cont # detect if the line is a line to be continued (ends with a backslash) - cont = ($0 ~ /\\$/) + cont = /\\$/ - # if it is, we drop the backslash from the line and skip to next line - # (the contents have been printed above if they should) - if (cont) { - sub(/[ \t]*\\$/, "", $0) - next - } + # if it is, we drop the backslash from the line + if (cont) sub(/[ \t]*\\$/, "", $0) } is_cont { diff --git a/type/__mail_alias/files/update_aliases.awk b/type/__mail_alias/files/update_aliases.awk index 87ea202..336009f 100644 --- a/type/__mail_alias/files/update_aliases.awk +++ b/type/__mail_alias/files/update_aliases.awk @@ -67,14 +67,12 @@ BEGIN { is_cont = /^[ \t]/ || cont # detect if the line is a line to be continued (ends with a backslash) - cont = ($0 ~ /\\$/) - # if it is, we drop the backslash from the line. - if (cont) sub(/[ \t]*\\$/, "", $0) + cont = /\\$/ } is_cont { - # we ignore the line as it has been rewritten previously or is not - # interesting + # we only print the line if it has not been rewritten (select) + if (!select) print next } From 1c9ab6e07b1808ce708cc9926bce4983a7faf0d7 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 1 Oct 2020 17:18:01 +0200 Subject: [PATCH 47/54] [type/__dma] Update man.rst --- type/__dma/gencode-remote | 5 ++-- type/__dma/man.rst | 56 ++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index e254323..1987106 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -66,7 +66,7 @@ conf_should=$( echo 'STARTTLS' ;; (opportunistic) - default_smtp_port=25 # XXX: correct? + default_smtp_port=25 echo 'SECURETRANSFER' echo 'STARTTLS' echo 'OPPORTUNISTIC_TLS' @@ -152,7 +152,8 @@ CODE fi -if test -f "${__object:?}/parameter/send-test-email" +# Send a test email if enabled and necessary (=configuration changed) +if test -f "${__object:?}/parameter/send-test-mail" then if grep -q '^__mail_alias/root:' "${__messages_in:?}" \ || grep -q '^__dma_auth/' "${__messages_in:?}" \ diff --git a/type/__dma/man.rst b/type/__dma/man.rst index ba4a5a6..29a71fa 100644 --- a/type/__dma/man.rst +++ b/type/__dma/man.rst @@ -8,47 +8,46 @@ cdist-type__dma - Setup the DragonFly Mail Agent as the MTA. DESCRIPTION ----------- -This (singleton) type uses dma, a small Mail Transport Agent (MTA), to accept -mails from locally installed Mail User Agents (MUA) and deliver the mails -to a remote destination. - -Remote delivery happens over TLS to one or more mailboxes that are local to the -mail server configured in the ``smarthost`` parameter. +This (singleton) type uses DMA, a small Mail Transport Agent (MTA), to accept +mails from locally installed Mail User Agents (MUA) and either deliver the mails +to a remote smart host for delivery or communicate with remote SMTP servers +directly. REQUIRED PARAMETERS ------------------- -smarthost - The mail server used to send email. - It must be configured to act as a relay for the host being configured by - this type so that mail can be sent to users non-local to the smarthost. +None. BOOLEAN PARAMETERS ------------------ defer - If enabled, the mail queue has to be manually flushed with the `-q` option. -full-bounce - Enable if the bounce message should include the complete original message, + If enabled, mail will not be sent immediately, but stored in a queue. + To flush the queue and send the mails, ```dma -q`` has to be run + periodically (e.g. using a cron job.) + This type does not manage such a cron job, but some operating systems ship + such a cron job with the package. +fullbounce + Enable if bounce messages should include the complete original message, not just the headers. -null-client +nullclient Enable to bypass aliases and local delivery, and instead forward all mails to the defined ``--smarthost``. -send-test-email - If present, after setup this type will send an email to root, to allow you - to easily test your setup. +send-test-mail + If set, this type will send a test email to root after setup, to check if + the configured settings work. OPTIONAL PARAMETERS ------------------- mailname If present, this will be the hostname used to identify this host and the - remote part of the from addresses. - If not defined, it defaults to `/etc/mailname` on Debian derivatives and to - `__target_fqdn` otherwise. + remote part of the sender addresses. + If not defined, it defaults to ``/etc/mailname`` on Debian derivatives and + to ``__target_fqdn`` otherwise. See `dma(8)` for more information. - Note: on Debian derivatives the `/etc/mailname` file should be updated + Note: on Debian derivatives the ``/etc/mailname`` file should be updated instead of using this parameter. masquerade Masquerade the envelope-from addresses with this address/hostname. @@ -59,7 +58,7 @@ masquerade port The port on which to deliver email. If not provided, a sensible default port will be used based on the - `--security` argument. + ``--security`` argument. security Configures whether and how DMA should use secure connections. @@ -74,14 +73,23 @@ security messages directly to the outside mail exchangers. insecure allow plain text SMTP login over an insecure connection. - Should really not be used anymore! + Should really *not* be used anymore! +smarthost + The mail server used to send email. + It must be configured to act as a relay for the host being configured by + this type so that mail can be sent to users non-local to the smarthost. + EXAMPLES -------- .. code-block:: sh - __dma --smarthost mx1.domain.tld --send-test-email + # Install DMA and use the smarthost mx1.domain.tld to send mail. + __dma --smarthost mx1.domain.tld --send-test-mail + + # Install DMA in a default configuration. + __dma SEE ALSO From f76bcd35742d4dc0db2af247b2be51fc6ae6132c Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 1 Oct 2020 17:21:19 +0200 Subject: [PATCH 48/54] [type/__dma_auth] Update man.rst --- type/__dma_auth/man.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/type/__dma_auth/man.rst b/type/__dma_auth/man.rst index f56bfb7..da76883 100644 --- a/type/__dma_auth/man.rst +++ b/type/__dma_auth/man.rst @@ -27,7 +27,7 @@ password OPTIONAL PARAMETERS ------------------- state - Either `present` or `absent`. Defaults to `present`. + Either ``present`` or ``absent``. Defaults to ``present``. BOOLEAN PARAMETERS ------------------ From 04076a75eb728b8d4ad50b31c1b0bd87a9d781a8 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 16 Nov 2020 13:57:34 +0100 Subject: [PATCH 49/54] [type/__mail_alias] man.rst: Make bugs a list --- type/__mail_alias/man.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/type/__mail_alias/man.rst b/type/__mail_alias/man.rst index d6d9742..de40512 100644 --- a/type/__mail_alias/man.rst +++ b/type/__mail_alias/man.rst @@ -48,11 +48,15 @@ EXAMPLES BUGS ---- -- Quoted strings are not parsed by this type. As a result, email addresses - containing ``,`` (commas) are treated incorrectly (they are treated as two - addresses/aliases.) - Make sure that email addresses do not contain commas. -- ``:include:`` directives in the aliases file are ignored by this type. +- Quoted strings are not parsed by this type. As a result, aliases + containing ``,`` (commas) are treated incorrectly (they are treated as + separate aliases.) + Make sure that email addresses, file names, and pipe commands do not contain + commas. +- ``:include:`` directives in the aliases file are not evaluated by this type. + They are treated like a regular alias, the values of the included file are + not expanded. + SEE ALSO -------- From 7e20d13b9ff5126580c0ac9a9231c2ebea966985 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 16 Nov 2020 13:58:05 +0100 Subject: [PATCH 50/54] [type/__mail_alias] Use explicit line variables in update_aliases.awk --- type/__mail_alias/files/update_aliases.awk | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/type/__mail_alias/files/update_aliases.awk b/type/__mail_alias/files/update_aliases.awk index 336009f..11a4c85 100644 --- a/type/__mail_alias/files/update_aliases.awk +++ b/type/__mail_alias/files/update_aliases.awk @@ -18,11 +18,11 @@ # along with cdist. If not, see . # -function getvalue(path) { +function getvalue(path, line) { # Reads the first line of the file located at path and returns it. - getline < path + getline line < path close(path) - return $0 + return line } function sepafter(f, def, _) { @@ -31,14 +31,14 @@ function sepafter(f, def, _) { return _ ? _ : def } -function write_aliases() { +function write_aliases( line) { if (aliases_written) return # print aliases line printf "%s%s", ENVIRON["__object_id"], sepafter(1, ": ") - while ((getline < aliases_should_file) > 0) { + while ((getline line < aliases_should_file) > 0) { if (aliases_written) printf ", " - printf "%s", $0 + printf "%s", line aliases_written = 1 } printf "\n" From 0932c9ccde66bdcb75e998f7a730624fdbd3068e Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 16 Nov 2020 14:10:30 +0100 Subject: [PATCH 51/54] [type/{__dma,__dma_auth,__mail_alias}] Quote things properly --- type/__dma/files/update_dma_conf.awk | 2 +- type/__dma/gencode-remote | 17 +++++++++-------- type/__dma_auth/gencode-remote | 11 ++++++----- type/__mail_alias/gencode-remote | 17 +++++++++-------- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/type/__dma/files/update_dma_conf.awk b/type/__dma/files/update_dma_conf.awk index 67661fd..2f60a3d 100644 --- a/type/__dma/files/update_dma_conf.awk +++ b/type/__dma/files/update_dma_conf.awk @@ -18,7 +18,7 @@ # along with cdist. If not, see . function comment_line(line) { - # returns the position in line at which the comment'\''s text starts + # returns the position in line at which the comment's text starts # (0 if the line is not a comment) match(line, /^[ \t]*\#+[ \t]*/) return RSTART ? (RLENGTH + 1) : 0 diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index 1987106..a33388d 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -18,7 +18,8 @@ # along with cdist. If not, see . # -drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } +quote() { printf "'%s'" "$(printf '%s' "$*" | sed -e "s/'/'\\\\''/g")"; } +drop_awk_comments() { quote "$(sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@")"; } CONF_PATH=/etc/dma # set in Makefile @@ -139,13 +140,13 @@ then # options are grouped by word (the first word in the line) and appended # at the end of the file. - cat <'${dma_conf}.tmp' \ -&& cat '${dma_conf}.tmp' >'${dma_conf}' -${conf_should} -EOF -rm '${dma_conf}.tmp' -CODE + cat <<-CODE + awk $(drop_awk_comments "${__type:?}/files/update_dma_conf.awk") $(quote "${dma_conf}") $(quote "${dma_conf}") <<'EOF' >$(quote "${dma_conf}.tmp") \ + && cat $(quote "${dma_conf}.tmp") >$(quote "${dma_conf}") + ${conf_should} + EOF + rm $(quote "${dma_conf}.tmp") + CODE config_updated=true echo 'config updated' >>"${__messages_out:?}" diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index aee4d7f..4279b7a 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -18,7 +18,8 @@ # along with cdist. If not, see . # -drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } +quote() { printf "'%s'" "$(printf '%s' "$*" | sed -e "s/'/'\\\\''/g")"; } +drop_awk_comments() { quote "$(sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@")"; } state_is=$(cat "${__object:?}/explorer/state") state_should=$(cat "${__object:?}/parameter/state") @@ -63,9 +64,9 @@ esac cat <'${auth_conf}.tmp' \ -&& cat '${auth_conf}.tmp' >'${auth_conf}' -rm -f '${auth_conf}.tmp' +awk $(drop_awk_comments "${__type:?}/files/update_dma_auth.awk") <$(quote "${auth_conf}") >$(quote "${auth_conf}.tmp") \ +&& cat $(quote "${auth_conf}.tmp") >$(quote "${auth_conf}") +rm -f $(quote "${auth_conf}.tmp") EOF diff --git a/type/__mail_alias/gencode-remote b/type/__mail_alias/gencode-remote index 3eea452..4a8f889 100755 --- a/type/__mail_alias/gencode-remote +++ b/type/__mail_alias/gencode-remote @@ -18,7 +18,8 @@ # along with cdist. If not, see . # -drop_awk_comments() { sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@"; } +quote() { printf "'%s'" "$(printf '%s' "$*" | sed -e "s/'/'\\\\''/g")"; } +drop_awk_comments() { quote "$(sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@")"; } aliases_file=$(cat "${__object:?}/explorer/aliases_file") @@ -30,7 +31,7 @@ test -n "${aliases_file}" || { state_should=$(cat "${__object:?}/parameter/state") -case $state_should +case ${state_should} in (present) if cmp -s "${__object:?}/explorer/aliases" "${__object:?}/parameter/alias" @@ -65,22 +66,22 @@ in esac cat <'${aliases_file}.tmp' \ +awk $(drop_awk_comments "${__type:?}/files/update_aliases.awk") <$(quote "${aliases_file}") >$(quote "${aliases_file}.tmp") \ || { - rm -f '${aliases_file}.tmp' + rm -f $(quote "${aliases_file}.tmp") echo 'Generating new aliases file failed!' >&2 exit 1 } -if ! cmp -s '${aliases_file}' '${aliases_file}.tmp' +if ! cmp -s $(quote "${aliases_file}") $(quote "${aliases_file}.tmp") then # aliases file was modified, replace: - cat '${aliases_file}.tmp' >'${aliases_file}' + cat $(quote "${aliases_file}.tmp") >$(quote "${aliases_file}") # then, run newaliases if present ("missing" on Alpine Linux because of typo) command -v newaliases >/dev/null 2>&1 && newaliases || true fi -rm -f '${aliases_file}.tmp' +rm -f $(quote "${aliases_file}.tmp") EOF From 487574c865c0b150d83bc37bfda44c0051f91973 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 16 Nov 2020 14:15:52 +0100 Subject: [PATCH 52/54] [type/__dma] Convert AWK to loop over same file twice --- type/__dma/files/update_dma_conf.awk | 8 ++++++++ type/__dma/gencode-remote | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/type/__dma/files/update_dma_conf.awk b/type/__dma/files/update_dma_conf.awk index 2f60a3d..15ef7bf 100644 --- a/type/__dma/files/update_dma_conf.awk +++ b/type/__dma/files/update_dma_conf.awk @@ -88,6 +88,12 @@ BEGIN { FS = "\n" EQS = "[ \t]" # copied from dma/conf.c + if (ARGV[2]) exit (e=1) + + # Loop over file twice! + ARGV[2] = ARGV[1] + ARGC++ + # read the "should" state into the `conf` array. while (getline < "/dev/stdin") { word = first($0, EQS) @@ -165,6 +171,8 @@ NR > FNR { } END { + if (e) exit + # print rest of config options ( for (word in conf) print_confs(word) } diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index a33388d..fa676d4 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -141,7 +141,7 @@ then # at the end of the file. cat <<-CODE - awk $(drop_awk_comments "${__type:?}/files/update_dma_conf.awk") $(quote "${dma_conf}") $(quote "${dma_conf}") <<'EOF' >$(quote "${dma_conf}.tmp") \ + awk $(drop_awk_comments "${__type:?}/files/update_dma_conf.awk") $(quote "${dma_conf}") <<'EOF' >$(quote "${dma_conf}.tmp") \ && cat $(quote "${dma_conf}.tmp") >$(quote "${dma_conf}") ${conf_should} EOF From f9f5c578f744cd157c138fca09ff84616cf805b6 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 11 Jan 2021 12:16:09 +0100 Subject: [PATCH 53/54] [type/__dma*] Fix shellcheck errors --- type/__dma/gencode-remote | 2 +- type/__dma/manifest | 2 +- type/__dma_auth/explorer/state | 2 +- type/__dma_auth/gencode-remote | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/type/__dma/gencode-remote b/type/__dma/gencode-remote index fa676d4..580b22e 100755 --- a/type/__dma/gencode-remote +++ b/type/__dma/gencode-remote @@ -158,7 +158,7 @@ if test -f "${__object:?}/parameter/send-test-mail" then if grep -q '^__mail_alias/root:' "${__messages_in:?}" \ || grep -q '^__dma_auth/' "${__messages_in:?}" \ - || $config_updated + || ${config_updated} then cat <<-CODE sendmail root <<'EOF' diff --git a/type/__dma/manifest b/type/__dma/manifest index 2cbd1a5..530ad09 100755 --- a/type/__dma/manifest +++ b/type/__dma/manifest @@ -21,7 +21,7 @@ os=$(cat "${__global:?}/explorer/os") # Install DMA -case $os +case ${os} in (alpine) __package dma --state present diff --git a/type/__dma_auth/explorer/state b/type/__dma_auth/explorer/state index 621e5a2..c829cd4 100755 --- a/type/__dma_auth/explorer/state +++ b/type/__dma_auth/explorer/state @@ -25,7 +25,7 @@ # different_password: a line exists but with a different password # multiple: multiple lines matching host exist (should not happen) -auth_conf=$("${__type_explorer}/auth_conf") +auth_conf=$("${__type_explorer:?}/auth_conf") test -r "${auth_conf}" || exit 0 awk -F'\n' ' diff --git a/type/__dma_auth/gencode-remote b/type/__dma_auth/gencode-remote index 4279b7a..b6a0100 100755 --- a/type/__dma_auth/gencode-remote +++ b/type/__dma_auth/gencode-remote @@ -40,7 +40,7 @@ then exit 0 fi -case $state_should +case ${state_should} in (present) test -n "${login}" || { echo '--login must be non-empty' >&2; exit 1; } From 7cef989b1fb4c92e53c20768d65c5e8d4f3406eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Floure?= Date: Tue, 12 Jan 2021 07:14:24 +0100 Subject: [PATCH 54/54] Fix run-shellcheck.sh following 'conflict resolution' from gitlab web ui --- scripts/run-shellcheck.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run-shellcheck.sh b/scripts/run-shellcheck.sh index fcca722..d6c2db6 100755 --- a/scripts/run-shellcheck.sh +++ b/scripts/run-shellcheck.sh @@ -17,7 +17,7 @@ check() { rm -f "${SHELLCHECKTMP}" check -path '*/explorer/*' -check -path '*/files/*' ! -name '*.awk' ! -name '*.py' +check -path '*/files/*' -name '*.sh' check -name manifest check -name gencode-local check -name gencode-remote