#!/bin/bash #if [ ! "$BASH_VERSION" ] ; then # bash $0 $1 $2 $3 $4 $5 $6 $7 $8 $9 # exit 1 #fi declare -Ax args declare -Ax config declare -Ax messages wxi-config(){ case $1 in login) jq '.login.'$ORG'.token = "'$TOKEN'"' $HOME/.warengroup/config.json &> $HOME/.warengroup/config.json.tmp ;; *) echo -n "" ;; esac mv $HOME/.warengroup/config.json.tmp $HOME/.warengroup/config.json &> /dev/null } wxi-restricted(){ if [[ -z $1 || $1 == "--user" ]] then if [[ $USER == "root" || $USER == "local" ]] then wxi-content status "Command" "Restricted" wxi-content text "It's not permitted to execute this command with root or local user." wxi-footer wxi-repeat "\n" 3 exit 1 fi fi if [[ $1 == "--org" ]] then if [[ ! -z ${args['org']} ]] then case ${args['org']} in warengroup) ORG=warengroup ;; cwchristerw) ORG=cwchristerw ;; *) wxi-content status "Organization" "Unsupported" wxi-footer wxi-stop ;; esac elif [[ $(hostname -d) == "devices.waren.io" ]] then ORG=warengroup elif [[ $(hostname -d) == "devices.christerwaren.fi" ]] then ORG=cwchristerw fi if [[ ! -z $ORG ]] then case $ORG in warengroup) DOMAIN=waren.io VAULT_DOMAIN=vault.cwinfo.net ORG_HEADER="Warén Group" ;; cwchristerw) DOMAIN=christerwaren.fi VAULT_DOMAIN=vault.cwinfo.net ORG_HEADER="Christer Warén" ;; *) wxi-content status "Organization" "Unsupported" wxi-footer wxi-stop ;; esac else wxi-content status "Organization" "Required" wxi-footer wxi-stop fi fi if [[ -z $1 || $1 == "--vault" ]] then if [[ -z $VAULT_DOMAIN ]] then wxi-content status "Vault" "Unavailable" wxi-footer wxi-repeat "\n" 3 exit 1 fi VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/sys/health) if [[ ! $VAULT_STATUS -eq 200 ]] then wxi-content status "Vault" "Offline" wxi-footer wxi-repeat "\n" 3 exit 1 fi fi } wxi-start(){ wxi-header "Warén CLI" h1 mkdir -p $HOME/.warengroup if [[ ! -f $HOME/.warengroup/config.json || $(jq -e . < $HOME/.warengroup/config.json &>/dev/null; echo $?) -gt 0 ]] then echo '{}' | jq > $HOME/.warengroup/config.json fi mkdir -p $HOME/.ssh/keys chmod 700 -R $HOME/.ssh/keys mkdir -p $HOME/.ssh/multiplex chmod 700 -R $HOME/.ssh/multiplex } wxi-stop(){ if [[ ! -f $HOME/bin/wx ]] then wx-install &> /dev/null else wx-update &> /dev/null fi # wxi-repeat "\n" 3 # for key in "${!args[@]}" # do # echo "$key: ${args[$key]}" # done wxi-repeat "\n" 3 exit 1 } wxi-content(){ if [[ $1 == "text" ]] then echo "$2" elif [[ $1 == "status" ]] then wxi-repeat "\n" 2 echo -n "$wxiBold" echo "Status" echo -n "$wxiNormal" echo "$2 - $3" elif [[ $1 == "link" ]] then echo "$2 - $3" fi } wxi-footer(){ echo "" echo "------------------------------" wxi-repeat " " $((30/2-12/2)) echo -n "$wxiBold" echo "Warén Group™" echo -n "$wxiNormal" wxi-repeat " " $((30/2-17/2)) echo "https://waren.io" echo "==============================" } wxiRed=$(tput setaf 196) wxiGreen=$(tput setaf 46) wxiYellow=$(tput setaf 226) wxiBlue=$(tput setaf 21) wxiPurple=$(tput setaf 165) wxiTurquoise=$(tput setaf 14) wxiPink=$(tput setaf 198) wxiOrange=$(tput setaf 202) wxiUnderline=$(tput smul) wxiBold=$(tput bold) wxiNormal=$(tput sgr0) wxi-header(){ if [[ $2 == "h1" ]] then wxi-repeat "\n" 3 echo "==============================" wxi-repeat " " $((30/2-${#1}/2)) echo -n "$wxiBold" echo "$1" echo -n "$wxiNormal" echo "==============================" fi if [[ $2 == "h2" || -z $2 ]] then wxi-start wxi-repeat " " $((30/2-6/2-${#1}/2)) echo -n "$wxiBold" echo ">> $1 <<" echo -n "$wxiNormal" echo "------------------------------" echo "" fi if [[ $2 == "h3" ]] then echo -n "$wxiBold" echo "$1" echo -n "$wxiNormal" fi } wxi-repeat() { if [[ $1 == " " ]] then local str=$1 n=$2 spaces printf -v spaces "%*s" $n " " printf "%s" "${spaces// /$str}" else for i in $(seq 1 $2); do echo -en $1 done fi } wx-help(){ wxi-header "Help" wxi-content text " Usage: $0 COMMAND [OPTIONS] Common Commands: init Init help Help ssh SSH config Config edit Edit save Save sync Sync clean Clean keys Keys generate Generate sign Sign retrieve Retrieve save Save sync Sync clean Clean Authentication Commands: login Login logout Logout Management Commands: auto Auto clean Clean settings Settings Maintenance Commands: install Install update Update uninstall Uninstall "; wxi-footer } wx-infra(){ wx-login &> /dev/null wx-auto &> /dev/null wxi-header "Infra" wxi-restricted case $USERNAME in cwchristerw) if [[ -d "$HOME/.warengroup/infra" ]] then INFRA_PATH="$HOME/.warengroup/infra" else INFRA_PATH="$HOME/.warengroup/infra" mkdir -p "$INFRA_PATH" &> /dev/null git clone ssh://git@git.waren.io:2222/warengroup-private/infra.git --config core.sshCommand="ssh -i $HOME/.ssh/keys/warengroup-legacy -o ProxyJump=none" "$INFRA_PATH" &> /dev/null fi if [[ ! -f "$INFRA_PATH/vault/cwchristerw" || ! -f "$INFRA_PATH/vault/warengroup" ]] then mkdir -p "$INFRA_PATH/vault" &> /dev/null curl \ -H "X-Vault-Token: $TOKEN" \ -X GET \ https://$VAULT_DOMAIN/v1/cli/data/cwchristerw/settings/infra -s | jq -r '.data.data.cwchristerw' > "$INFRA_PATH/vault/cwchristerw" curl \ -H "X-Vault-Token: $TOKEN" \ -X GET \ https://$VAULT_DOMAIN/v1/cli/data/cwchristerw/settings/infra -s | jq -r '.data.data.warengroup' > "$INFRA_PATH/vault/warengroup" fi INFRA_VAULT="--vault-id warengroup@vault/warengroup --vault-id cwchristerw@vault/cwchristerw" ;; *) wxi-content status "User" "Unsupported" wxi-footer wxi-stop INFRA_PATH="$HOME/.warengroup/infra" INFRA_VAULT="--vault-id warengroup@vault/warengroup" ;; esac if [[ -z ${args['2']} ]] then echo "Tag Required" else cd "$INFRA_PATH" #git pull &> /dev/null #ansible-galaxy collection install -r requirements.yml --upgrade &> /dev/null if [[ ${args['2']} == "init" ]] then wxi-header "Init" h3 if [[ -z ${args['3']} ]] then tags=init else tags=${args['3']} fi ansible-playbook $INFRA_VAULT playbooks/init.yml -t $tags --limit "${args['limit']}" elif [[ ${args['2']} == "manager" ]] then wxi-header "Manager" h3 ansible-playbook $INFRA_VAULT manager.yml --extra-vars "${args['extra-vars']}" else wxi-header "Playbooks" h3 tags=${args['2']} ansible-playbook $INFRA_VAULT playbooks.yml -t $tags --limit "${args['limit']}" fi cd "$OLDPWD" fi wxi-footer } wx-ssh(){ wx-login &> /dev/null case ${args['2']} in config) wxi-ssh-config ;; keys) wxi-ssh-keys ;; *) wxi-header "SSH" wxi-restricted wxi-footer ;; esac } wx-welcome(){ wxi-header "Welcome" wxi-header "Help" h3 wxi-content text "Use \"wx help\" command" echo "" wxi-header "Useful Links" h3 wxi-content link "Infra" "https://infra.waren.io" wxi-content link "Status" "https://status.waren.io" wxi-footer } wx-login(){ wxi-header "Login" wxi-restricted --user wxi-restricted --org wxi-restricted --vault wxi-header "$ORG_HEADER" h3 if [[ ! -z ${args['auth-method']} ]] then AUTH_METHOD=${args['auth-method']} elif [[ ! -z ${args['token']} ]] then AUTH_METHOD=token elif [[ -f "$HOME/.warengroup/config.json" && $(cat $HOME/.warengroup/config.json | jq -r .login.$ORG.token) != 'null' && $(cat $HOME/.warengroup/config.json | jq -r .login.$ORG.token) != '' ]] then AUTH_METHOD=token elif [[ ! -z ${args['username']} ]] then AUTH_METHOD=ldap else AUTH_METHOD=ldap fi if [[ ! -z $AUTH_METHOD ]] then case $AUTH_METHOD in ldap) echo -n "Username: " if [[ ! -z ${args['username']} ]] then USERNAME=${args['username']} wxi-content text "$USERNAME" else read USERNAME fi echo -n "Password: " if [[ ! -z ${args['password']} ]] then PASSWORD=${args['password']} else read -s PASSWORD fi if [[ ! -z $PASSWORD ]] then wxi-content text "****************" else wxi-content text "" fi if [[ -z $USERNAME || -z $PASSWORD ]] then wxi-content status "Username & Password" "Required" wxi-footer wxi-stop fi VAULT_LOGIN=$(curl https://$VAULT_DOMAIN/v1/auth/ldap/login/$USERNAME -X POST -d '{ "password": "'$PASSWORD'" }' -s | jq -r '.auth.client_token') if [[ -z $VAULT_LOGIN || ${#VAULT_LOGIN} -lt 95 || ${#VAULT_LOGIN} -gt 95 ]] then wxi-content status "Login" "Failed" wxi-stop fi TOKEN=$VAULT_LOGIN wxi-config login ;; token) echo -n "Token: " if [[ ! -z ${args['token']} ]] then if [[ ${args['token']} != "true" ]] then TOKEN=${args['token']} fi elif [[ -f "$HOME/.warengroup/config.json" && $(cat $HOME/.warengroup/config.json | jq -r .login.$ORG.token) != 'null' && $(cat $HOME/.warengroup/config.json | jq -r .login.$ORG.token) != '' ]] then TOKEN=$(cat $HOME/.warengroup/config.json | jq -r .login.$ORG.token) else read -s TOKEN fi if [[ ! -z $TOKEN ]] then wxi-content text "***********************************************************************************************" fi if [[ -z $TOKEN ]] then wxi-content status "Token" "Required" wxi-footer wxi-stop fi if [[ ${#TOKEN} -lt 95 || ${#TOKEN} -gt 95 ]] then wxi-content status "Token" "Invalid" wxi-footer wxi-stop fi VAULT_LOGIN=$(curl https://$VAULT_DOMAIN/v1/auth/token/renew-self -X POST --header "X-Vault-Token: $TOKEN" -d '{ "token": "'$TOKEN'" }' -s | jq -r '.auth.client_token') if [[ -z $VAULT_LOGIN || ${#VAULT_LOGIN} -lt 95 || ${#VAULT_LOGIN} -gt 95 ]] then wxi-content status "Login" "Failed" wxi-stop fi TOKEN=$VAULT_LOGIN wxi-config login ;; *) wxi-content status "Login Type" "Unsupported" wxi-footer wxi-stop ;; esac fi VAULT_USERNAME=$(curl https://$VAULT_DOMAIN/v1/auth/token/lookup-self -X GET --header "X-Vault-Token: $TOKEN" -s | jq -r '.data.display_name') if [[ -z $VAULT_USERNAME ]] then wxi-content status "Login" "Username Missing" wxi-stop elif [[ $VAULT_USERNAME != ldap* && $VAULT_USERNAME != oidc* ]] then wxi-content status "Login" "Authentication Method Invalid" wxi-stop elif [[ $VAULT_USERNAME == ldap* ]] then USERNAME=${VAULT_USERNAME#ldap-} elif [[ $VAULT_USERNAME == oidc* ]] then USERNAME=${VAULT_USERNAME#oidc-} fi wxi-footer } wx-logout(){ wxi-header "Logout" wxi-restricted --user wxi-restricted --org wxi-restricted --vault if [[ -f "$HOME/.warengroup/config.json" && $(cat $HOME/.warengroup/config.json | jq -r .login.$ORG.token) != 'null' ]] then TOKEN=$(cat $HOME/.warengroup/config.json | jq -r .login.$ORG.token) fi VAULT_STATUS=$(curl https://$VAULT_DOMAIN/v1/auth/token/revoke-self -X POST --header "X-Vault-Token: $TOKEN" -s -o /dev/null -w "%{http_code}") if [[ $VAULT_STATUS -eq 204 || $VAULT_STATUS -eq 403 ]] then wxi-header "$ORG_HEADER" h3 echo "Logging Out..." TOKEN="" wxi-config login wxi-footer wxi-stop fi } wx-install(){ wxi-header "Install" wxi-restricted --user if [[ -f "./wx" && -f "./maintainer.sh" && -d "./src" ]] then ./maintainer.sh fi mkdir -p $HOME/bin if [[ $(curl -s -o /dev/null -w "%{http_code}" https://git.waren.io/warengroup/wx/raw/branch/master/wx) -eq 200 ]] then curl https://git.waren.io/warengroup/wx/raw/branch/master/wx -o $HOME/bin/wx &> /dev/null chmod +x $HOME/bin/wx &> /dev/null fi CRONJOB_NAME="#Warén CLI: Auto" CRONJOB_TASK="*/5 * * * * $HOME/bin/wx auto" if [[ -z $(crontab -l | grep -F "$CRONJOB_NAME") || -z $(crontab -l | grep -F "$CRONJOB_TASK") ]] then (crontab -l ; echo "$CRONJOB_NAME" ; echo "$CRONJOB_TASK") | grep -Fv "no crontab" | crontab - fi wxi-footer } wx-uninstall(){ wxi-header "Uninstall" wxi-restricted --user wx-clean &> /dev/null if [[ -d "$HOME/.warengroup" ]] then rm "$HOME/.warengroup" -rf fi CRONJOB_NAME="#Warén CLI: Auto" CRONJOB_TASK="*/5 * * * * $HOME/bin/wx auto" if [[ $(crontab -l | grep -F "$CRONJOB_NAME") || $(crontab -l | grep -F "$CRONJOB_TASK") ]] then crontab -l | grep -Fv "$CRONJOB_NAME" | grep -Fv "$CRONJOB_TASK" | grep -Fv "no crontab" | crontab - fi if [[ -f "$HOME/bin/wx" ]] then rm "$HOME/bin/wx" -rf fi wxi-footer wxi-repeat "\n" 3 exit 1 } wx-update(){ wxi-header "Update" wxi-restricted --user wx-install &> /dev/null echo "Updates Completed" wxi-footer } wx-auto(){ wx-login &> /dev/null wxi-header "Auto" wxi-restricted wxi-footer wxi-ssh-config-sync wxi-ssh-keys-sign wxi-ssh-keys-sync } wx-clean(){ wxi-header "Clean" wxi-restricted --user wxi-footer wxi-ssh-config-clean wxi-ssh-keys-clean } wx-settings(){ wx-login &> /dev/null wx-auto &> /dev/null wxi-header "Settings" wxi-restricted --user wxi-footer } wxi-ssh-config(){ case ${args['3']} in edit) wxi-ssh-config-sync wxi-ssh-config-edit wxi-ssh-config-save ;; save) wxi-ssh-config-save ;; sync) wxi-ssh-config-sync ;; clean) wxi-ssh-config-clean ;; *) wxi-ssh-config-sync ;; esac } wxi-ssh-keys(){ case ${args['3']} in generate) wxi-ssh-keys-retrieve ${args['4']} wxi-ssh-keys-generate ${args['4']} wxi-ssh-keys-save ${args['4']} ;; sign) wxi-ssh-keys-sign ;; retrieve) wxi-ssh-keys-retrieve ${args['4']} ;; save) wxi-ssh-keys-save ${args['4']} ;; sync) wxi-ssh-keys-sync ;; clean) wxi-ssh-keys-clean ;; *) wxi-header "SSH / Keys" wxi-footer ;; esac } wxi-ssh-config-clean(){ wxi-header "SSH / Config / Clean" wxi-restricted VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/config -X GET --header "X-Vault-Token: $TOKEN") if [[ -f "$HOME/.ssh/config" && $VAULT_STATUS -eq 200 ]] then rm "$HOME/.ssh/config" fi wxi-footer } wxi-ssh-config-edit(){ wxi-header "SSH / Config / Edit" wxi-restricted nano ~/.ssh/config wxi-footer } wxi-ssh-config-save(){ wxi-header "SSH / Config / Save" wxi-restricted if [[ -f "$HOME/.ssh/config" ]] then curl https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/config -X POST --header "X-Vault-Token: $TOKEN" -d "{ \"data\": { \"data\": \"$(cat ~/.ssh/config | base64 -w 0)\" } }" -s &> /dev/null fi wxi-footer } wxi-ssh-config-sync(){ wxi-header "SSH / Config / Sync" wxi-restricted VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/config -X GET --header "X-Vault-Token: $TOKEN") if [[ $VAULT_STATUS -eq 200 ]] then touch ~/.ssh/config SSH1_CONFIG_MD5=$(curl https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/config -X GET --header "X-Vault-Token: $TOKEN" -s | jq -r '.data.data.data' | base64 -d | md5sum | base64) SSH2_CONFIG_MD5=$(cat ~/.ssh/config | md5sum | base64) if [[ $SSH1_CONFIG_MD5 != $SSH2_CONFIG_MD5 ]] then echo $(curl https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/config -X GET --header "X-Vault-Token: $TOKEN" -s | jq -r '.data.data.data') | base64 -d > ~/.ssh/config 2>&1 chmod 700 ~/.ssh/config fi fi wxi-footer } wxi-ssh-keys-clean(){ wxi-header "SSH / Keys / Clean" wxi-restricted if [[ ! -z $1 ]] then if [[ -f "$HOME/.ssh/keys/$1" ]] then rm "$HOME/.ssh/keys/$1" &> /dev/null rm "$HOME/.ssh/keys/$1.pub" &> /dev/null rm "$HOME/.ssh/keys/$1.sig" &> /dev/null fi else wxi-ssh-keys-clean $ORG if [[ $USERNAME == "cwchristerw" ]] then wxi-ssh-keys-clean warengroup fi for file in ~/.ssh/keys/* do VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/keys/$(basename "$file" .pub) -X GET --header "X-Vault-Token: $TOKEN") if [[ $VAULT_STATUS -eq 200 ]] then rm "$file" &> /dev/null fi done fi wxi-footer } wxi-ssh-keys-generate(){ wxi-header "SSH / Keys / Generate" wxi-restricted if [[ ! -z $1 ]] then if [[ ! -f "$HOME/.ssh/keys/$1" ]] then ssh-keygen -t ed25519 -f $HOME/.ssh/keys/$1 -q -N "" -C "$USERNAME" &> /dev/null fi fi wxi-footer } wxi-ssh-keys-retrieve(){ wxi-header "SSH / Keys / Retrieve" wxi-restricted if [[ ! -z $1 ]] then VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/keys/$1 -X GET --header "X-Vault-Token: $TOKEN") if [[ $VAULT_STATUS -eq 200 ]] then echo $(curl https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/keys/$1 -X GET --header "X-Vault-Token: $TOKEN" -s | jq -r '.data.data.private') | base64 -d > ~/.ssh/keys/$1 2>&1 chmod 700 ~/.ssh/keys/$1 echo $(curl https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/keys/$1 -X GET --header "X-Vault-Token: $TOKEN" -s | jq -r '.data.data.public') | base64 -d > ~/.ssh/keys/$1.pub 2>&1 chmod 700 ~/.ssh/keys/$1.pub fi fi wxi-footer } wxi-ssh-keys-save(){ wxi-header "SSH / Keys / Save" wxi-restricted if [[ ! -z $1 ]] then if [[ -f "$HOME/.ssh/keys/$1" ]] then curl https://$VAULT_DOMAIN/v1/cli/data/$USERNAME/settings/ssh/keys/$1 -X POST --header "X-Vault-Token: $TOKEN" -d "{ \"data\": { \"private\": \"$(cat ~/.ssh/keys/$1 | base64 -w 0)\", \"public\": \"$(cat ~/.ssh/keys/$1.pub | base64 -w 0)\" } }" -s &> /dev/null fi fi wxi-footer } wxi-ssh-keys-sign(){ wxi-header "SSH / Keys / Sign" wxi-restricted wxi-ssh-keys-sign-create $ORG sysadmin 3600 if [[ $USERNAME == "cwchristerw" ]] then wxi-ssh-keys-sign-create warengroup sysadmin 3600 fi wxi-footer } wxi-ssh-keys-sign-create(){ NAME=$1 ROLE=$2 PRINCIPALS=$2 TTL=$3 wxi-ssh-keys-generate $NAME &> /dev/null if [[ -f "$HOME/.ssh/keys/$NAME" ]] then wxi-content text "$NAME/$ROLE" echo $(curl https://$VAULT_DOMAIN/v1/ssh/sign/$ROLE -X POST --header "X-Vault-Token: $TOKEN" -d "{ \"public_key\": \"$(cat $HOME/.ssh/keys/$NAME.pub)\", \"valid_principals\": \"$PRINCIPALS,$USERNAME\", \"ttl\": \"$TTL\" }" -s | jq -r '.data.signed_key') > ~/.ssh/keys/$NAME.sig 2>&1 fi } wxi-ssh-keys-sync(){ wxi-header "SSH / Keys / Sync" wxi-restricted VAULT_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$VAULT_DOMAIN/v1/cli/metadata/$USERNAME/settings/ssh/keys -X LIST --header "X-Vault-Token: $TOKEN") if [[ $VAULT_STATUS -eq 200 ]] then for name in $(curl https://$VAULT_DOMAIN/v1/cli/metadata/$USERNAME/settings/ssh/keys -X LIST --header "X-Vault-Token: $TOKEN" -s | jq -r '.data.keys | @sh' | tr -d \') do wxi-content text $name wxi-ssh-keys-retrieve $name &> /dev/null done fi wxi-footer } i=1 while [[ "$1" != "" ]] do case $1 in --*) key="${1%%=*}" value="${1#*=}" if [[ "$value" == "$key" ]] then shift value="$1" fi if [[ -z $value ]] then value=true fi args["${key#--}"]="$value" ;; -*) key="${1%=*}" value="${1#*=}" if [[ "$value" == "$key" ]] then shift value="$1" fi if [[ -z $value ]] then value=true fi args["${key#-}"]="$value" ;; *) args["$i"]="${1%%=*}" i=$((i + 1)) ;; esac shift done if [[ ! -z ${args['1']} ]] && [[ $(type -t wx-${args['1']}) == function ]] then wx-${args['1']} else wx-welcome fi wxi-stop