feat: add sshd-prohibit-password

This commit is contained in:
AJ ONeal
2023-06-09 23:38:50 +00:00
parent 6baa78f95d
commit 3a574392f0
9 changed files with 546 additions and 2 deletions

97
ssh-harden/README.md Normal file
View File

@@ -0,0 +1,97 @@
---
title: SSH Prohibit Password
homepage: https://webinstall.dev/ssh-prohibit-password
tagline: |
SSH Prohibit Password: Because friends don't let friends ssh with passwords
linux: true
---
## Cheat Sheet
> Will check if your system This will check if your Modern SSH deployments are
> key-only and don't allow root login. However, there's a lot of legacy systems
> out there.
`ssh-harden` will
1. Check that some `/home/*/.ssh/authorized_keys` is non-empty
2. Check that `/etc/sudoers.d` is not empty
3. Optionally create a `sudoer` for a given user and group
4. Disable `root` login
5. Disable Password and Challenge login
```sh
USAGE
ssh-harden [username] [sudo-group]
EXAMPLES
sudo ssh-harden
sudo ssh-harden app
sudo ssh-harden "$(id -n -u)" wheel
```
### How to check for sudoers
```sh
sudo sh -c 'grep "^\w\+ ALL=" /etc/sudoers.d/*'
```
### How to check for authorized ssh users
**Quick 'n' Easy**
```sh
sudo sh -c "grep -E '^(ssh|ec)' /home/*/.ssh/authorized_keys" |
cut -d' ' -f3 |
sort -u
```
**Detailed**
```sh
my_authorized=''
for my_file in /home/*/.ssh/authorized_keys; do
# if no files match the glob becomes a literal string
if test "${my_file}" = '/home/*/.ssh/authorized_keys'; then
break
fi
echo "${my_file} authorizes:"
if ! grep -q -E '^(ssh|ec)' "${my_file}"; then
echo " (none, empty file)"
continue
fi
grep '^(ssh|ec)' "${my_file}" | cut -d' ' -f3 | while read -r my_comment; do
echo " ${my_comment}"
done
my_authorized='true'
done
if test -z "${my_authorized}"; then
echo >&2 ""
echo >&2 "ERROR"
echo >&2 " No authorized remote users found."
echo >&2 ""
exit 1
fi
```
### How to add passwordless sudoer
```sh
echo "app ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/app
```
How to copy allowed keys from root to the new user:
```sh
mkdir -p /home/app/.ssh/
chmod 0700 /home/app/.ssh/
cat "$HOME/.ssh/authorized_keys" >> /home/app/.ssh/authorized_keys
chmod 0600 /home/app/.ssh/authorized_keys
chown -R app:app /home/app/.ssh/
```

View File

@@ -0,0 +1,182 @@
#!/bin/sh
set -e
set -u
# There are two important use cases:
# 1. Run as 'root', for the 'app' (or specified) user
# 2. Run as a sudoer, for self
fn_check_as_root() { (
my_admin="${1:-}"
if ! command -v sudo > /dev/null; then
echo ""
echo "WARNING"
echo " 'sudo' is not installed."
echo " '${my_admin}' MUST be able to login via the 'root' password."
echo ""
else
my_admin_is_sudoer=''
if grep -q "^${my_admin} ALL=" /etc/sudoers; then
my_admin_is_sudoer='true'
elif test -e /etc/sudoers.d; then
if grep -q "^${my_admin} ALL=" /etc/sudoers.d/*; then
my_admin_is_sudoer='true'
fi
fi
echo ""
echo "ERROR"
echo " '${my_admin}' is NOT a sudoer"
echo ""
echo "SOLUTION"
echo " mkdir -p /etc/sudoers.d"
echo " chmod 0700 /etc/sudoers.d"
echo " echo '${my_admin} ALL=(ALL:ALL) NOPASSWD: ALL' | "
echo " tee '/etc/sudoers.d/${my_admin}'"
echo ""
fi
); }
fn_check_as_sudoer() { (
echo
); }
main() {
echo ""
echo "IMPORTANT"
echo ""
echo "READ CAREFULLY (or get LOCKED OUT)"
echo ""
my_user_id="$(id -u)"
if test "0" = "${my_user_id}"; then
fn_check_as_root ${1:-app}
else
fn_check_as_sudoer
fi
my_username="$(id -u -n)"
my_sudo_user="${1:-${my_username}}"
my_sudo_exists=''
my_sudo=''
if command -v sudo > /dev/null; then
my_sudo_exists='true'
my_sudo="sudo"
fi
my_root=''
if test "$(id -u)" = "0"; then
my_root='true'
my_sudo=''
fi
my_authorized_exists=''
echo ''
echo "All authorized users:"
if ! $my_sudo sh -c 'ls /home/*/.ssh/authorized_keys' > /devl/null 2>&1; then
echo " (none)"
else
$my_sudo sh -c "grep -E '^(ssh|ec)' /home/*/.ssh/authorized_keys" |
cut -d' ' -f3 |
sort -u |
while read -r my_comment; do
echo " ${my_comment}"
my_authorized_exists='true'
done
fi
my_sudoer_exists=''
echo ''
echo "All sudoers:"
if test -z "${my_sudo_exists}"; then
echo ' (sudo not installed)'
else
{
$my_sudo sh -c "grep -E '(sudo|wheel):' /etc/gshadow" |
cut -d':' -f4 |
tr ',' '\n'
#$my_sudo sh -c "grep '^%\?\w\+ ALL=' /etc/sudoers /etc/sudoers.d/*" |
$my_sudo sh -c "grep '^\w\+ ALL=' /etc/sudoers /etc/sudoers.d/*" |
cut -d':' -f2 |
cut -d' ' -f1
} |
grep -v root |
sort -u |
while read -r my_sudoer; do
my_sudoer_exists='true'
echo " ${my_sudoer}"
done
fi
#adduser "$my_new_user" sudo || adduser "$my_new_user" wheel
echo "$my_new_user ALL=(ALL:ALL) NOPASSWD: ALL" | tee "/etc/sudoers.d/$my_new_user"
# allow users who can already login as 'root' to login as 'app'
mkdir -p "/home/$my_new_user/.ssh/"
chmod 0700 "/home/$my_new_user/.ssh/"
echo "${my_keys}" >> "/home/$my_new_user/.ssh/authorized_keys"
chmod 0600 "/home/$my_new_user/.ssh/authorized_keys"
touch "/home/$my_new_user/.ssh/config"
chmod 0644 "/home/$my_new_user/.ssh/config"
chown -R "$my_new_user":"$my_new_user" "/home/$my_new_user/.ssh/"
# ensure that 'app' has an SSH Keypair
sudo -i -u "$my_new_user" sh -c "ssh-keygen -b 2048 -t rsa -f '/home/$my_new_user/.ssh/id_rsa' -q -N ''"
chown -R "$my_new_user":"$my_new_user" "/home/$my_new_user/.ssh/"
# Install webi for the new 'app' user
WEBI_HOST=${WEBI_HOST:-"https://webinstall.dev"}
sudo -i -u "$my_new_user" sh -c "curl -fsSL '$WEBI_HOST/webi' | sh" ||
sudo -i -u "$my_new_user" sh -c "wget -q -O - '$WEBI_HOST/webi' | sh"
# TODO ensure that ssh-password login is off
my_pass="$(grep 'PasswordAuthentication yes' /etc/ssh/sshd_config)"
my_pam=""
if [ "Darwin" = "$(uname -s)" ]; then
# Turn off PAM for macOS or it will allow password login
my_pam="$(grep 'UsePAM yes' /etc/ssh/sshd_config)"
fi
if [ -n "${my_pass}" ] || [ -n "${my_pam}" ]; then
echo "######################################################################"
echo "# #"
echo "# WARNING #"
echo "# #"
echo "# Found /etc/ssh/sshd_config: #"
if [ -n "${my_pass}" ]; then
echo "# PasswordAuthentication yes #"
fi
if [ -n "${my_pam}" ]; then
echo "# UsePAM yes #"
fi
echo "# #"
echo "# This is EXTREMELY DANGEROUS and insecure. #"
echo "# We'll attempt to fix this now... #"
echo "# #"
sed -i 's/#\?PasswordAuthentication \(yes\|no\)/PasswordAuthentication no/' \
/etc/ssh/sshd_config
sed -i 's/#\?UsePAM \(yes\|no\)/UsePAM no/' \
/etc/ssh/sshd_config
if grep "PasswordAuthentication yes" /etc/ssh/sshd_config; then
echo "# FAILED. Please check /etc/ssh/sshd_config manually. #"
else
echo "# Fixed... HOWEVER, you'll need to manually restart ssh: #"
echo "# #"
echo "# sudo systemctl restart ssh #"
echo "# #"
echo "# (you may want to make sure you can login as the new user first) #"
fi
echo "# #"
echo "######################################################################"
fi
echo "Created user '${my_new_user}' as sudoer with a random password."
echo "(set a new password with 'password ${my_new_user}')"
}
main "${1:-app}" "${2:-}"

View File

@@ -0,0 +1,11 @@
---
title: SSH Prohibit Password
homepage: https://webinstall.dev/sshd-prohibit-password
tagline: |
Alias for https://webinstall.dev/sshd-prohibit-password
alias: sshd-prohibit-password
description: |
See https://webinstall.dev/sshd-prohibit-password
---
Alias for https://webinstall.dev/sshd-prohibit-password

View File

@@ -0,0 +1,13 @@
#!/bin/sh
set -e
set -u
# shellcheck disable=SC2016
__alias_sshd_prohibit_password() {
echo "'ssh-prohibit-password@${WEBI_TAG:-stable}' is an alias for 'sshd-prohibit-password@${WEBI_VERSION-}'"
WEBI_HOST=${WEBI_HOST:-"https://webinstall.dev"}
curl -fsSL "$WEBI_HOST/sshd-prohibit-password@${WEBI_VERSION-}" | sh
}
__alias_sshd_prohibit_password

View File

@@ -8,7 +8,7 @@ tagline: |
## Cheat Sheet
> SSH Utils includes shortcut commands for some common tasks, including
> `ssh-pubkey`, `ssh-setpass`, and `ssh-adduser`
> `ssh-pubkey`, `ssh-setpass`, `ssh-adduser`, and `sshd-prohibit-password`
**ssh-pubkey**:
@@ -32,6 +32,22 @@ correctly if run as root. `ssh-adduser` adds user `app` with the same
**`~/.ssh/authorized_keys`** as the `root` user, with a long random password,
and gives `app` `sudo` privileges.
**sshd-prohibit-password**:
Enforces security for `/etc/ssh/sshd_config`
```diff
- #PasswordAuthentication yes
+ PasswordAuthentication no
- #PermitRootLogin yes
+ PermitRootLogin prohibit-password
# macOS only
- UsePAM yes
+ UsePAM no
```
**ssh-setpass**:
`ssh-setpass` will ask you for your old passphrase (if any) and then for the new

View File

@@ -6,7 +6,8 @@ __install_ssh_utils() {
rm -f \
"$HOME/.local/bin/ssh-pubkey" \
"$HOME/.local/bin/ssh-setpass" \
"$HOME/.local/bin/ssh-adduser"
"$HOME/.local/bin/ssh-adduser" \
"$HOME/.local/bin/sshd-prohibit-password"
# done
webi_download \
@@ -18,6 +19,9 @@ __install_ssh_utils() {
webi_download \
"$WEBI_HOST/packages/ssh-adduser/ssh-adduser.sh" \
"$HOME/.local/bin/ssh-adduser"
webi_download \
"$WEBI_HOST/packages/sshd-prohibit-password/sshd-prohibit-password" \
"$HOME/.local/bin/sshd-prohibit-password"
chmod a+x "$HOME/.local/bin/ssh-"*
}

View File

@@ -0,0 +1,53 @@
---
title: SSH Prohibit Password
homepage: https://webinstall.dev/sshd-prohibit-password
tagline: |
SSH Prohibit Password: Because friends don't let friends ssh with passwords
linux: true
---
## Cheat Sheet
> Will check if your system This will check if your Modern SSH deployments are
> key-only and don't allow root login. However, there's a lot of legacy systems
> out there.
`sshd-prohibit-password` will inspect `/etc/ssh/sshd_config` and
1. Enforce that `PasswordAuthentication` is `no`
2. Enforce that `PermitRootLogin` is `no` or `prohibit-password` \
(or `without-password`, for older systems)
3. (macOS only) Enforce that `UsePAM` is `no`
This **will run automatically** and **uses `sudo`** to make changes.
### What's checked and changed?
```diff
- #PasswordAuthentication yes
+ PasswordAuthentication no
- #PermitRootLogin yes
+ PermitRootLogin prohibit-password
# macOS only
- UsePAM yes
+ UsePAM no
```
### How to restart SSH?
```sh
# Ubuntu / Debian / RedHat
sudo systemctl restart sshd
# Alpine / Gentoo
sudo rc-service sshd restart
# macOS
sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist
sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist
# others
killall sshd
```

View File

@@ -0,0 +1,20 @@
#!/bin/sh
set -e
set -u
__install_sshd_prohibit_password() {
my_cmd="sshd-prohibit-password"
rm -f "$HOME/.local/bin/${my_cmd}"
webi_download \
"$WEBI_HOST/packages/${my_cmd}/${my_cmd}" \
"$HOME/.local/bin/${my_cmd}"
chmod a+x "$HOME/.local/bin/${my_cmd}"
# run the command
"$HOME/.local/bin/${my_cmd}"
}
__install_sshd_prohibit_password

View File

@@ -0,0 +1,148 @@
#!/bin/sh
set -e
set -u
main() {
cmd_sudo=''
if command -v sudo > /dev/null; then
my_id="$(id -u)"
if test "0" != "${my_id}"; then
cmd_sudo='sudo'
fi
fi
cmd_sed="${cmd_sudo} sed -i -E"
my_bsd_sed=''
if ! sed -V 2>&1 | grep -q 'GNU'; then
cmd_sed="${cmd_sudo} sed -i .prohibit-password-bak -E"
my_bsd_sed='true'
fi
my_changes=''
# PasswordAuthentication
echo ""
echo "Checking for 'PasswordAuthentication no'..."
echo " grep '^\s*PasswordAuthentication\s*no' /etc/ssh/sshd_config"
my_allow_passwords="$(
grep -q \
'^\s*PasswordAuthentication\s*no\s*$' \
/etc/ssh/sshd_config ||
echo 'true'
)"
if test -n "${my_allow_passwords}"; then
$cmd_sed \
's/#*[[:space:]]*PasswordAuthentication[[:space:]]*(yes|no)[[:space:]]*$/PasswordAuthentication no/g' \
/etc/ssh/sshd_config
if ! grep -q '^PasswordAuthentication no$' /etc/ssh/sshd_config; then
echo ""
echo "ERROR"
echo " failed to update '/etc/ssh/sshd_config'"
echo ""
return 1
fi
echo " RESTART REQUIRED: disabled user password login"
my_changes='true'
else
echo " PASS: passwords are NOT allowed"
fi
# PermitRootLogin
echo ""
echo "Checking for 'PermitRootLogin prohibit-password'..."
echo " grep -E '^\s*PermitRootLogin\s*(no|prohibit-password)' /etc/ssh/sshd_config"
my_allow_root="$(
grep -q -E \
'^\s*PermitRootLogin\s*(no|prohibit-password|without-password)\s*$' \
/etc/ssh/sshd_config ||
echo 'true'
)"
if test -n "${my_allow_root}"; then
$cmd_sed \
's/#*[[:space:]]*PermitRootLogin[[:space:]]*(yes|no|prohibit-password|without-password)[[:space:]]*$/PermitRootLogin prohibit-password/g' \
/etc/ssh/sshd_config
if ! grep -q -E \
'^PermitRootLogin prohibit-password$' \
/etc/ssh/sshd_config; then
echo ""
echo "ERROR"
echo " failed to update '/etc/ssh/sshd_config'"
echo ""
return 1
fi
echo " RESTART REQUIRED: disabled root password login"
my_changes='true'
else
echo " PASS: 'root' can NOT login via password"
fi
# UsePAM
echo ""
echo "Checking for 'UsePAM no' (macOS only)..."
# shellcheck disable=SC2016
echo ' test "$(uname -s)" = "Darwin" &&'
echo " grep '^\s*UsePAM\s*no' /etc/ssh/sshd_config"
if test "$(uname -s)" = "Darwin"; then
my_allow_pam="$(
grep -q \
'^\s*UsePAM\s*no' \
/etc/ssh/sshd_config ||
echo 'true'
)"
if test -n "${my_allow_pam}"; then
$cmd_sed \
's/#*[[:space:]]*UsePAM[[:space:]]*(yes|no)[[:space:]]*$/UsePAM no/g' \
/etc/ssh/sshd_config
if ! grep -q '^UsePAM no$' /etc/ssh/sshd_config; then
echo ""
echo "ERROR"
echo " failed to update '/etc/ssh/sshd_config'"
echo ""
return 1
fi
echo " RESTART REQUIRED: disabled macOS password login"
my_changes='true'
else
echo " PASS: passwords are NOT allowed"
fi
fi
if test -z "${my_changes}"; then
echo ""
echo ""
echo "All checks pass. No changes necessary."
echo ""
return 0
fi
if test -n "${my_bsd_sed}"; then
$cmd_sudo rm -f /etc/ssh/sshd_config.prohibit-password-bak
fi
echo ""
echo "##################################################################"
echo "# #"
echo "# IMPORTANT #"
echo "# #"
echo "# READ CAREFULLY #"
echo "# #"
echo "# (or get locked out) #"
echo "# #"
echo "##################################################################"
echo ""
echo ""
echo "1. TEST SSH KEYS"
echo ""
echo " Be sure that you can login as an admin user with ssh keys"
echo ""
echo "2. RESTART SSH with one of the following:"
echo ""
echo " systemctl restart sshd # Ubuntu / Debian"
echo " rc-service sshd restart # Alpine / Gentoo"
echo " killall sshd # all others"
echo ""
}
main "${1:-app}" "${2:-}"