Compare commits

...

10 commits

Author SHA1 Message Date
Stephen Seo 105413e533 Update generate_ssh_Key_and_cert.sh
Allow for up to 3 tries of signing the cert in case of incorrect
password entry.
2023-12-26 15:16:42 +09:00
Stephen Seo f1ff72a66c Add note about man page for ssh certs 2023-07-19 16:44:28 +09:00
Stephen Seo b92383900e Update LICENSE year 2023-07-19 16:31:50 +09:00
Stephen Seo c685a91bb4 Add generate_ssh_key_and_cert.sh
This script generates a ssh key that is signed by a CA key to be used
for certificate-based-authentication to an sshd server. See the
CERTIFICATES section in the ssh-keygen man-page.
2023-07-19 16:30:24 +09:00
Stephen Seo 02e23c0786 Update wireguardConfigGenerator.sh
Do not use ip address that is the subnet broadcast address.
2023-04-22 17:05:00 +09:00
Stephen Seo 39bc54234a Impl full subnet clients for wireguardConfigGenerator.sh 2021-05-22 17:46:40 +09:00
Stephen Seo d99c70fc78 Refactor wireguardConfigGenerator.sh 2021-05-12 20:19:06 +09:00
Stephen Seo 9bb261d364 Add note to wireguardConfigGenerator.sh 2021-05-12 20:14:05 +09:00
Stephen Seo 31a445f91e Add LICENSE 2021-05-12 20:07:21 +09:00
Stephen Seo d1c6a428f5 Add wireguardConfigGenerator.sh 2021-05-12 20:06:36 +09:00
3 changed files with 410 additions and 0 deletions

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021,2023 Stephen Seo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

193
generate_ssh_key_and_cert.sh Executable file
View file

@ -0,0 +1,193 @@
#!/usr/bin/env bash
# This script generates a rsa/ed25519 ssh key signed by a CA key (the CA key
# must be on the machine or the CA key's public key can be specified and
# ssh-agent will be used to sign with the CA key).
#
# See the CERTIFICATES section in the `ssh-keygen` man-page.
# Set this to 0 to disable use of gpg-agent.
GPG_AGENT_ENABLED=1
CA_KEY_THROUGH_SSH_AGENT=0
CA_KEY_PATH=""
USER_KEY_RSA=0
USER_KEY_ED25519=0
USER_KEY_NAME=""
USER_KEY_IDENTIFIER="temp_key"
USER_KEY_USER_NAME=""
USER_KEY_EXPIRE_TIME="+15m"
read -p "Set key identifier? (Default \"temp_key\") [y/N]> " user_input
if [[ -n "$user_input" ]] && [[ "$user_input" == [yY] ]]; then
read -e -p "Specify key identifier: > " user_input
USER_KEY_IDENTIFIER="$user_input"
if [[ -z "$USER_KEY_IDENTIFIER" ]]; then
echo "ERROR: Empty string cannot be used as identifier."
exit 1
fi
fi
echo "Using key identifier \"$USER_KEY_IDENTIFIER\"."
read -p "Set expire time? (default \"$USER_KEY_EXPIRE_TIME\") [y/N]> " user_input
if [[ -n "$user_input" ]] && [[ "$user_input" == [yY] ]]; then
read -p "Specify expire time: > " user_input
USER_KEY_EXPIRE_TIME="$user_input"
if [[ -z "$USER_KEY_EXPIRE_TIME" ]]; then
echo "ERROR: Empty string cannot be used for expire time."
exit 1
fi
echo "Expire time set to \"$USER_KEY_EXPIRE_TIME\"."
fi
read -e -p "Specify user name to identify for (prinicpal(s)): > " user_input
if [[ -z "$user_input" ]]; then
echo "ERROR: Cannot specify empty string for user name!"
exit 1
else
USER_KEY_USER_NAME="$user_input"
fi
echo "Using \"$USER_KEY_USER_NAME\" in cert principals."
read -p "Use CA key through ssh-agent? [y/N]> " user_input
if [[ -n "$user_input" ]] && [[ "$user_input" == [yY] ]]; then
CA_KEY_THROUGH_SSH_AGENT=1
echo "Using CA key through ssh-agent."
while true; do
read -e -p "Specify the CA public key> " user_input
if [[ -r $user_input ]]; then
CA_KEY_PATH="$user_input"
echo "Using \"$CA_KEY_PATH\" as CA public key."
break
else
echo "ERROR: \"$user_input\" doesn't exist!"
fi
done
else
while true; do
read -e -p "Specify the CA key> " user_input
if [[ -r $user_input ]]; then
CA_KEY_PATH="$user_input"
echo "Using \"$CA_KEY_PATH\" as CA key."
break
else
echo "ERROR: \"$user_input\" doesn't exist!"
fi
done
fi
USING_EXISTING_KEY=0
while true; do
read -p "Use existing key? [y/N]> " user_input
if [[ "$user_input" == [yY] ]]; then
USING_EXISTING_KEY=1
break;
else
break;
fi
done
unset EXISTING_PUBKEY_PATH
while (( USING_EXISTING_KEY )); do
read -e -p "Specify the path to the key's pubkey> " user_input
if [[ -r "$user_input" ]]; then
EXISTING_PUBKEY_PATH="$user_input"
echo "Using existing key \"$EXISTING_PUBKEY_PATH\""
break;
fi
done
if [[ -z "$EXISTING_PUBKEY_PATH" ]]; then
while true; do
read -p "Generate RSA or ED25519 key? [r/e]> " user_input
if [[ "$user_input" == [rR] ]]; then
USER_KEY_RSA=1
echo "Using RSA."
break
elif [[ "$user_input" == [eE] ]]; then
USER_KEY_ED25519=1
echo "Using ED25519."
break
fi
done
while true; do
read -p "Specify key name: > " user_input
if [[ -n "$user_input" ]]; then
USER_KEY_NAME="$user_input"
echo "Using key name \"$USER_KEY_NAME\"."
break
fi
done
if (( USER_KEY_RSA )); then
echo ssh-keygen -t rsa -b 4096 -a 100 -o -f "$USER_KEY_NAME"
ssh-keygen -t rsa -b 4096 -a 100 -o -f "$USER_KEY_NAME"
elif (( USER_KEY_ED25519 )); then
echo ssh-keygen -t ed25519 -a 100 -o -f "$USER_KEY_NAME"
ssh-keygen -t ed25519 -a 100 -o -f "$USER_KEY_NAME"
else
echo "ERROR: Neither RSA nor ED25519 specified!"
exit 1
fi
if [[ ! -r "$USER_KEY_NAME" ]] || [[ ! -r "${USER_KEY_NAME}.pub" ]]; then
echo "ERROR: Neither \"$USER_KEY_NAME\" nor \"${USER_KEY_NAME}.pub\" exists!"
exit 1
fi
fi
if [[ -z "$USER_KEY_NAME" ]] && [[ -n "$EXISTING_PUBKEY_PATH" ]]; then
USER_PUBKEY_NAME="$EXISTING_PUBKEY_PATH"
elif [[ -z "$USER_KEY_NAME" ]]; then
echo "ERROR: Key not generated nor existing key specified!"
exit 1
else
USER_PUBKEY_NAME="${USER_KEY_NAME}.pub"
fi
if (( CA_KEY_THROUGH_SSH_AGENT )) && [[ -r "$CA_KEY_PATH" ]]; then
for ((i=0; i<3; ++i)); do
echo 'Signing certificate...'
(( GPG_AGENT_ENABLED )) && gpg-connect-agent updatestartuptty /bye >&/dev/null
ssh-keygen -Us "$CA_KEY_PATH" -I "$USER_KEY_IDENTIFIER" -V "$USER_KEY_EXPIRE_TIME" -n "$USER_KEY_USER_NAME" "${USER_PUBKEY_NAME}"
if (( $? != 0 )); then
echo "ERROR: Failed to sign certificate!"
if (( i >= 2 )); then
exit 1
fi
else
break
fi
done
elif [[ -r "$CA_KEY_PATH" ]]; then
for ((i=0; i<3; ++i)); do
echo 'Signing certificate...'
(( GPG_AGENT_ENABLED )) && gpg-connect-agent updatestartuptty /bye >&/dev/null
ssh-keygen -s "$CA_KEY_PATH" -I "$USER_KEY_IDENTIFIER" -V "$USER_KEY_EXPIRE_TIME" -n "$USER_KEY_USER_NAME" "${USER_PUBKEY_NAME}"
if (( $? != 0 )); then
echo "ERROR: Failed to sign certificate!"
if (( i >= 2 )); then
exit 1
fi
else
break
fi
done
else
echo "ERROR: Invalid settings for CA key!"
exit 1
fi
echo Done.
if [[ -z "$USER_KEY_NAME" ]] && [[ -n "$USER_PUBKEY_NAME" ]]; then
if echo "$USER_PUBKEY_NAME" | grep '\.pub$' >&/dev/null; then
USER_KEY_NAME="$(echo -n "$USER_PUBKEY_NAME" | sed 's/\.pub$//')"
fi
fi
echo "Hint: Use ssh -o CertificateFile=${USER_KEY_NAME:-key}-cert.pub -i ${USER_KEY_NAME:-key} host"

196
wireguardConfigGenerator.sh Executable file
View file

@ -0,0 +1,196 @@
#!/bin/bash
# Note that this script only sets up client connections to a server.
# This means that the confs will be generated without
# client-to-client connections.
set -e
function print_help {
echo "Generates config for wireguard"
echo "-h - prints this help"
echo "-n <name> - gives a name to the config"
echo "-c <count> - number of clients to generate for. Mutually exclusive with \"-u\""
echo "-s <ipv4_second> - sets the second byte of the ipv4"
echo "-i <ipv4_third> - sets the third byte of the ipv4"
echo "-e <endpoint> - ip address or domain name (required)"
echo "-p <port> - listen port of server (defaults to 50000)"
echo "-k - enables persistent keepalive for clients"
echo "-o <directory> - output dir to place configs (required)"
echo "-u <subnet> - subnet to use (default 24). Mutually exclusive with \"-c\""
echo "-f <ipv4_fourth> - must use with \"-u\" to set partial fourth byte"
}
WGNAME="wg$(date | sha1sum | head -c 8)"
CLIENT_COUNT=1
IPV4_FIRST=10
IPV4_SECOND=8 # this can be modified with "-s <integer>"
IPV4_THIRD=0 # this can be modified with "-i <integer>"
IPV4_FOURTH=0 # used when "-u <subnet>" is used
SERVER_ENDPOINT="REQUIRED"
SERVER_LISTEN_PORT=50000
ENABLE_PERSISTENT_KEEPALIVE=0
CONFIG_OUTPUT_DIRECTORY="REQUIRED"
WG_SUBNET=24
CLIENT_COUNT_SET=0
WG_SUBNET_SET=0
IPV4_FOURTH_SET=0
# OPTARG
while getopts 'hn:c:s:i:e:p:ko:u:f:' opt; do
if [ "$opt" == "?" ]; then
print_help
exit 1
elif [ "$opt" == "h" ]; then
print_help
exit 0
elif [ "$opt" == "n" ]; then
WGNAME="$OPTARG"
elif [ "$opt" == "c" ]; then
CLIENT_COUNT="$OPTARG"
CLIENT_COUNT_SET=1
elif [ "$opt" == "s" ]; then
IPV4_SECOND="$OPTARG"
if (($IPV4_SECOND < 0 || $IPV4_SECOND > 255)); then
echo "ERROR: IPV4_SECOND is out of range of a byte"
exit 7
fi
elif [ "$opt" == "i" ]; then
IPV4_THIRD="$OPTARG"
if (($IPV4_THIRD < 0 || $IPV4_THIRD > 255)); then
echo "ERROR: IPV4_THIRD is out of range of a byte"
exit 8
fi
elif [ "$opt" == "e" ]; then
SERVER_ENDPOINT="$OPTARG"
elif [ "$opt" == "p" ]; then
SERVER_LISTEN_PORT="$OPTARG"
if [[ ! "${SERVER_LISTEN_PORT}" =~ [0-9]+ ]]; then
echo "ERROR: Given port is not a number"
exit 5
elif (($SERVER_LISTEN_PORT > 65536)); then
echo "ERROR: Given port is too large"
exit 6
fi
elif [ "$opt" == "k" ]; then
ENABLE_PERSISTENT_KEEPALIVE=1
elif [ "$opt" == "o" ]; then
CONFIG_OUTPUT_DIRECTORY="$OPTARG"
elif [ "$opt" == "u" ]; then
WG_SUBNET="$OPTARG"
WG_SUBNET_SET=1
elif [ "$opt" == "f" ]; then
IPV4_FOURTH="$OPTARG"
IPV4_FOURTH_SET=1
fi
done
# validation
if [ "$SERVER_ENDPOINT" == "REQUIRED" ]; then
echo "ERROR: Endpoint is not set with \"-e\" !"
exit 2
elif [ "$CONFIG_OUTPUT_DIRECTORY" == "REQUIRED" ]; then
echo "ERROR: Output directory is not set with \"-o\" !"
exit 3
elif [ ! -d "$CONFIG_OUTPUT_DIRECTORY" ]; then
echo "ERROR: dir set with \"-o\" is not a directory!"
exit 4
elif (( $CLIENT_COUNT_SET )) && (( $WG_SUBNET_SET )); then
echo "ERROR: \"-c\" and \"-u\" is mutually exclusive!"
exit 12
elif (( $IPV4_FOURTH_SET )) && (( $WG_SUBNET_SET == 0 )); then
echo "ERROR: fourth byte set but \"-u\" not used!"
exit 13
fi
# validation of "-u <subnet>"
if (( $WG_SUBNET < 24 )); then
echo "ERROR: subnet cannot be less than 24!"
exit 9
elif (( $WG_SUBNET > 24 )); then
USED_BITS=$(( 32 - $WG_SUBNET ))
if (( $USED_BITS < 2 )); then
echo "ERROR: subnet \"$WG_SUBNET\" is too large! Use 24-30!"
exit 11
fi
TEMP_A="$IPV4_FOURTH"
while (( $USED_BITS > 0 )); do
if (( $TEMP_A & 1 != 0 )); then
echo "ERROR: Invalid IPV4_FOURTH when using subnet \"$WG_SUBNET\"!"
exit 10
fi
TEMP_A=$(( $TEMP_A >> 1 ))
USED_BITS=$(( $USED_BITS - 1 ))
done
CLIENT_COUNT=$(( 2**(32 - $WG_SUBNET) - 2 - 1 ))
fi
echo "Creating config with name \"$WGNAME\" with \"$CLIENT_COUNT\" clients and subnet \"$WG_SUBNET\"..."
mkdir -p "$HOME/temp"
TEMP_DIR=$(mktemp -d -p "$HOME/temp")
# first create server config
SERVER_CONF="${TEMP_DIR}/${WGNAME}server.conf"
SERVER_PRK="$(wg genkey)"
SERVER_PUB="$(echo -n ${SERVER_PRK} | wg pubkey)"
echo "Creating server conf (will be appended to with client info)..."
cat >> "${SERVER_CONF}" <<EOF
[Interface]
Address = ${IPV4_FIRST}.${IPV4_SECOND}.${IPV4_THIRD}.$(( 1 | $IPV4_FOURTH ))/$WG_SUBNET
ListenPort = ${SERVER_LISTEN_PORT}
PrivateKey = ${SERVER_PRK}
EOF
# generate config per each client
for ((i = 0; i < $CLIENT_COUNT; ++i)); do
CLIENT_CONF="${TEMP_DIR}/${WGNAME}client${i}.conf"
CLIENT_PRK="$(wg genkey)"
CLIENT_PUB="$(echo -n ${CLIENT_PRK} | wg pubkey)"
CLIENT_PRE="$(wg genpsk)"
echo "Appending client $((i + 1)) to server conf..."
cat >> "${SERVER_CONF}" <<EOF
[Peer]
PublicKey = ${CLIENT_PUB}
PresharedKey = ${CLIENT_PRE}
AllowedIPs = ${IPV4_FIRST}.${IPV4_SECOND}.${IPV4_THIRD}.$(( (i + 2) | $IPV4_FOURTH ))/32
EOF
echo "Creating client $((i + 1)) conf..."
cat >> "${CLIENT_CONF}" <<EOF
[Interface]
Address = ${IPV4_FIRST}.${IPV4_SECOND}.${IPV4_THIRD}.$(( (i + 2) | $IPV4_FOURTH ))/$WG_SUBNET
PrivateKey = ${CLIENT_PRK}
[Peer]
PublicKey = ${SERVER_PUB}
PresharedKey = ${CLIENT_PRE}
AllowedIPs = ${IPV4_FIRST}.${IPV4_SECOND}.${IPV4_THIRD}.$(( 1 | $IPV4_FOURTH ))/32
Endpoint = ${SERVER_ENDPOINT}:${SERVER_LISTEN_PORT}
EOF
if (($ENABLE_PERSISTENT_KEEPALIVE)); then
cat >> "${CLIENT_CONF}" <<EOF
PersistentKeepAlive = 25
EOF
fi
done
# output configs to output directory
echo "Placing generated configs to output directory..."
cp -v "$SERVER_CONF" "${CONFIG_OUTPUT_DIRECTORY}/"
for ((i = 0; i < $CLIENT_COUNT; ++i)); do
cp -v "${TEMP_DIR}/${WGNAME}client${i}.conf" "${CONFIG_OUTPUT_DIRECTORY}/"
done
echo "Removing temporary directory..."
rm -rvf "$TEMP_DIR"
echo "Done. Configs should exist at \"$CONFIG_OUTPUT_DIRECTORY\" ."