Dotfiles/setup/modules/FreeipaAnsible/ansible/policies.d/usr_ctl_dnshostfile.sh

73 lines
3.1 KiB
Bash

#!/usr/bin/env bash
# policy: usr_ctl_dnshostfile — grant specific IPA users write access to
# /etc/hosts via a POSIX ACL. Default (no membership) leaves /etc/hosts at
# its normal root-only permissions — membership is what GRANTS the right,
# it does not restrict anything for non-members.
#
# Scope: /etc/hosts only, deliberately not /etc/resolv.conf. On systems using
# systemd-resolved, /etc/resolv.conf is usually a symlink to a stub file on
# tmpfs that gets regenerated on boot/network changes, so a static ACL there
# would silently vanish; covering it properly would mean resolving and
# re-applying to whatever the symlink currently targets on every run, which
# is a deliberate follow-up rather than something folded in here silently.
#
# Requires: the 'acl' package (setfacl/getfacl) on the client.
# Security note: this is a real grant, not a cosmetic one — a member can
# redirect any hostname to any IP on this machine (local DNS override), which
# is enough to locally spoof a login page or update server. Grant deliberately.
HOSTS_FILE="/etc/hosts"
DNSHOST_STATE="$STATE_DIR/dnshostfile-acl-users"
[[ -f "$DNSHOST_STATE" ]] || touch "$DNSHOST_STATE"
if ! command -v setfacl &>/dev/null; then
warn "usr_ctl_dnshostfile: setfacl not found (install the 'acl' package) — skipping"
else
_dnshost_grp_exists=false
grep -qxF "usr_ctl_dnshostfile" <<< "$_ALL_USER_GROUPS" && _dnshost_grp_exists=true
_DNSHOST_USERS=()
if [[ "$_dnshost_grp_exists" == true ]]; then
while IFS= read -r _u; do
[[ -z "$_u" ]] && continue
_DNSHOST_USERS+=("$_u")
done < <(_ipa_group_member_users usr_ctl_dnshostfile)
fi
_dnshost_in_desired() {
local n="$1"
for _d in "${_DNSHOST_USERS[@]+"${_DNSHOST_USERS[@]}"}"; do [[ "$_d" == "$n" ]] && return 0; done
return 1
}
# Grant: add an ACL entry for every currently-desired user (idempotent).
for _u in "${_DNSHOST_USERS[@]+"${_DNSHOST_USERS[@]}"}"; do
getent passwd "$_u" &>/dev/null || continue
if ! getfacl "$HOSTS_FILE" 2>/dev/null | grep -qxF "user:${_u}:rw-"; then
setfacl -m "u:${_u}:rw" "$HOSTS_FILE" \
&& log "usr_ctl_dnshostfile: granted $_u write access to $HOSTS_FILE" \
|| warn "usr_ctl_dnshostfile: setfacl grant failed for $_u"
fi
done
# Revert: remove the ACL entry for anyone previously granted who is no
# longer in the group (or the group was deleted entirely).
_NEW_DNSHOST_STATE=()
while IFS= read -r _old_u; do
[[ -z "$_old_u" ]] && continue
if _dnshost_in_desired "$_old_u"; then
_NEW_DNSHOST_STATE+=("$_old_u")
else
setfacl -x "u:${_old_u}" "$HOSTS_FILE" 2>/dev/null \
&& log "usr_ctl_dnshostfile: revoked $_old_u write access to $HOSTS_FILE"
fi
done < "$DNSHOST_STATE"
if [[ ${#_DNSHOST_USERS[@]} -gt 0 ]]; then
printf '%s\n' "${_DNSHOST_USERS[@]}" | sort -u > "$DNSHOST_STATE"
else
> "$DNSHOST_STATE"
fi
unset _dnshost_grp_exists _DNSHOST_USERS _u _NEW_DNSHOST_STATE _old_u
fi