diff --git a/README.md b/README.md index 80339be..5537f4c 100644 --- a/README.md +++ b/README.md @@ -54,4 +54,26 @@ end This is how you would repeatedly poll the SFAPI to check if the command is done running. `SFAPI.Executable.result` does repeatedly check the status, and fetches the result when done (continuously refreshing the API token). So progress is -made even when not checking `istaskdone`. \ No newline at end of file +made even when not checking `istaskdone`. + +### Obtaining a sshproxy key/certificate + +You can create a script like the following to use on the command line. + +```bash +#!/usr/bin/env julia +include("/Superfacility") +push!(LOAD_PATH, "/Superfacility/Superfacility") +using Superfacility: SshProxy +SshProxy.sshproxy() +``` + +``` +$ ./sshproxy_jl -h +usage: sshproxy_jl [--username USERNAME] [--collab COLLAB] [-h] + +optional arguments: + --username USERNAME + --collab COLLAB + -h, --help show this help message and exit +``` diff --git a/Superfacility/Project.toml b/Superfacility/Project.toml index ab5ff38..d934d8b 100644 --- a/Superfacility/Project.toml +++ b/Superfacility/Project.toml @@ -1,7 +1,7 @@ name = "Superfacility" uuid = "f7a38f07-e5d6-4eec-ba19-7d8f01225f45" authors = ["Johannes Blaschke "] -version = "0.1.0" +version = "0.2.0" [deps] Chain = "8be319e6-bccf-4806-a6f7-6fae938471bc" @@ -12,6 +12,9 @@ JSONWebTokens = "9b8beb19-0777-58c6-920b-28f749fee4d3" ResultTypes = "08a2b407-ddc3-586a-afd6-c784ad1fffe2" TimeZones = "f269a46b-ccf7-5d73-abea-4c690281aa53" URIs = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" +ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" +Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + [compat] julia = "1.6" @@ -21,4 +24,4 @@ JSON = "0.21" JSONWebTokens = "1" ResultTypes = "3" TimeZones = "1" -URIs = "1" \ No newline at end of file +URIs = "1" diff --git a/Superfacility/src/Superfacility.jl b/Superfacility/src/Superfacility.jl index 9453262..7b652b5 100644 --- a/Superfacility/src/Superfacility.jl +++ b/Superfacility/src/Superfacility.jl @@ -26,4 +26,8 @@ include("status.jl") using .Status end # module SFAPI + +include("sshproxy.jl") +using .SshProxy + end diff --git a/Superfacility/src/sshproxy.jl b/Superfacility/src/sshproxy.jl new file mode 100644 index 0000000..7b74056 --- /dev/null +++ b/Superfacility/src/sshproxy.jl @@ -0,0 +1,88 @@ +module SshProxy + +using HTTP +using JSON +using Base64 +using ArgParse + +const KEY_PAIR_URL = "https://sshproxy.nersc.gov/create_pair/default/" +const COLLAB_URL = "https://sshproxy.nersc.gov/create_pair/collab/" + +function get_key(username::Union{Nothing, String}, collab::Union{Nothing, String}) + url, data, key_name = if isnothing(collab) + (KEY_PAIR_URL, Dict(), "nersc") + else + (COLLAB_URL, Dict("target_user" => collab), collab) + end + + username = username === nothing ? ENV["USER"] : username + + passwd = Base.getpass("Password+OTP") + user_pw = read(passwd, String) + + keypath = joinpath(ENV["HOME"], ".ssh") + private = joinpath(keypath, key_name) + public = joinpath(keypath, "$key_name.pub") + pub_cert = joinpath(keypath, "$key_name-cert.pub") + + isfile(private) && rm(private) + isfile(public) && rm(public) + + res = HTTP.post(url, body=JSON.json(data), + headers=Dict("Content-Type" => "Application/json", "Authorization" => "Basic $(Base64.base64encode("$username:$user_pw"))")) + Base.shred!(passwd) + + if res.status != 200 + throw(ErrorException("Failed to get key: $(res.status)")) + end + + out = String(res.body) + pub_index = findfirst("ssh-rsa-cert-v01", out) + + if pub_index === nothing + throw(ErrorException("Invalid response from server")) + end + + key_sep = "-----END RSA PRIVATE KEY-----\n" + private_key, pub_key = split(out, key_sep) + private_key = string(private_key, key_sep) + + # Write the files + open(pub_cert, "w") do f + write(f, pub_key) + end + open(private, "w") do f + write(f, private_key) + end + chmod(private, 0o600) + + public_pem = read(`ssh-keygen -y -f $private`, String) + open(public, "w") do f + write(f, public_pem) + end + +end + +function sshproxy() + s = ArgParseSettings() + @add_arg_table s begin + "--username" + "--collab" + end + + parsed_args = parse_args(s) + username = get(parsed_args, "username", nothing) + collab = get(parsed_args, "collab", nothing) + + try + get_key(username, collab) + catch e + rethrow(e) + exit(1) + end + exit(0) +end + +export sshproxy + +end