|
#!/bin/bash |
|
# shellcheck disable=SC2015,SC2039,SC2166,SC3043 |
|
VERBOSITY=${VERBOSITY:-1} |
|
HEADLESS="--headless" |
|
|
|
rf() { |
|
"$@" || { |
|
stderr "failed [$?]:" "$@" |
|
exit 1; |
|
} |
|
} |
|
stderr() { echo "$@" 1>&2; } |
|
debug() { [ $VERBOSITY -lt 1 ] || stderr "$@"; } |
|
|
|
is_coreutils_date() { |
|
if [ -z "$_IS_COREUTILS_DATE" ]; then |
|
date --help 2>&1 | grep -q "coreutils" && |
|
_IS_COREUTILS_DATE=true || _IS_COREUTILS_DATE=false |
|
fi |
|
case "$_IS_COREUTILS_DATE" in |
|
true) return 0;; |
|
false) return 1;; |
|
esac |
|
} |
|
|
|
unix2utc() { |
|
date -R "--date=@$1" +"%Y-%m-%d %H:%M:%S %z %Z" |
|
} |
|
|
|
tounix() { |
|
local date="$1" |
|
if is_coreutils_date; then |
|
# wierd, output says has numerical timezone (offset) string (EDT or UTC). |
|
date=${date% [A-Z][A-Z][A-Z]} |
|
rf date --date="$date" "+%s" |
|
else |
|
rf date -d"$date" -D"%Y-%m-%d %H:%M:%S %z %Z" "+%s" |
|
fi |
|
} |
|
|
|
get_refresh_expire() { |
|
local audience="$1" audsafe="" cached="" f="" out="" |
|
|
|
audsafe=${audience//\//-} |
|
[ -d ~/Library/Caches ] && cached=$(echo ~/Library/Caches) || |
|
cached=$(echo "$HOME/.cache") |
|
|
|
f="$cached/chainguard/$audsafe/refresh-token" |
|
[ -f "$f" ] || { echo 0; return 1; } |
|
out=$(base64 -d < "$f") || { |
|
stderr "get_refresh_expire: failed: base64 -d '$f' failed" |
|
return 1; |
|
} |
|
|
|
# this is in unix timestamp |
|
exp=$(echo "$out" | jq .exp) && [ -n "$exp" ] || { |
|
stderr "jq of base64 decoded '$f' returned $? had exp='$exp'" |
|
return 1 |
|
} |
|
|
|
unix2utc "$exp" |
|
} |
|
|
|
getttl() { |
|
local audience="$1" rc="" stout="" |
|
set -- chainctl auth status --quick --output=json --audience="$audience" |
|
stout=$("$@") |
|
rc=$? |
|
if [ $rc -ne 0 -a $rc -ne 1 ]; then |
|
stderr "failed execute[$rc]: $*" |
|
[ -z "$stout" ] || stderr "output: ${stout}" |
|
return $rc |
|
fi |
|
local valid="" reason="" |
|
valid=$(echo "$stout" | jq -r .valid) || { |
|
stderr "fail execute[$rc]: $*" |
|
stderr "produced invalid json output" |
|
return 1 |
|
} |
|
|
|
[ "$valid" = "true" -o "$valid" = "false" ] || |
|
{ stderr "unexpected output from $*. valid='$valid'"; return 1; } |
|
|
|
[ "$valid" = "false" ] && { |
|
_RET=0 |
|
_RET_ref=0 |
|
_RET_tok_exp=0 |
|
_RET_ref_exp=0 |
|
_RET_OUT="$stout" |
|
return 0 |
|
} |
|
|
|
set -- chainctl auth status --output=json --audience="$audience" |
|
stout=$("$@") || { |
|
rc=$? |
|
stderr "fail execute[$rc] (passed with --quick): $*" |
|
[ -z "$stout" ] || stderr "output: ${stout}" |
|
return 1 |
|
} |
|
local expire="" exps="" refexpire="" refexps="" nows="" |
|
expire=$(echo "$stout" | jq -r .expiry) && |
|
[ -n "$expire" ] || { |
|
stderr "failed to get expiry info from status" |
|
return 1 |
|
} |
|
exps=$(tounix "$expire") || { |
|
stderr "failed to convert $expire for audience=$audience" |
|
return 1 |
|
} |
|
|
|
refexpire=$(get_refresh_expire "$audience") || { |
|
stderr "failed to get refresh expire for $audience" |
|
return 1 |
|
} |
|
|
|
refexps=$(tounix "$refexpire") |
|
|
|
nows=$(date "+%s") || fail "date +%s failed" |
|
email=$(echo "$stout" | jq -r .email) |
|
#stderr "$audience [$email] good for $(((exps-nows)/60))m" |
|
_RET=$((exps-nows)) |
|
_RET_ref=$((refexps-nows)) |
|
_RET_tok_exp="$expire" |
|
_RET_ref_exp="$refexps" |
|
_RET_OUT="$stout" |
|
} |
|
|
|
tok_with_ttl() { |
|
local audience="$1" wantttl="$2" |
|
local rttl="" ttl="" out="" |
|
getttl "$audience" || return 1 |
|
rttl="$_RET_ref" |
|
ttl="$_RET" |
|
# if refresh is valid: refresh, it should be free |
|
if [ "$rttl" -gt "$wantttl" ]; then |
|
out=$(chainctl auth login ${HEADLESS:+"$HEADLESS"} --audience="$audience" 2>&1) || return |
|
getttl "$audience" || return 1 |
|
rttl="$_RET_ref" |
|
ttl="$_RET" |
|
fi |
|
if [ $ttl -gt $wantttl -a $rttl -gt $wantttl ]; then |
|
echo "$audience - good for $((ttl/60))m refresh=$((rttl/60))m" |
|
return 0 |
|
fi |
|
debug "$audience had ttl=$((ttl/60))m rttl=$((rttl/60))m wanted $((wantttl/60))m" |
|
chainctl auth login ${HEADLESS:+"$HEADLESS"} --audience="$audience" || return |
|
getttl "$audience" || return |
|
echo "$audience - good for $((_RET/60))m refresh=$((_RET_ref/60))m" |
|
} |
|
|
|
## CHAINGUARD_IDENTITY gets set. hmm... not sure where. |
|
command -v chainctl >/dev/null 2>&1 || { stderr "no chainctl in PATH"; exit 1; } |
|
|
|
# this is required to get better headless login flow |
|
if ! chainctl config view | grep -q "device-flow: chainguard" ; then |
|
stderr "Please run: chainctl config set auth.device-flow chainguard" |
|
exit 1 |
|
fi |
|
|
|
# this you probably just want. |
|
if ! chainctl config view | grep -q "social-login: google-oauth2" ; then |
|
stderr "Probably you want: chainctl config set default.social-login google-oauth2" |
|
exit 1 |
|
fi |
|
|
|
if [ -f ~/.docker/config.json ]; then |
|
out=$(jq -r '.credHelpers."cgr.dev"' < ~/.docker/config.json) |
|
[ $? -eq 0 ] || exit 1 |
|
if [ "$out" != "cgr" ]; then |
|
rf chainctl auth configure-docker ${HEADLESS:+"${HEADLESS}"} |
|
fi |
|
fi |
|
|
|
check=false |
|
if [ "$1" = "--check" -o "$1" = "-c" ]; then |
|
check=true |
|
shift |
|
fi |
|
|
|
want_ttl=$((30*60)) |
|
case "$1" in |
|
--want=*) want_ttl=$((60*${1#--want=})); shift;; |
|
--want|-w) want_ttl=$((60*$2)); shift 2;; |
|
-w[0-9]*) want_ttl=$((60*${1#-w})); shift;; |
|
esac |
|
if [ $# -eq 0 ]; then |
|
# cgr.dev - created by chainctl auth configure-docker |
|
# https://console-api.enforce.dev - created by chainctl --output=json img repos list --parent="" |
|
# apk.cgr.dev - created by enterprise-packages 'make apk-token' |
|
# console-api.enforce.dev - ??? |
|
set -- https://console-api.enforce.dev console-api.enforce.dev apk.cgr.dev cgr.dev |
|
fi |
|
|
|
if [ "$check" = "true" ]; then |
|
for audience in "$@"; do |
|
rf getttl "$audience" |
|
#id=$(echo "$_RET_OUT" | jq --raw .identity) |
|
extra=$(echo "$_RET_OUT" | jq -r '. | .email + " / " + .identity') |
|
echo "$audience - $((_RET/60))m refresh=$((_RET_ref/60))m${extra:+ ${extra}}" |
|
done |
|
exit |
|
fi |
|
|
|
for audience in "$@"; do |
|
rf tok_with_ttl "$audience" "$want_ttl" |
|
done |
|
exit |