73 lines
3.1 KiB
Bash
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
|