| #!/bin/bash |
| shopt -s nullglob |
| |
| # We want to iterate over all system users, check if they are opted-in to ssh |
| # authorized_keys building, and then construct their keyfile |
| for user in $(cut -d':' -f1 /etc/passwd); do |
| home="$(eval echo ~$user)" || continue |
| link="$(readlink $home/.ssh/authorized_keys 2>/dev/null)" || continue |
| # Users are only opted-in if they symlink to our well-known directory where |
| # the final output of this script lives. |
| if [ "$link" != "/run/authorized_keys/$user" ]; then |
| echo "Ignoring $user $home/.ssh/authorized_keys" >&2 |
| continue |
| fi |
| |
| echo "Updating $link" >&2 |
| declare -A basemap=() |
| declare -a dirs=( |
| "/usr/share/authorized_keys.d/$user" |
| "$home/.ssh/authorized_keys.d" |
| "/run/authorized_keys.d/$user" |
| ) |
| # Build a map that can be used for sorting directories by their priority |
| # and prioritizing the last listed directories over the later ones. We |
| # append a counter to ensure that there is a stable sorting mechanism for |
| # duplicate filenames. Duplicate filenames will be overridden by higher |
| # priority directories. |
| # Ex. |
| # /usr/share/authorized_keys.d/root/10-key |
| # /usr/share/authorized_keys.d/root/15-key |
| # /run/authorized_keys.d/root/10-key |
| # /run/authorized_keys.d/root/20-key |
| # Becomes |
| # ["10-key"]="/run/authorized_keys.d/root/10-key" |
| # ["15-key"]="/usr/share/authorized_keys.d/root/15-key" |
| # ["20-key"]="/run/authorized_keys.d/root/20-key" |
| for dir in "${dirs[@]}"; do |
| for file in "$dir"/*; do |
| basemap["${file##*/}"]="$file" |
| done |
| done |
| rm -f /run/authorized_keys.tmp |
| touch /run/authorized_keys.tmp |
| for key in $(printf "%s\n" "${!basemap[@]}" | sort -r); do |
| echo " Including ${basemap[$key]}" >&2 |
| cat "${basemap[$key]}" >>/run/authorized_keys.tmp |
| done |
| mkdir -p /run/authorized_keys |
| mv /run/authorized_keys.tmp /run/authorized_keys/$user |
| chown $user /run/authorized_keys/$user |
| done |