Re-implement guru vpn script
This commit is contained in:
parent
3f5ce5dddc
commit
c1a09652ab
7
scripts/guru-sophos-password
Executable file
7
scripts/guru-sophos-password
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BASE_PW=$(op read "op://t4auhpfxnrrtyvfml4kysvgb2i/rqinfxfqr7z5e2gmix6beiqcxy/password")
|
||||||
|
OTP=$(op read "op://t4auhpfxnrrtyvfml4kysvgb2i/rqinfxfqr7z5e2gmix6beiqcxy/one-time password?attribute=otp")
|
||||||
|
|
||||||
|
echo ${BASE_PW}${OTP}
|
165
scripts/guru-vpn
165
scripts/guru-vpn
@ -1,142 +1,37 @@
|
|||||||
#! /usr/bin/env python3
|
#! /bin/bash
|
||||||
import argparse
|
set -e
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
|
# Require 1 argument (connect or disconnect)
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Usage: guru-vpn <connect|disconnect>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
def has_ykman() -> bool:
|
# If we are told to disconnect
|
||||||
try:
|
if [ "$1" == "disconnect" ]; then
|
||||||
subprocess.run(["ykman", "--version"], check=True, stdout=subprocess.DEVNULL)
|
echo "Disconnecting..."
|
||||||
return True
|
nmcli connection down "Guru VPN"
|
||||||
except subprocess.CalledProcessError:
|
exit 0
|
||||||
return False
|
fi
|
||||||
|
|
||||||
|
# If we are told to connect
|
||||||
|
if [ "$1" == "connect" ]; then
|
||||||
|
# Attempt to disconnect, just in case we are on a stale connection
|
||||||
|
echo "Attempting to leave stale connection if it exists"
|
||||||
|
nmcli connection down "Guru VPN" | true
|
||||||
|
|
||||||
def has_yk_plugged_in() -> bool:
|
# Generate the password
|
||||||
devices = subprocess.run(["ykman", "list"], check=True, stdout=subprocess.PIPE)
|
echo "Constructing one-time password"
|
||||||
devices = devices.stdout.decode("utf-8").split("\n")
|
vpn_pass=$(guru-sophos-password)
|
||||||
return len(devices) > 1
|
|
||||||
|
|
||||||
|
# Connect
|
||||||
|
echo "Connecting..."
|
||||||
|
nmcli connection modify "Guru VPN" vpn.secrets "password=${vpn_pass}"
|
||||||
|
nmcli connection up "Guru VPN"
|
||||||
|
|
||||||
def is_interface_up(interface: str) -> bool:
|
exit 0
|
||||||
try:
|
fi
|
||||||
subprocess.run(
|
|
||||||
["ip", "link", "show", interface], check=True, stdout=subprocess.DEVNULL
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
# If we are here, we were given an invalid command
|
||||||
def get_oath_code(service: str) -> Optional[int]:
|
echo "Usage: guru-vpn <connect|disconnect>"
|
||||||
response = subprocess.run(
|
exit 1
|
||||||
["ykman", "oath", "accounts", "code", "Guru"],
|
|
||||||
check=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
output = response.stdout.decode("utf-8")
|
|
||||||
if not output:
|
|
||||||
return None
|
|
||||||
return int(output.split("\n")[0].split(" ")[-1])
|
|
||||||
|
|
||||||
|
|
||||||
def get_password(label: str, ns: str, key: str) -> str:
|
|
||||||
# Try to find it
|
|
||||||
try:
|
|
||||||
result = subprocess.run(
|
|
||||||
["secret-tool", "lookup", ns, key], check=True, stdout=subprocess.PIPE
|
|
||||||
)
|
|
||||||
return result.stdout.decode("utf-8")
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
# If we are here, it doesn't exist
|
|
||||||
print(f"Enter your {label}")
|
|
||||||
subprocess.run(["secret-tool", "store", "--label", label, ns, key], check=True)
|
|
||||||
return get_password(label, ns, key)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_connect(args: argparse.Namespace) -> int:
|
|
||||||
if not has_yk_plugged_in():
|
|
||||||
print("Could not find YubiKey. Is it plugged in?", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# If we are connected to AS54041, we need to briefly kill the connection
|
|
||||||
if args.wireguard_support and is_interface_up("vpn"):
|
|
||||||
print("Bringing down AS54041 VPN")
|
|
||||||
subprocess.run(["sudo", "wg-quick", "down", "vpn"], check=True)
|
|
||||||
|
|
||||||
# Get the base password
|
|
||||||
base_password = get_password("Guru VPN Password", "guru-vpn", "base-password")
|
|
||||||
|
|
||||||
# Fetch the credentials from the Yubikey
|
|
||||||
oath_code = get_oath_code("Guru")
|
|
||||||
print(f"Using OATH code: {oath_code}")
|
|
||||||
if not len(str(oath_code)) == 6:
|
|
||||||
print("Invalid OATH code length. Try again in a minute.", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# Construct the one-time password
|
|
||||||
password = f"{base_password}{oath_code}"
|
|
||||||
|
|
||||||
# Connect via nmcli
|
|
||||||
print("Bringing up Guru VPN")
|
|
||||||
subprocess.run(
|
|
||||||
[
|
|
||||||
"nmcli",
|
|
||||||
"connection",
|
|
||||||
"modify",
|
|
||||||
"Guru VPN",
|
|
||||||
"vpn.secrets",
|
|
||||||
f"password={password}",
|
|
||||||
],
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
subprocess.run(["nmcli", "connection", "up", "Guru VPN"], check=True)
|
|
||||||
|
|
||||||
# Bring AS54041 back up
|
|
||||||
print("Bringing up AS54041 VPN")
|
|
||||||
if args.wireguard_support:
|
|
||||||
subprocess.run(["sudo", "wg-quick", "up", "vpn"], check=True)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_disconnect(args: argparse.Namespace) -> int:
|
|
||||||
# Disconnect from Guru VPN
|
|
||||||
print("Bringing down Guru VPN")
|
|
||||||
result = subprocess.run(["nmcli", "connection", "down", "Guru VPN"])
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
# Handle program arguments
|
|
||||||
ap = argparse.ArgumentParser(
|
|
||||||
prog="guru-vpn", description="Utility for connecting to the Guru VPN"
|
|
||||||
)
|
|
||||||
ap.add_argument(
|
|
||||||
"operation",
|
|
||||||
choices=["connect", "disconnect", "reconnect"],
|
|
||||||
help="Operation to perform",
|
|
||||||
)
|
|
||||||
ap.add_argument(
|
|
||||||
"-w",
|
|
||||||
"--wireguard-support",
|
|
||||||
help="Handles wireguard interfaces",
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
args = ap.parse_args()
|
|
||||||
|
|
||||||
# Ensure we can actually get credentials from the Yubikey
|
|
||||||
if not has_ykman():
|
|
||||||
print("Could not execute `ykman`. Is it installed?", file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# Handle subcommands
|
|
||||||
cmd_fns = {
|
|
||||||
"connect": lambda args: handle_disconnect(args) or handle_connect(args),
|
|
||||||
"disconnect": handle_disconnect,
|
|
||||||
}
|
|
||||||
return cmd_fns[args.operation](args)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
Loading…
x
Reference in New Issue
Block a user