Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: validate the authorized_keys file when running the list subcommand #177

Merged
merged 2 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,23 @@ no-agent-forwarding,no-user-rc,no-X11-forwarding,no-port-forwarding

This can be useful for cases where the ssh server does not allow certain options or you
wish to further constrain a user's environment. Please see `man sshd` for more information.

Existing keys can be listed via the `list` subcommand:

```shell
# in text format
sshcommand list cmd

# filter by a particular name
sshcommand list cmd progrium

# in json format
sshcommand list cmd "" json

# with name filtering
sshcommand list cmd progrium json

# ignore validation errors (though they will be printed to stderr)
export SSHCOMMAND_IGNORE_LIST_WARNINGS=true
sshcommand list cmd
```
43 changes: 43 additions & 0 deletions sshcommand
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,50 @@ fn-adduser() {
esac
}

fn-verify-file() {
declare desc="Test that public key is valid"
declare file="$1"
local has_errors=false

local key line=0
local TMP_KEY_FILE
TMP_KEY_FILE=$(mktemp "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX")
# shellcheck disable=SC2064
trap "rm -rf '$TMP_KEY_FILE' >/dev/null" RETURN INT TERM EXIT

SSHCOMMAND_IGNORE_LIST_WARNINGS="${SSHCOMMAND_IGNORE_LIST_WARNINGS:-false}"
while read -r key; do
line=$((line + 1))
[[ -z "$key" ]] && continue
[[ "$key" =~ ^#.*$ ]] && continue

echo "$key" >"$TMP_KEY_FILE"
if ! ssh-keygen -lf "$TMP_KEY_FILE" &>/dev/null; then
has_errors=true
if [[ "$SSHCOMMAND_IGNORE_LIST_WARNINGS" == "false" ]]; then
log-warn "${file} line $line failed ssh-keygen check."
else
log-warn "${file} line $line failed ssh-keygen check, ignoring."
fi
fi
done <"${file}"

if [[ "$has_errors" == "true" ]]; then
return 1
fi
}

log-fail() {
declare desc="Log fail formatter"
echo "$@" 1>&2
exit 1
}

log-warn() {
declare desc="Log warn formatter"
echo "$@" 1>&2
}

log-verbose() {
declare desc="Log verbose formatter"
if [[ -n "$SSHCOMMAND_VERBOSE_OUTPUT" ]]; then
Expand Down Expand Up @@ -209,6 +247,11 @@ sshcommand-list() {
[[ -e "$userhome/.ssh/authorized_keys" ]] || log-fail "authorized_keys not found for $USER"
[[ -s "$userhome/.ssh/authorized_keys" ]] || log-fail "authorized_keys is empty for $USER"

SSHCOMMAND_IGNORE_LIST_WARNINGS="${SSHCOMMAND_IGNORE_LIST_WARNINGS:-false}"
if ! fn-verify-file "$userhome/.ssh/authorized_keys" && [[ "$SSHCOMMAND_IGNORE_LIST_WARNINGS" == "false" ]]; then
return 1
fi

if [[ -n "$OUTPUT_TYPE" ]] && [[ "$OUTPUT_TYPE" == "json" ]]; then
data=$(sed --silent --regexp-extended \
's/^command="FINGERPRINT=(\S+) NAME=(\\"|)(.*)\2 `.*",(\S+).*/{ "fingerprint": "\1", "name": "\3", "SSHCOMMAND_ALLOWED_KEYS": "\4" }/p' \
Expand Down