Merge branch 'dma-type' into 'master'

Add __mail_alias, __dma* types

See merge request ungleich-public/cdist-contrib!6
This commit is contained in:
fnux 2021-01-12 07:15:55 +01:00
commit f4375dbbb9
30 changed files with 1357 additions and 9 deletions

View File

@ -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/*.sh"
rm -f "${SHELLCHECKTMP}"
check -path '*/explorer/*'
check -path '*/files/*' -name '*.sh'
check -name manifest
check -name gencode-local
check -name gencode-remote
if test -s "${SHELLCHECKTMP}"
then
cat "${SHELLCHECKTMP}" >&2
exit 1
fi

49
type/__dma/explorer/auth_conf Executable file
View File

@ -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 <http://www.gnu.org/licenses/>.
#
# 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

34
type/__dma/explorer/conf Executable file
View File

@ -0,0 +1,34 @@
#!/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 <http://www.gnu.org/licenses/>.
#
# 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"
test -f "${dma_conf}" || exit 0
grep -v -e '^[ \t]*#\|^$' "${dma_conf}" \
| sed -e 's/[ \t]*#.*$//' \
| sort -s -k 1,1

View File

@ -0,0 +1,178 @@
#!/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 <http://www.gnu.org/licenses/>.
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
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)
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 {
if (e) exit
# print rest of config options (
for (word in conf) print_confs(word)
}

177
type/__dma/gencode-remote Executable file
View File

@ -0,0 +1,177 @@
#!/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 <http://www.gnu.org/licenses/>.
#
quote() { printf "'%s'" "$(printf '%s' "$*" | sed -e "s/'/'\\\\''/g")"; }
drop_awk_comments() { quote "$(sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@")"; }
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 "should" values for config
conf_should=$(
if test -s "${__object:?}/parameter/smarthost"
then
printf 'SMARTHOST %s\n' "$(cat "${__object:?}/parameter/smarthost")"
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")
in
(ssl|tls)
default_smtp_port=465
echo 'SECURETRANSFER'
;;
(starttls)
default_smtp_port=587
echo 'SECURETRANSFER'
echo 'STARTTLS'
;;
(opportunistic)
default_smtp_port=25
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 # DMA uses port 25 by default
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
if test -f "${__object:?}/parameter/defer"
then
echo 'DEFER'
fi
if test -f "${__object:?}/parameter/fullbounce"
then
echo 'FULLBOUNCE'
fi
if test -f "${__object:?}/parameter/nullclient"
then
test -s "${__object:?}/parameter/smarthost" || {
echo '--nullclient requires a --smarthost to be defined' >&2
exit 1
}
echo 'NULLCLIENT'
fi
)
# Sort conf_should to compare against "conf_is"
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
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 <<-CODE
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
rm $(quote "${dma_conf}.tmp")
CODE
config_updated=true
echo 'config updated' >>"${__messages_out:?}"
fi
# 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:?}" \
|| ${config_updated}
then
cat <<-CODE
sendmail root <<'EOF'
Subject: [cdist] Test mail from '${__target_fqdn:?}'
Hi,
you can ignore this message.
Its sole purpose is to notify you that root mail on ${__target_fqdn:?}
will be redirected to you.
Enjoy!
EOF
CODE
fi
fi

112
type/__dma/man.rst Normal file
View File

@ -0,0 +1,112 @@
cdist-type__dma(7)
============================
NAME
----
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 either deliver the mails
to a remote smart host for delivery or communicate with remote SMTP servers
directly.
REQUIRED PARAMETERS
-------------------
None.
BOOLEAN PARAMETERS
------------------
defer
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.
nullclient
Enable to bypass aliases and local delivery, and instead forward all mails
to the defined ``--smarthost``.
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 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
instead of using this parameter.
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!
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
# 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
--------
- `DragonFly Mail Agent <https://github.com/corecode/dma>`_
- `DragonFly Handbook MTA <https://www.dragonflybsd.org/handbook/mta/>`_
AUTHORS
-------
Evilham <contact@evilham.com>
Dennis Camera <dennis.camera@ssrq-sds-fds.ch>
COPYING
-------
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.

66
type/__dma/manifest Executable file
View File

@ -0,0 +1,66 @@
#!/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 <http://www.gnu.org/licenses/>.
#
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'
;;
(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 - <<-'EOF'
#
# Execute the "real" sendmail program, named /usr/libexec/sendmail/sendmail
#
sendmail /usr/libexec/dma
send-mail /usr/libexec/dma
mailq /usr/libexec/dma
newaliases /usr/libexec/dma
rmail /usr/libexec/dma
EOF
;;
(*)
cat <<EOF >&2
Your OS (${os}) is not supported yet.
Maybe adding support is as simple as adapting the packages or allowing it,
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

View File

@ -0,0 +1,4 @@
defer
fullbounce
nullclient
send-test-mail

View File

@ -0,0 +1 @@
opportunistic

View File

@ -0,0 +1,4 @@
mailname
port
security
smarthost

View File

@ -0,0 +1 @@
masquerade

0
type/__dma/singleton Normal file
View File

View File

@ -0,0 +1 @@
../../__dma/explorer/auth_conf

91
type/__dma_auth/explorer/state Executable file
View File

@ -0,0 +1,91 @@
#!/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 <http://www.gnu.org/licenses/>.
#
# 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_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
awk -F'\n' '
function getvalue(path) {
# Reads the first line of the file located at path and returns it.
getline < path
close(path)
return $0
}
BEGIN {
DP = "[: \t]" # copied from dma/conf.c
parameter_dir = ENVIRON["__object"] "/parameter/"
# Read the parameters of this object
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
}
{
# 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 + 1)
host = substr(host, 1, RSTART - 1)
} else {
passwd = ""
}
}
host == host_param {
# a match…
if (state == "absent") {
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"
}
}
END {
print state
}
' "${auth_conf}"

View File

@ -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 <http://www.gnu.org/licenses/>.
#
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()
}
}

72
type/__dma_auth/gencode-remote Executable file
View File

@ -0,0 +1,72 @@
#!/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 <http://www.gnu.org/licenses/>.
#
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")
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
exit 0
fi
case ${state_should}
in
(present)
test -n "${login}" || { echo '--login must be non-empty' >&2; exit 1; }
if test "${state_is}" = 'absent'
then
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)
printf 'delete authuser %s on %s\n' "${login}" "${server}" >>"${__messages_out:?}"
;;
(*)
printf 'Invalid --state: %s.\n' "${state_should}" >&2
printf 'Acceptable values are: present, absent.\n' >&2
exit 1
;;
esac
cat <<EOF
test -f $(quote "${auth_conf}") || touch $(quote "${auth_conf}")
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

66
type/__dma_auth/man.rst Normal file
View File

@ -0,0 +1,66 @@
cdist-type__dma_auth(7)
=======================
NAME
----
cdist-type__dma_auth - Configure SMTP logins for the DragonFly Mail Agent MTA.
DESCRIPTION
-----------
This cdist type allows you to set up credentials to log in to remote SMTP
servers.
NB: dma currently (v0.13) does not differentiate between users on a host.
It will use whatever user it finds in the ``auth.conf`` first.
Thus, this type will use the ``__object_id`` as the host specifier.
REQUIRED PARAMETERS
-------------------
login
The user's LOGIN name on the SMTP server.
password
The user's password (in plain text.)
OPTIONAL PARAMETERS
-------------------
state
Either ``present`` or ``absent``. Defaults to ``present``.
BOOLEAN PARAMETERS
------------------
None.
EXAMPLES
--------
.. code-block:: sh
# Set the password for smarthost
__dma_auth smarthost.example.com --login joe --password hunter2
# Set credentials for user at an external provider
__dma_auth mail.provider.com --login paul@example.com --password letmein
# Delete credentials for example.com (for all users)
__dma_auth example.com --login '' --password '' --state absent
SEE ALSO
--------
:strong:`cdist-type__dma`\ (7), :strong:`dma`\ (8)
AUTHORS
-------
Dennis Camera <dennis.camera@ssrq-sds-fds.ch>
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.

View File

View File

@ -0,0 +1 @@
present

View File

@ -0,0 +1 @@
state

View File

@ -0,0 +1,2 @@
login
password

View File

@ -0,0 +1,73 @@
#!/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 <http://www.gnu.org/licenses/>.
#
# 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
: "${__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)
split(aliases, matches, /,[ \t]*/)
for (i in matches) {
gsub(/^[ \t]*|[ \t]*$/, "", matches[i])
if (matches[i]) print matches[i]
}
}
/^#/ {
# comment line (ignore)
select = 0; cont = 0 # comments terminate alias lists and continuations
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 = /\\$/
# if it is, we drop the backslash from the line
if (cont) sub(/[ \t]*\\$/, "", $0)
}
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
select = 1
print_aliases($2)
next
}
{
# other user
select = 0
}
' "${aliases_file}"

View File

@ -0,0 +1,52 @@
#!/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 <http://www.gnu.org/licenses/>.
#
# This explorer finds the aliases file to modify.
found() { echo "$*"; exit 0; }
check_file() {
if test -f "$1"
then
found "$1"
fi
}
case $("${__explorer:?}/os")
in
(freebsd|openbsd|solaris)
check_file /etc/mail/aliases
# default
found /etc/mail/aliases
;;
(alpine|debian|devuan|ubuntu)
check_file /etc/aliases
# default
found /etc/aliases
;;
(*)
check_file /etc/mail/aliases
check_file /etc/aliases
# default
found /etc/aliases
;;
esac

View File

@ -0,0 +1,96 @@
#!/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 <http://www.gnu.org/licenses/>.
#
function getvalue(path, line) {
# Reads the first line of the file located at path and returns it.
getline line < path
close(path)
return line
}
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( line) {
if (aliases_written) return
# print aliases line
printf "%s%s", ENVIRON["__object_id"], sepafter(1, ": ")
while ((getline line < aliases_should_file) > 0) {
if (aliases_written) printf ", "
printf "%s", line
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 = /\\$/
}
is_cont {
# we only print the line if it has not been rewritten (select)
if (!select) print
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()
}

View File

@ -0,0 +1,87 @@
#!/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 <http://www.gnu.org/licenses/>.
#
quote() { printf "'%s'" "$(printf '%s' "$*" | sed -e "s/'/'\\\\''/g")"; }
drop_awk_comments() { quote "$(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}
in
(present)
if cmp -s "${__object:?}/explorer/aliases" "${__object:?}/parameter/alias"
then
# all good!
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:?}"
else
echo "add aliases" >>"${__messages_out:?}"
fi
;;
(absent)
# nothing to do if no aliases found.
test -s "${__object:?}/explorer/aliases" || exit 0
echo "delete aliases" >>"${__messages_out:?}"
;;
(*)
printf 'Invalid --state: %s.\n' "${state_should}" >&2
printf 'Acceptable values are: present, absent.\n' >&2
exit 1
esac
cat <<EOF
test -f $(quote "${aliases_file}") || touch $(quote "${aliases_file}")
awk $(drop_awk_comments "${__type:?}/files/update_aliases.awk") <$(quote "${aliases_file}") >$(quote "${aliases_file}.tmp") \
|| {
rm -f $(quote "${aliases_file}.tmp")
echo 'Generating new aliases file failed!' >&2
exit 1
}
if ! cmp -s $(quote "${aliases_file}") $(quote "${aliases_file}.tmp")
then
# aliases file was modified, replace:
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 $(quote "${aliases_file}.tmp")
EOF

76
type/__mail_alias/man.rst Normal file
View File

@ -0,0 +1,76 @@
cdist-type__mail_alias(7)
=========================
NAME
----
cdist-type__mail_alias - Manage mail aliases.
DESCRIPTION
-----------
This cdist type allows you to configure mail aliases (/etc/aliases).
REQUIRED PARAMETERS
-------------------
None.
OPTIONAL PARAMETERS
-------------------
state
'present' or 'absent', defaults to 'present'
alias
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
------------------
None.
EXAMPLES
--------
.. code-block:: sh
# 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
BUGS
----
- 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
--------
:strong:`aliases`\ (5)
AUTHORS
-------
Dennis Camera <dennis.camera@ssrq-sds-fds.ch>
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.

View File

View File

@ -0,0 +1 @@
present

View File

@ -0,0 +1 @@
state

View File

@ -0,0 +1 @@
alias