Compare commits
15 Commits
Author | SHA1 | Date |
---|---|---|
Lucas | cbdede3433 | |
Lucas | fb6454a300 | |
Lucas | 09e8f9650d | |
Lucas | 3e3db13365 | |
Lucas | 2e9d469fde | |
Lucas | 70f4a1b190 | |
Lucas | 9e75968acc | |
Lucas | 24522541ad | |
Lucas | 7b1e34f625 | |
Lucas | e0bc09679e | |
Lucas | 06b4ec5354 | |
Lucas | d630c83ade | |
Lucas | cfd97ff74d | |
Lucas | e7aeadfa32 | |
Lucas | 497363b31b |
2
Makefile
2
Makefile
|
@ -19,7 +19,7 @@
|
|||
chmod a+x $@
|
||||
|
||||
P = cassh
|
||||
V = 0
|
||||
V = 2
|
||||
|
||||
PREFIX = /usr/local
|
||||
MANPREFIX = ${PREFIX}/man
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
.\" along with this software. If not, see
|
||||
.\" <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
.\"
|
||||
.Dd April 07, 2022
|
||||
.Dd April 20, 2022
|
||||
.Dt CASSH-KEYFILE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
|
|
@ -27,7 +27,7 @@ fi
|
|||
cassh_command=$2
|
||||
needs_agent=false
|
||||
case $cassh_command in
|
||||
issue)
|
||||
issue|revoke)
|
||||
needs_agent=true
|
||||
;;
|
||||
esac
|
||||
|
|
68
cassh.1
68
cassh.1
|
@ -11,7 +11,7 @@
|
|||
.\" along with this software. If not, see
|
||||
.\" <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
.\"
|
||||
.Dd March 01, 2022
|
||||
.Dd April 20, 2022
|
||||
.Dt CASSH 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -30,13 +30,19 @@
|
|||
.Bk -words
|
||||
.Cm mkfile
|
||||
.Ic authorized_keys
|
||||
.Op options
|
||||
.Op options ...
|
||||
.Ek
|
||||
.Nm
|
||||
.Bk -words
|
||||
.Cm mkfile
|
||||
.Ic known_hosts
|
||||
.Op hostnames
|
||||
.Op hostnames ...
|
||||
.Ek
|
||||
.Nm
|
||||
.Bk -words
|
||||
.Cm revoke
|
||||
.Op Fl qv
|
||||
.Ar
|
||||
.Ek
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
|
@ -56,9 +62,14 @@ A Certification Authority directory consists of a
|
|||
.Pa ./ca.pub
|
||||
file corresponding to the public key of it, a
|
||||
.Pa ./pubkeys/
|
||||
directory which holds the public keys to be signed, and an optional
|
||||
.Pa ./serial.txt
|
||||
file holding the current serial number for the issued certificates.
|
||||
directory which holds the public keys to be signed, an optional
|
||||
.Pa ./krl
|
||||
file corresponding to the last issued Key Revocation List, and optional
|
||||
.Pa ./ca_serial.txt
|
||||
and
|
||||
.Pa ./krl_serial.txt
|
||||
files corresponding to the current serial number for the issued certificates
|
||||
and Key Revocation Lists.
|
||||
.Pp
|
||||
The following commands are available to
|
||||
.Nm :
|
||||
|
@ -84,9 +95,15 @@ The recognized tokens are:
|
|||
A literal
|
||||
.Sq % .
|
||||
.It \&%C
|
||||
The Certification Authority private key comment.
|
||||
The Certification Authority private key comment field as reported by
|
||||
.Xr ssh-add 1 ,
|
||||
or the string
|
||||
.Sq cassh
|
||||
if there is no comment reported.
|
||||
.It %f
|
||||
The basename of the public key being signed.
|
||||
The basename of the public key being signed, without
|
||||
.Sq .pub
|
||||
suffix.
|
||||
.El
|
||||
.Pp
|
||||
.Ar key_id
|
||||
|
@ -100,37 +117,56 @@ accepts the tokens %% and %f.
|
|||
After token expansion, all recognized options are passed down to
|
||||
.Xr ssh-keygen 1
|
||||
process.
|
||||
.It Cm mkfile Ic authorized_keys Op Ar options
|
||||
.It Cm mkfile Ic authorized_keys Op Ar options ...
|
||||
Write an
|
||||
.Ic authorized_keys
|
||||
file on standard output corresponding to the current Certification
|
||||
Authority.
|
||||
.Ar options
|
||||
is copied verbatim to the output, and
|
||||
are concatenated with commas and copied verbatim to the output.
|
||||
.Cm cert-authority
|
||||
is always added.
|
||||
is always added to the options list.
|
||||
See
|
||||
.Xr sshd 8 AUTHORIZED_KEYS FILE FORMAT
|
||||
for details.
|
||||
.It Cm mkfile Ic known_hosts Op Ar hostnames
|
||||
.It Cm mkfile Ic known_hosts Op Ar hostnames ...
|
||||
Write a
|
||||
.Ic known_hosts
|
||||
file on standard output corresponding to the current Certification
|
||||
Authority.
|
||||
.Ar hostnames
|
||||
is copied verbatim to the output.
|
||||
are concatenated with commas and copied verbatim to the output.
|
||||
See
|
||||
.Xr sshd 8 SSH_KNOWN_HOSTS FILE FORMAT
|
||||
for details.
|
||||
.It Cm revoke Oo Fl qv Oc Ar
|
||||
Generates a Key Revocation List for the current Certification Authority.
|
||||
All recognized options are passed down to
|
||||
.Xr ssh-keygen 1
|
||||
process.
|
||||
See
|
||||
.Xr ssh-keygen 1 KEY REVOCATION LISTS
|
||||
for details on the file format for input files.
|
||||
If
|
||||
.Pa ./krl
|
||||
exists,
|
||||
.Cm revoke
|
||||
will update.
|
||||
.Pa ./krl
|
||||
can be synced back with the input files by first removing it.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width MMMMMMMMMMMMMM -compact
|
||||
.Bl -tag -width MMMMMMMMMMMMMMMMMM -compact
|
||||
.It Pa ./ca.pub
|
||||
Certification Authority public key
|
||||
.It Pa ./pubkeys/
|
||||
Directory containing the public keys to be signed
|
||||
.It Pa ./serial.txt
|
||||
Last issued serial
|
||||
.It Pa ./krl
|
||||
Key Revocation List
|
||||
.It Pa ./ca_serial.txt
|
||||
Last issued serial for certificates
|
||||
.It Pa ./krl_serial.txt
|
||||
Last issued serial for KRLs
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
|
|
204
cassh.sh
204
cassh.sh
|
@ -17,8 +17,9 @@ usage()
|
|||
Usage:
|
||||
${0##*/} issue [-hqv] [-I key_id] [-n principals]
|
||||
[-V validity_interval]
|
||||
${0##*/} mkfile authorized_keys [options]
|
||||
${0##*/} mkfile known_hosts [hostnames]
|
||||
${0##*/} mkfile authorized_keys [options ...]
|
||||
${0##*/} mkfile known_hosts [hostnames ...]
|
||||
${0##*/} revoke [-qv] file ...
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
@ -29,29 +30,11 @@ err()
|
|||
exit 1
|
||||
}
|
||||
|
||||
strip_leading_zeros()
|
||||
# Returns the comment from the loaded secret key in ssh-agent, if any is
|
||||
# present.
|
||||
get_ca_sk_comment_from_pk()
|
||||
{
|
||||
_s=$1
|
||||
if [ -z "$_s" ]; then
|
||||
return
|
||||
fi
|
||||
while [ X"${_s#0}" != X"$_s" ]; do
|
||||
_s=${_s#0}
|
||||
done
|
||||
echo "${_s:-0}"
|
||||
}
|
||||
|
||||
strcmp()
|
||||
{
|
||||
_r=$(expr "X$1" "$2" "X$3")
|
||||
[ "${_r:-0}" -eq 1 ]
|
||||
}
|
||||
|
||||
# Returns comment from the ssh-agent if any is returned, otherwise it
|
||||
# returns the public key's fingerprint.
|
||||
get_ca_comment_from_sk()
|
||||
{
|
||||
ssh-keygen -lf "$1" | {
|
||||
ssh-keygen -lf "$1" 2>/dev/null | {
|
||||
read -r pk_sz pk_fp pk_extra
|
||||
_comment=$(ssh-add -l | while read -r sk_sz sk_fp sk_extra; do
|
||||
if [ "X$sk_fp" = "X$pk_fp" ]; then
|
||||
|
@ -59,57 +42,67 @@ get_ca_comment_from_sk()
|
|||
break
|
||||
fi
|
||||
done)
|
||||
echo "${_comment:-${pk_fp#*:}}"
|
||||
echo "${_comment:-}"
|
||||
}
|
||||
}
|
||||
|
||||
_template_fmt()
|
||||
format()
|
||||
{
|
||||
_allowed_chars=$1
|
||||
_char=$2
|
||||
if [ "X$_char" = X% ]; then
|
||||
echo %
|
||||
return $?
|
||||
fi
|
||||
_s=$1
|
||||
shift
|
||||
|
||||
case $_char in
|
||||
[$_allowed_chars])
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
_v=$(eval echo '${_template_fmt_'"$_char"':-}')
|
||||
if [ -z "$_v" ]; then
|
||||
_cleanup=unset
|
||||
while [ $# -ge 2 ]; do
|
||||
_k=$1 _v=$2
|
||||
shift 2
|
||||
case $_k in
|
||||
[A-Za-z])
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
eval "_token_${_k}=\$_v"
|
||||
_cleanup=$_cleanup" _token_${_k}"
|
||||
done
|
||||
if [ $# -ne 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$_v"
|
||||
}
|
||||
|
||||
template()
|
||||
{
|
||||
_allowed=$1
|
||||
_s=$2
|
||||
_out=
|
||||
|
||||
while [ "${_s#*%}" != "$_s" ]; do
|
||||
_t=${_s#*%}
|
||||
_out=$_out${_s%"%"$_t}
|
||||
_s=$_t
|
||||
_c=${_s%${_s#?}}
|
||||
|
||||
_t=$(_template_fmt "$_allowed" "$_c")
|
||||
if [ $? -ne 0 ]; then
|
||||
if [ -z "${_c:-}" ]; then
|
||||
return 1
|
||||
elif [ X"${_c}" = X% ]; then
|
||||
_out=$_out%
|
||||
else
|
||||
eval "_out=$_out\$_token_${_c}" || return 1
|
||||
fi
|
||||
_out=$_out$_t
|
||||
|
||||
_s=${_s#$_c}
|
||||
done
|
||||
_out=$_out$_s
|
||||
|
||||
eval "$_cleanup"
|
||||
|
||||
echo "$_out"
|
||||
}
|
||||
|
||||
strjoin()
|
||||
{
|
||||
_c=$1
|
||||
shift
|
||||
|
||||
_out=
|
||||
for _s; do
|
||||
_out=${_out:+$_out$_c}$_s
|
||||
done
|
||||
|
||||
echo "$_out"
|
||||
}
|
||||
|
||||
|
@ -138,64 +131,42 @@ main_issue()
|
|||
usage
|
||||
fi
|
||||
|
||||
if [ ! -f "$PATH_CA_PUB" ]; then
|
||||
err "no $PATH_CA_PUB found"
|
||||
fi
|
||||
if ! ssh-add $qflag $vflag -T "$PATH_CA_PUB"; then
|
||||
err "can't use CA key"
|
||||
fi
|
||||
if [ ! -d "$PATH_PUBKEYS_DIR/" ]; then
|
||||
err "no pubkeys directory found"
|
||||
fi
|
||||
|
||||
if [ ! -f "$PATH_CA_SERIAL" ]; then
|
||||
date -u +%Y%m%d000000000 >"$PATH_CA_SERIAL"
|
||||
echo 1 >"$PATH_CA_SERIAL"
|
||||
fi
|
||||
read -r serial <"$PATH_CA_SERIAL"
|
||||
# Remove NNNNNNNNN suffix
|
||||
serial_date=${serial%?????????}
|
||||
current_date=$(date -u +%Y%m%d)
|
||||
if strcmp "$current_date" ">" "$serial_date"; then
|
||||
serial_date=$current_date
|
||||
serial_counter=0
|
||||
else
|
||||
# Remove YYYYmmdd prefix and leading
|
||||
serial_counter=$(strip_leading_zeros "${serial#????????}")
|
||||
fi
|
||||
serial=$(printf "%s%09u\n" "$serial_date" "$serial_counter")
|
||||
|
||||
_template_fmt_C=$(get_ca_comment_from_sk "$PATH_CA_PUB")
|
||||
find "$PATH_PUBKEYS_DIR/" -type f -name '*.pub' ! -name '*-cert.pub' | {
|
||||
rc=0
|
||||
if [ ! -d "$PATH_PUBKEYS_DIR" ]; then
|
||||
exit 0
|
||||
fi
|
||||
find "$PATH_PUBKEYS_DIR" -type f -name '*.pub' ! -name '*-cert.pub' |
|
||||
sort | {
|
||||
ca_comment=$(get_ca_sk_comment_from_pk "$PATH_CA_PUB")
|
||||
: ${ca_comment:=cassh}
|
||||
|
||||
while read -r pk; do
|
||||
pkname=${pk%.pub}
|
||||
pkname=${pkname#$PATH_PUBKEYS_DIR/}
|
||||
_template_fmt_f=$pkname
|
||||
|
||||
id=$(template Cf "$key_id_fmt")
|
||||
id=$(format "$key_id_fmt" C "$ca_comment" f "$pkname")
|
||||
set -- -I "$id" -Us "$PATH_CA_PUB" \
|
||||
$hflag $qflag $vflag \
|
||||
-V "$validity_interval" -z "$serial"
|
||||
|
||||
if $nflag; then
|
||||
principals=$(template f "$principals_fmt")
|
||||
principals=$(format "$principals_fmt" \
|
||||
f "$pkname")
|
||||
ssh-keygen "$@" -n "$principals" "$pk"
|
||||
else
|
||||
ssh-keygen "$@" "$pk"
|
||||
fi || rc=1
|
||||
|
||||
serial_counter=$(($serial_counter + 1))
|
||||
if [ $serial_counter -ge 1000000000 ]; then
|
||||
err "can't issue more certificates today"
|
||||
fi
|
||||
serial=$(printf "%s%09u\n" "$serial_date" \
|
||||
"$serial_counter" | tee "$PATH_CA_SERIAL")
|
||||
|
||||
if [ $rc -ne 0 ]; then
|
||||
break
|
||||
if [ $? -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
serial=$(($serial + 1))
|
||||
echo $serial >"$PATH_CA_SERIAL"
|
||||
done
|
||||
return $rc
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,19 +190,11 @@ main_mkfile()
|
|||
|
||||
case $file in
|
||||
authorized_keys)
|
||||
if [ $# -gt 1 ]; then
|
||||
usage
|
||||
fi
|
||||
options=cert-authority${1:+,$1}
|
||||
printf "%s " "$options"
|
||||
printf "%s " "$(strjoin , cert-authority "$@")"
|
||||
;;
|
||||
known_hosts)
|
||||
if [ $# -gt 1 ]; then
|
||||
usage
|
||||
fi
|
||||
hostnames=${1:-}
|
||||
if [ -n "$hostnames" ]; then
|
||||
printf "@cert-authority %s " "$hostnames"
|
||||
if [ $# -gt 0 ]; then
|
||||
printf "@cert-authority %s " "$(strjoin , "$@")"
|
||||
else
|
||||
printf "@cert-authority "
|
||||
fi
|
||||
|
@ -244,10 +207,42 @@ main_mkfile()
|
|||
cat "$PATH_CA_PUB"
|
||||
}
|
||||
|
||||
main_revoke()
|
||||
{
|
||||
qflag=
|
||||
vflag=
|
||||
while getopts fqv flag; do
|
||||
case $flag in
|
||||
q) qflag=-q ;;
|
||||
v) vflag=${vflag:--}v ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
if [ ! -f "$PATH_KRL_SERIAL" ]; then
|
||||
echo 1 >"$PATH_KRL_SERIAL"
|
||||
fi
|
||||
read -r serial <"$PATH_KRL_SERIAL"
|
||||
|
||||
uflag=
|
||||
if [ -f "$PATH_KRL" ]; then
|
||||
uflag=-u
|
||||
fi
|
||||
|
||||
ssh-keygen -kf "$PATH_KRL" -Us "$PATH_CA_PUB" -z "$serial" \
|
||||
$qflag $vflag $uflag "$@" || exit 1
|
||||
|
||||
serial=$(($serial + 1))
|
||||
echo $serial >"$PATH_KRL_SERIAL"
|
||||
}
|
||||
|
||||
set -u
|
||||
|
||||
PATH_CA_PUB=./ca.pub
|
||||
PATH_CA_SERIAL=./serial.txt
|
||||
PATH_CA_SERIAL=./ca_serial.txt
|
||||
PATH_KRL=./krl
|
||||
PATH_KRL_SERIAL=./krl_serial.txt
|
||||
PATH_PUBKEYS_DIR=./pubkeys
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
|
@ -259,5 +254,6 @@ shift
|
|||
case $cmd in
|
||||
issue) main_issue "$@" ;;
|
||||
mkfile) main_mkfile "$@" ;;
|
||||
revoke) main_revoke "$@" ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
|
|
Loading…
Reference in New Issue