Compare commits
34 Commits
58e033e16e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42b7ec401a | ||
|
|
d248af25a0 | ||
|
|
fd8ab31779 | ||
|
|
c020c481cf | ||
|
|
fe0c66d57a | ||
|
|
b08ef16aec | ||
|
|
b6fd58a7ca | ||
|
|
d30123dc4a | ||
|
|
cff9d8d3a9 | ||
|
|
fc7af4a1f1 | ||
|
|
623c8ef437 | ||
|
|
b18c0f3400 | ||
|
|
7f634bf0ba | ||
|
|
fce8a539d4 | ||
|
|
04d52d0017 | ||
|
|
20ca89d452 | ||
|
|
6620d6c0a9 | ||
|
|
7b7fba3f39 | ||
|
|
6886b6ca69 | ||
|
|
42b3506b1c | ||
|
|
a26c71c092 | ||
|
|
44174c8c6b | ||
|
|
0b1e8f5c08 | ||
|
|
6705bab1fe | ||
|
|
a0223e439b | ||
|
|
ccd5f04b00 | ||
|
|
341648ad80 | ||
|
|
b094a3464c | ||
|
|
dbf1e1f1d1 | ||
|
|
45c0d47198 | ||
|
|
3bce34f8cc | ||
|
|
9eacb621cc | ||
|
|
2b9c0556a4 | ||
|
|
3c889513e9 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
password.txt
|
||||
certs/
|
||||
secrets/
|
||||
db/
|
||||
db/
|
||||
|
||||
ca.json
|
||||
42
README.md
42
README.md
@@ -18,13 +18,25 @@ step ca init --ssh --acme
|
||||
|
||||
## SSH Certificates
|
||||
|
||||
Install script:
|
||||
|
||||
```bash
|
||||
bash <(curl -sL https://gitea.john-stream.com/john/janus/raw/branch/main/scripts/ssh-server-check.sh)
|
||||
```
|
||||
|
||||
### Server
|
||||
|
||||
Use step-ca to sign an existing public key to produce a signed certificate with some principals on it.
|
||||
|
||||
```
|
||||
export IP_ADDRESS=$(ip -4 addr show dev eth0 | awk '/inet /{print $2}' | cut -d/ -f1) && \
|
||||
export HOSTNAME=$(hostname -s) && \
|
||||
step ssh certificate --host --sign \
|
||||
--principal janus --principal janus.john-stream.com \
|
||||
--principal "$HOSTNAME" \
|
||||
--principal "$HOSTNAME.john-stream.com" \
|
||||
--principal "$IP_ADDRESS" \
|
||||
--provisioner admin \
|
||||
janus /etc/ssh/ssh_host_ed25519_key.pub
|
||||
"$HOSTNAME" /etc/ssh/ssh_host_ed25519_key.pub
|
||||
```
|
||||
|
||||
Get the (public) cert for the CA that signs the user SSH certs from step-ca.
|
||||
@@ -36,9 +48,31 @@ step ssh config --roots > /etc/ssh/ssh_user_ca.pub
|
||||
Configure sshd to point to the key/cert combo.
|
||||
|
||||
```
|
||||
cat << EOF > /etc/ssh/sshd_config.d/certs.conf
|
||||
cat <<EOF > /etc/ssh/sshd_config.d/certs.conf
|
||||
TrustedUserCAKeys /etc/ssh/ssh_user_ca.pub
|
||||
HostKey /etc/ssh/ssh_host_ed25519_key
|
||||
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
|
||||
EOF
|
||||
```
|
||||
```
|
||||
|
||||
```
|
||||
systemctl reload sshd
|
||||
```
|
||||
|
||||
### Client
|
||||
|
||||
Trust certs that are signed by Janus:
|
||||
|
||||
```
|
||||
(umask 022; cat <<EOF > ~/.ssh/known_hosts
|
||||
@cert-authority *.john-stream.com,192.168.1.* $(step ssh config --host --roots)
|
||||
EOF
|
||||
)
|
||||
```
|
||||
|
||||
```
|
||||
step ssh certificate --sign \
|
||||
--principal root --principal john \
|
||||
--provisioner admin \
|
||||
john ~/.ssh/id_ed25519.pub
|
||||
```
|
||||
|
||||
121
scripts/ssh-client.sh
Executable file
121
scripts/ssh-client.sh
Executable file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Env vars
|
||||
#
|
||||
|
||||
SSH_CFG_PATH="$(readlink -f ~/.ssh)"
|
||||
SSH_USER_KEY="$SSH_CFG_PATH/id_ed25519"
|
||||
SSH_USER_PUBLIC_KEY="$SSH_USER_KEY.pub"
|
||||
SSH_USER_CERT="$SSH_USER_KEY-cert.pub"
|
||||
|
||||
GREEN_CHECK="\e[32m✔\e[0m"
|
||||
RED_X="\e[31m✗\e[0m"
|
||||
YELLOW_BANG="\e[33m!\e[0m"
|
||||
MAGENTA_QUESTION="\e[35m?\e[0m"
|
||||
UP_ONE_LINE="\e[1A"
|
||||
|
||||
#
|
||||
# Function Definitions
|
||||
#
|
||||
|
||||
reset_line() {
|
||||
echo -en "\r\e[K"
|
||||
}
|
||||
|
||||
title_msg() {
|
||||
local title="\e[1m${1:-Title}:\e[0m"
|
||||
local prompt="${2:-Prompt for the user}"
|
||||
# printf "%b %b" "$title" "$prompt"
|
||||
echo -e "$title $prompt"
|
||||
}
|
||||
|
||||
prompt_user() {
|
||||
full_prompt_msg="$(title_msg "${1}" "${2}")"
|
||||
echo -n -e "$MAGENTA_QUESTION $full_prompt_msg"
|
||||
read -p " (y/n) " -r
|
||||
}
|
||||
|
||||
update_prompt() {
|
||||
local icon="$1"
|
||||
case $# in
|
||||
1) msg="$full_prompt_msg $REPLY";;
|
||||
2) msg="$2";;
|
||||
3) msg="$(title_msg "${2}" "${3}")";;
|
||||
*) msg="Too many arguments";;
|
||||
esac
|
||||
|
||||
reset_line
|
||||
echo -e "$icon $msg"
|
||||
}
|
||||
|
||||
reupdate_prompt() {
|
||||
# echo -en "$UP_ONE_LINE"
|
||||
echo -en "$UP_ONE_LINE"
|
||||
update_prompt "$@"
|
||||
}
|
||||
|
||||
icon_msg() {
|
||||
local icon="$1"
|
||||
echo -en "$icon "
|
||||
title_msg "${@:2}"
|
||||
}
|
||||
|
||||
success_msg() {
|
||||
icon_msg "${GREEN_CHECK}" "$@"
|
||||
}
|
||||
|
||||
warn_msg() {
|
||||
icon_msg "${YELLOW_BANG}" "$@"
|
||||
}
|
||||
|
||||
check_user_private_key() {
|
||||
if [[ -e "$SSH_USER_KEY" ]]; then
|
||||
success_msg "SSH User" "Private key: $SSH_USER_KEY"
|
||||
else
|
||||
prompt_user "SSH User" "Private key missing: ${SSH_USER_KEY}. Create?"
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
reupdate_prompt $YELLOW_BANG "SSH User" "Creating private key"
|
||||
ERROR_MSG=$(ssh-keygen -t ed25519 -f "$SSH_USER_KEY" -N "" 2>&1)
|
||||
if [[ $? -eq 0 ]]; then
|
||||
reupdate_prompt $GREEN_CHECK "SSH User" "Created private key: ${SSH_USER_KEY}"
|
||||
else
|
||||
reupdate_prompt $RED_X "SSH User" "Failed to create key: ${SSH_USER_KEY}"
|
||||
echo -e "Error: $ERROR_MSG"
|
||||
fi
|
||||
elif [[ $REPLY =~ ^[Nn]$ ]]; then
|
||||
reupdate_prompt $YELLOW_BANG "SSH User" "Continuing without private key"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_user_cert() {
|
||||
if [[ -e "$SSH_USER_CERT" ]]; then
|
||||
success_msg "SSH User" "Certificate: $SSH_USER_CERT"
|
||||
else
|
||||
prompt_user "SSH User" "Cert missing. Renew cert?"
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
if renew_user_cert; then
|
||||
update_prompt $GREEN_CHECK "SSH User" "Renewed cert"
|
||||
else
|
||||
update_prompt $RED_X "SSH User" "Failed to renew cert"
|
||||
fi
|
||||
elif [[ $REPLY =~ ^[Nn]$ ]]; then
|
||||
reupdate_prompt $RED_X "SSH User" "Declined to renew cert"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
renew_user_cert() {
|
||||
step ssh certificate --sign \
|
||||
--principal root --principal john \
|
||||
--provisioner admin \
|
||||
john@john-pc-ubuntu ~/.ssh/id_ed25519.pub < /dev/tty
|
||||
}
|
||||
|
||||
#
|
||||
# Run Process
|
||||
#
|
||||
|
||||
check_user_private_key
|
||||
check_user_cert
|
||||
230
scripts/ssh-server-check.sh
Executable file
230
scripts/ssh-server-check.sh
Executable file
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Env vars
|
||||
#
|
||||
|
||||
# SSH config paths
|
||||
SSH_CFG_DIR="/etc/ssh"
|
||||
SSH_USER_CA="$SSH_CFG_DIR/ssh_user_ca.pub"
|
||||
SSH_HOST_KEY="$SSH_CFG_DIR/ssh_host_ed25519_key"
|
||||
SSH_HOST_PUBLIC_KEY="$SSH_HOST_KEY.pub"
|
||||
SSH_HOST_CERT="$SSH_HOST_KEY-cert.pub"
|
||||
|
||||
GREEN_CHECK="\e[32m✔\e[0m"
|
||||
RED_X="\e[31m✗\e[0m"
|
||||
YELLOW_BANG="\e[33m!\e[0m"
|
||||
|
||||
CREATE_USER_CA=0
|
||||
CREATE_HOST_CERT=0
|
||||
NEEDS_RESTART=0
|
||||
|
||||
|
||||
#
|
||||
# Function Definitions
|
||||
#
|
||||
|
||||
# This test loads the sshd config to see what values actually get parsed.
|
||||
ssh_config_val() {
|
||||
local field="$1"
|
||||
local val
|
||||
|
||||
if [[ -z "$field" ]]; then
|
||||
echo "usage: ssh_config_val <config name>" >&2
|
||||
return 2
|
||||
fi
|
||||
|
||||
echo $(sshd -T 2>/dev/null | grep -i "^$field " | head -1 | awk '{print $2}')
|
||||
}
|
||||
|
||||
title_msg() {
|
||||
local title="\e[1m${1:-Title}:\e[0m"
|
||||
local prompt="${2:-Prompt for the user}"
|
||||
# printf "%b %b" "$title" "$prompt"
|
||||
echo -e "$title $prompt"
|
||||
}
|
||||
|
||||
prompt_user() {
|
||||
full_prompt_msg="$(title_msg "${1}" "${2}")"
|
||||
echo -n -e "$YELLOW_BANG $full_prompt_msg"
|
||||
read -p " (y/n) " -n 1 -r
|
||||
}
|
||||
|
||||
update_prompt() {
|
||||
local icon="$1"
|
||||
case $# in
|
||||
1) msg="$full_prompt_msg $REPLY";;
|
||||
2) msg="$2";;
|
||||
3) msg="$(title_msg "${2}" "${3}")";;
|
||||
*) msg="Too many arguments";;
|
||||
esac
|
||||
|
||||
echo -en "\r\e[K"
|
||||
echo -e "$icon $msg"
|
||||
}
|
||||
|
||||
sign_host_cert() {
|
||||
local if="eth0"
|
||||
local IP_ADDRESS=$(ip -4 addr show dev $if | awk '/inet /{print $2}' | cut -d/ -f1) && \
|
||||
local hostname=$(hostname -s) && \
|
||||
step ssh certificate --host --sign \
|
||||
--principal "$hostname" \
|
||||
--principal "$hostname.john-stream.com" \
|
||||
--principal "$IP_ADDRESS" \
|
||||
--provisioner admin \
|
||||
"$hostname" "$SSH_HOST_PUBLIC_KEY"
|
||||
}
|
||||
|
||||
|
||||
check_ssh_config_files() {
|
||||
row_success() {
|
||||
local key="$1"
|
||||
local path="$2"
|
||||
local perms=$(stat -c '%a' "$path")
|
||||
printf "%-17b %-20s %-6s %s\n" " $GREEN_CHECK" "$key" "$perms" "$path"
|
||||
}
|
||||
|
||||
row_missing() {
|
||||
local key="$1"
|
||||
local path="$2"
|
||||
printf "%-15b %-20s %-6s %s\n" " $YELLOW_BANG" "$key" "-" "$path (missing)"
|
||||
}
|
||||
|
||||
row_unconfigured() {
|
||||
local key="$1"
|
||||
printf "%-17b %-20s %-6s %s\n" " $RED_X" "$key" "-" "(not configured)"
|
||||
}
|
||||
|
||||
get_key_status() {
|
||||
local path="$1"
|
||||
if [[ -z "$path" ]]; then
|
||||
echo "unconfigured"
|
||||
elif [[ ! -e "$path" ]]; then
|
||||
echo "missing"
|
||||
else
|
||||
echo "success"
|
||||
fi
|
||||
}
|
||||
|
||||
row_process() {
|
||||
local key="$1"
|
||||
path=$(ssh_config_val "$key")
|
||||
status=$(get_key_status "$path")
|
||||
case "$status" in
|
||||
success) row_success "$key" "$path" ;;
|
||||
missing) row_missing "$key" "$path" ;;
|
||||
unconfigured) row_unconfigured "$key" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
printf "%-6s %-20s %-6s %s\n" "STATUS" "KEY" "PERMS" "PATH"
|
||||
|
||||
row_process "hostkey"
|
||||
row_process "hostcertificate"
|
||||
case "$status" in
|
||||
missing) CREATE_HOST_CERT=1;;
|
||||
esac
|
||||
row_process "trustedusercakeys"
|
||||
case "$status" in
|
||||
missing) CREATE_USER_CA=1;;
|
||||
esac
|
||||
echo
|
||||
}
|
||||
|
||||
ssh_fingerprint() {
|
||||
local field="$1"
|
||||
|
||||
if [[ -z "$field" ]]; then
|
||||
echo "usage: ssh_fingerprint <field>" >&2
|
||||
return 2
|
||||
fi
|
||||
|
||||
local cfg_path=$(ssh_config_val $field)
|
||||
|
||||
if [[ -z "$cfg_path" ]]; then
|
||||
echo "error: sshd field '$field' not found or empty" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -r "$cfg_path" ]]; then
|
||||
echo "error: file not readable: $cfg_path" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
ssh-keygen -lf "$cfg_path" | awk '{ print $2 }'
|
||||
}
|
||||
|
||||
check_cert_config() {
|
||||
local base_dir="/etc/ssh/sshd_config.d"
|
||||
local cfg_path="$base_dir/${1:-certs.conf}"
|
||||
|
||||
install_cert_config() {
|
||||
mkdir -p $(dirname $cfg_path)
|
||||
cat <<EOF > $cfg_path
|
||||
TrustedUserCAKeys $SSH_USER_CA
|
||||
HostKey $SSH_HOST_KEY
|
||||
HostCertificate $SSH_HOST_CERT
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ ! -e $cfg_path ]]; then
|
||||
prompt_user "sshd" "Currently unconfigured for certs. Do you want to configure?"
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
install_cert_config
|
||||
update_prompt $GREEN_CHECK "sshd" "Configured to use and accept certs"
|
||||
NEEDS_RESTART=1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
restart_sshd() {
|
||||
if [[ $NEEDS_RESTART -eq 0 ]]; then return; fi
|
||||
echo -en "$YELLOW_BANG Restarting sshd..."
|
||||
systemctl restart sshd
|
||||
if [[ $? -eq 0 ]]; then
|
||||
local sshd_pid=$(systemctl show --property MainPID --value sshd)
|
||||
update_prompt $GREEN_CHECK "sshd" "Restarted sshd.service on PID: $sshd_pid"
|
||||
else
|
||||
update_prompt $RED_X "sshd" "Failed to restart sshd.service"
|
||||
exit 1
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
create_files() {
|
||||
if [[ $CREATE_HOST_CERT -eq 1 ]]; then
|
||||
prompt_user "SSH Host" "Cert missing. Sign the ssh host cert?"
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
update_prompt $YELLOW_BANG "Signing ssh host cert"
|
||||
sign_host_cert
|
||||
NEEDS_RESTART=1
|
||||
else
|
||||
update_prompt $RED_X
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $CREATE_USER_CA -eq 1 ]]; then
|
||||
prompt_user "SSH Host" "Create the trusted keys file?"
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
(step ssh config --roots > "$path")
|
||||
update_prompt $GREEN_CHECK "SSH Host" "Created the trusted keys file for the SSH host."
|
||||
NEEDS_RESTART=1
|
||||
else
|
||||
update_prompt $RED_X
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Run Process
|
||||
|
||||
check_cert_config "certs.conf"
|
||||
check_ssh_config_files
|
||||
create_files
|
||||
restart_sshd
|
||||
|
||||
title_msg "SSH Host Cert" "$SSH_HOST_CERT"
|
||||
CERT_INFO=$(ssh-keygen -Lf "$SSH_HOST_CERT")
|
||||
echo -e "$CERT_INFO" | grep "Public key"
|
||||
echo -e "$CERT_INFO" | grep "Valid"
|
||||
echo -e "$CERT_INFO" | grep -A3 "Principals"
|
||||
Reference in New Issue
Block a user