From e96397a9e8e9988be8937672b1ad22ed1018794b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christer=20War=C3=A9n?= Date: Thu, 1 Oct 2026 11:03:32 +0300 Subject: [PATCH] CLI: Update --- wx | 441 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) diff --git a/wx b/wx index f34a9b1..1a0e491 100755 --- a/wx +++ b/wx @@ -362,6 +362,52 @@ wxi-repeat() { fi } +wx-eas(){ + wxi-header "Emergency Access System" h2 + + LOCATION="$WX_HOME/credentials" + wxi-eas-login + + wxi-repeat "\n" 1 + + case ${args['2']} in + data) + wxi-eas-data + ;; + token) + wxi-eas-token + ;; + *) + wxi-content status "Error" "This feature isn't implemented yet" + ;; + esac +} + +wxi-eas-login(){ + if [[ $(systemd-detect-virt) == "wsl" ]] + then + echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0666", TAG+="uaccess", GROUP="plugdev", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0407"' | sudo tee /etc/udev/rules.d/99-yubikey.rules > /dev/null + sudo udevadm control --reload + fi + + YUBIKEYS=$(ykman list --serials 2>/dev/null | jq -R -s 'split("\n")[:-1]') + + if [[ $(echo $YUBIKEYS | jq 'length') != 1 ]] + then + if [[ ${args['1']} == "eas" ]] + then + wxi-content status "Error" "You need to attach atleast one Yubikey and unplug all other Yubikeys" + wxi-stop + fi + else + SERIAL=$(echo $YUBIKEYS | jq -r '.[0]') + wxi-content text "Emergency Access System - Serial Number: $SERIAL" + export SERIAL + fi + + sudo systemctl enable --now pcscd &> /dev/null +} + wx-help(){ wxi-header "Help" @@ -685,7 +731,153 @@ wx-logout(){ } +wxi-eas-data(){ + case ${args['3']} in + encrypt) + wxi-eas-data-encrypt + ;; + decrypt) + wxi-eas-data-decrypt + ;; + retrieve) + wxi-eas-data-retrieve + ;; + deliver) + wxi-eas-data-deliver + ;; + *) + wx-help + ;; + esac +} + +wxi-eas-token(){ + case ${args['3']} in + create) + wxi-eas-token-create + ;; + erase) + wxi-eas-token-erase + ;; + sign) + wxi-eas-token-sign + ;; + esac +} + wx-install(){ + if [[ "${args['1']}" == "install" ]] + then + wxi-header "Install" h2 + fi + + if [[ ! -L "$HOME/bin/wx" && ! -f "/opt/ansible/bin/ansible-playbook" && ! -f "/bin/jq" && ! -f "/bin/podman" && ! -f "/bin/ykman"|| "${args['1']}" == "upgrade" ]] + then + cd "$PWD" + + wxi-repeat "\n" 1 + + wxi-header "Git" h3 + git stash -- cli.sh + git pull + wxi-repeat "\n" 1 + + wxi-header "Elevated Privileges" h3 + sudo echo 'This command uses sudo!' + + wxi-repeat "\n" 1 + + wxi-header "Ansible" h3 + + wxi-content text* "Dependencies: " + sudo apt-get update &> /dev/null + sudo apt-get install -y python3-pip python3-venv jq git curl lsb-release sudo sshpass rsync &> /dev/null + wxi-content text "Ready ✔" + + wxi-content text* "Python 3 - Virtual Environment: " + sudo python3 -m venv /opt/ansible &> /dev/null + if [[ -d "/opt/ansible" ]] + then + wxi-content text "Ready ✔" + else + wxi-content text "Failed ✖" + fi + + wxi-content text* "Ansible: " + sudo /opt/ansible/bin/pip3 install ansible-core &> /dev/null + if [[ -f "/opt/ansible/bin/ansible-playbook" ]] + then + wxi-content text "Ready ✔" + else + wxi-content text "Failed ✖" + fi + + wxi-content text* "Python3 Libraries - Dependencies: " + sudo /opt/ansible/bin/pip3 install cryptography dnspython hvac jmespath netaddr passlib pexpect xmltodict ansi2html --upgrade &> /dev/null + wxi-content text "Ready ✔" + + wxi-content text* "Collections: " + sudo ln -s /opt/ansible/collections /usr/share/ansible/collections &> /dev/null + sudo /opt/ansible/bin/ansible-galaxy collection install -r ansible/requirements.yml -p /usr/share/ansible/collections --upgrade &> /dev/null + wxi-content text "Ready ✔" + + wxi-repeat "\n" 1 + + wxi-header "CLI Tool" h3 + + if [[ -d "./cli/src" ]] + then + wxi-content text* "Dependencies: " + sudo apt-get update &> /dev/null + sudo apt-get install -y jq yubikey-manager &> /dev/null + + if [[ -f "/bin/jq" && -f "/bin/ykman" ]] + then + wxi-content text "Ready ✔" + else + wxi-content text "Failed ✖" + fi + + wxi-content text* "Generate: " + python3 cli/generator.py + + if [[ -f "./cli/cli.sh" ]] + then + wxi-content text "Ready ✔" + + wxi-content text* "Deploying: " + mv -f ./cli/cli.sh ./cli.sh + chmod +x ./cli.sh + + if [[ ! -L "$HOME/bin/wx" ]] + then + mkdir -p "$HOME/bin" + ln -s "$PWD/cli.sh" "$HOME/bin/wx" + fi + + if [[ $(systemd-detect-virt) == "wsl" ]] + then + if [[ ! -L "/bin/wx" ]] + then + sudo ln -s "$PWD/cli.sh" "/bin/wx" + fi + fi + + if [[ -f "./cli.sh" ]] + then + wxi-content text "Ready ✔" + fi + else + wxi-content text "Failed ✖" + fi + fi + cd "$OLDPWD" + else + wxi-content text "You have already installed!" + fi +} + +wx-install2(){ wxi-header "Install" wxi-restricted --user @@ -744,6 +936,11 @@ wx-update(){ } +wx-upgrade(){ + wxi-header "Upgrade" h2 + wx-install +} + wx-auto(){ wx-login @@ -821,6 +1018,250 @@ wxi-ssh-keys(){ esac } +pxi-eas-data-decrypt(){ + pxi-header "Data - Encrypt" h3 + + if [[ -n ${args['confirm']} ]] + then + if [[ -f $LOCATION/ansible-vault/eas/$SERIAL && $(base64 "$LOCATION/ansible-vault/eas/$SERIAL") != "" ]] + then + i=0 + for file in "$PWD"/eas/data/$SERIAL/*/*/credentials; + do + i=$((i + 1)) + echo "$i)${file#"$PWD/eas/data/$SERIAL/"}" + ansible-vault decrypt --vault-id "Pori Entrepreneurship Society - Infra - Emergency Access System ($SERIAL)"@"$LOCATION/ansible-vault/eas/$SERIAL" "$file" + done + fi + else + pxi-content text "Confirmation Needed ✖ (--confirm)" + fi +} + +pxi-eas-data-deliver(){ + px-login vault + pxi-repeat "\n" 1 + pxi-header "Data - Deliver" h3 + + if [[ -n ${args['confirm']} ]] + then + cd "$PWD/ansible" + + playbook=cli + tasks=eas-data-deliver + limit="${args['limit']:-all}" + vaulted=(--vault-id "Pori Entrepreneurship Society - Infra - Emergency Access System ($SERIAL)@~/.pories/credentials/ansible-vault/eas/$SERIAL") + + /opt/ansible/bin/ansible-playbook $playbook.yml -t "$tasks" --limit "$limit,localhost" --extra-vars "${args['vars']}" "${vaulted[@]}" + cd "$OLDPWD" + else + pxi-content text "Confirmation Needed ✖ (--confirm)" + fi +} + +pxi-eas-data-encrypt(){ + pxi-header "Data - Encrypt" h3 + + if [[ -f $LOCATION/ansible-vault/eas/$SERIAL && $(base64 "$LOCATION/ansible-vault/eas/$SERIAL") != "" ]] + then + i=0 + for file in "$PWD"/eas/data/$SERIAL/*/*/credentials; + do + i=$((i + 1)) + echo "$i)${file#"$PWD/eas/data/$SERIAL/"}" + ansible-vault encrypt --vault-id "Pori Entrepreneurship Society - Infra - Emergency Access System ($SERIAL)"@"$LOCATION/ansible-vault/eas/$SERIAL" "$file" + done + fi +} + +pxi-eas-data-retrieve(){ + px-login vault + pxi-repeat "\n" 1 + pxi-header "Data - Retrieve" h3 + + if [[ -n ${args['confirm']} ]] + then + cd "$PWD/ansible" + + playbook=cli + tasks=eas-data-retrieve + limit="${args['limit']:-all}" + vaulted=(--vault-id "Pori Entrepreneurship Society - Infra - Emergency Access System ($SERIAL)@~/.pories/credentials/ansible-vault/eas/$SERIAL") + + /opt/ansible/bin/ansible-playbook $playbook.yml -t "$tasks" --limit "$limit,localhost" --extra-vars "${args['vars']}" "${vaulted[@]}" + cd "$OLDPWD" + else + pxi-content text "Confirmation Needed ✖ (--confirm)" + fi +} + +wxi-eas-token-create(){ + wxi-header "Token - Create" h3 + + wxi-header "Ansible Vault" h4 + if [[ $(ykman piv objects export 0x005FFF16 - 2>/dev/null) == "" ]] + then + wxi-content text* "PIN: " + read -s PIN + wxi-content text "******" + + if [[ -n $PIN ]] + then + wxi-content text* "Generating: " + echo $(LC_ALL=C tr -dc 'A-Z2-7' /dev/null) + if [[ $EAS_AVP != "" ]] + then + echo -e "Pori Entrepreneurship Society - Infra - Emergency Access System ($SERIAL) / Ansible Vault / $EAS_AVP" > "$LOCATION/ansible-vault/eas/$SERIAL" + if [[ -f "$LOCATION/ansible-vault/eas/$SERIAL" && $(base64 "$LOCATION/ansible-vault/eas/$SERIAL") != "" ]] + then + wxi-content text "Ready ✔" + else + wxi-content text "Failed ✖" + fi + else + wxi-content text "Failed ✖" + fi + + wxi-repeat "\n" 1 + + wxi-header "SSH" h4 + + if [[ ! -f "$LOCATION/ssh/eas/$SERIAL" || ! -f "$PWD/eas/credentials/ssh/$SERIAL.pub" ]] + then + if [[ ! -f "$LOCATION/ssh/eas/$SERIAL" && -f "$PWD/eas/credentials/ssh/$SERIAL.pub" ]] + then + OLDOLDPWD=$OLDPWD + cd "$LOCATION/ssh/eas" + + wxi-content text* "Retrieving: " + wxi-content text "Processing..." + + wxi-repeat "\n" 1 + + ssh-keygen -K + + wxi-repeat "\n" 1 + + if [[ -f "id_ed25519_sk_rk_warengroup-infra-eas-$SERIAL" ]] + then + mv "id_ed25519_sk_rk_warengroup-infra-eas-$SERIAL" "$SERIAL" + mv "id_ed25519_sk_rk_warengroup-infra-eas-$SERIAL.pub" "$SERIAL.pub" + + ssh-keygen -c -f "$LOCATION/ssh/eas/$SERIAL" -P "" -C "Pori Entrepreneurship Society - Infra - Emergency Access System ($SERIAL)" &> /dev/null + + wxi-content text* "Retrieving: " + wxi-content text "Ready ✔" + else + wxi-content text* "Retrieving: " + wxi-content text "Failed ✖" + fi + + wxi-repeat "\n" 1 + + wxi-content text* "Cleaning: " + rm -f id_ecdsa_sk_rk_* + rm -f id_ed25519_sk_rk_* + rm -f id_rsa_sk_rk_* + wxi-content text "Ready ✔" + + cd "$OLDPWD" + OLDPWD=$OLDOLDPWD + elif [[ ! -f "$LOCATION/ssh/eas/$SERIAL" ]] + then + wxi-content text* "Generating: " + wxi-content text "Processing..." + + wxi-repeat "\n" 1 + + ssh-keygen -t ed25519-sk -f "$LOCATION/ssh/eas/$SERIAL" -O resident -O verify-required -O "application=ssh:warengroup-infra-eas-$SERIAL" -N "" -C "Warén Group - Infra - Emergency Access System ($SERIAL)" + + wxi-repeat "\n" 1 + + if [[ -f "$LOCATION/ssh/eas/$SERIAL" ]] + then + wxi-content text* "Generating: " + wxi-content text "Ready ✔" + else + wxi-content text* "Generating: " + wxi-content text "Failed ✖" + fi + + wxi-repeat "\n" 1 + fi + + if [[ -f "$LOCATION/ssh/eas/$SERIAL.pub" && ! -f "$PWD/eas/credentials/ssh/$SERIAL.pub" ]] + then + wxi-content text* "Delivering: " + cp "$LOCATION/ssh/eas/$SERIAL.pub" "$PWD/eas/credentials/ssh/$SERIAL.pub" + if [[ -f "$PWD/eas/credentials/ssh/$SERIAL.pub" ]] + then + wxi-content text "Ready ✔" + else + wxi-content text "Failed ✖" + fi + fi + else + wxi-content text* "Processing: " + wxi-content text "Done ✔" + fi +} + +wxi-eas-token-erase(){ + wxi-header "Token - Erase" h3 + + wxi-content text* "Processing: " + + if [[ -f "$LOCATION/$serial" || -f "$LOCATION/$serial.pub" || -f "$PWD/eas/credentials/ssh/$serial.pub" ]] + then + if [[ -n ${args['confirm']} ]] + then + rm -f "$LOCATION/ssh/eas/$serial" + rm -f "$LOCATION/ssh/eas/$serial.pub" + rm -f "$PWD/eas/credentials/ssh/$serial.pub" + + if [[ ! -f "$LOCATION/ssh/eas/$serial" && ! -f "$LOCATION/ssh/eas/$serial.pub" && ! -f "$PWD/eas/credentials/ssh/$serial.pub" ]] + then + wxi-content text "Ready ✔" + else + wxi-content text "Failed ✖" + fi + else + wxi-content text "Confirmation Needed ✖ (--confirm)" + fi + else + wxi-content text "Done ✔" + fi +} + +wxi-eas-token-sign(){ + wxi-header "Token - Sign" h3 + + wxi-eas-token-sign-gen +} + +wxi-eas-token-sign-gen(){ + LOCATION="$WX_HOME/credentials" + if [[ -f "$LOCATION/ssh/eas/$SERIAL" ]] + then + if [[ ! -f "$LOCATION/ssh/eas/$SERIAL-" ]] + then + pxi-ssh-keys-generate "eas/$SERIAL-" "Warén Group - Infra - Emergency Access System ($SERIAL)" &> /dev/null + fi + + ssh-keygen -s "$LOCATION/ssh/eas/$SERIAL" -I "Warén Group - Infra - Emergency Access System ($SERIAL)" -n root -V -1m:+30m "$LOCATION/ssh/eas/$SERIAL-.pub" + mv -f "$LOCATION/ssh/eas/$SERIAL--cert.pub" "$LOCATION/ssh/eas/$SERIAL-.sig" + fi +} + wxi-ssh-config-clean(){ wxi-header "SSH / Config / Clean" wxi-restricted