Skip to content
This repository was archived by the owner on Mar 1, 2025. It is now read-only.

Commit af91010

Browse files
committed
Initial script revisions
1 parent c19ef3f commit af91010

File tree

4 files changed

+290
-0
lines changed

4 files changed

+290
-0
lines changed

common.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Signs into `op` or exits the script
2+
function op_signin {
3+
echo "Signing into 1Password..."
4+
eval "$(op signin $@ || echo 'echo "Sign-in failed." >&2; exit 1')"
5+
}
6+
7+
# Ideally something that won't ever actually be written to disk, but we'll worry
8+
# about that later
9+
export default_temp_storage_root="/tmp/op-ssh-utils"
10+
11+
# Writes the public/private key pair for a given vault item to the temporary
12+
# storage directory, and adds a rule to the temporary ssh config for the host
13+
# and user.
14+
function configure_keys {
15+
temp_storage_root=$1
16+
uuid=$2
17+
public_key=$3
18+
private_key=$4
19+
host=$5
20+
user=$6
21+
22+
# Initialize storage directory if not set up yet
23+
mkdir -p "$temp_storage_root/keys"
24+
if [ ! -f "$temp_storage_root/ssh_config" ]; then
25+
echo > "$temp_storage_root/ssh_config"
26+
fi
27+
28+
touch "$temp_storage_root/keys/$uuid"
29+
chmod 0600 "$temp_storage_root/keys/$uuid"
30+
echo "$private_key" > "$temp_storage_root/keys/$uuid"
31+
32+
touch "$temp_storage_root/keys/$uuid.pub"
33+
chmod 0644 "$temp_storage_root/keys/$uuid.pub"
34+
echo "$public_key" > "$temp_storage_root/keys/$uuid.pub"
35+
36+
cat <<-SSH_CONFIG >> "$temp_storage_root/ssh_config"
37+
Match host $host user $user
38+
IdentityFile $temp_storage_root/keys/$uuid
39+
SSH_CONFIG
40+
}

itemtemplate.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"sections": [
3+
{
4+
"name": "",
5+
"title": "",
6+
"fields": [
7+
{
8+
"k": "string",
9+
"n": "url",
10+
"t": "host",
11+
"v": "SSH_HOST"
12+
},
13+
{
14+
"k": "string",
15+
"n": "username",
16+
"t": "username",
17+
"v": "SSH_USERNAME"
18+
},
19+
{
20+
"k": "concealed",
21+
"n": "password",
22+
"t": "password",
23+
"v": "SSH_PASSWORD"
24+
},
25+
{
26+
"k": "string",
27+
"n": "ssh_public_key",
28+
"t": "public key",
29+
"v": "SSH_PUBLIC_KEY"
30+
},
31+
{
32+
"k": "concealed",
33+
"n": "ssh_private_key",
34+
"t": "private key",
35+
"v": "SSH_PRIVATE_KEY"
36+
}
37+
]
38+
}
39+
]
40+
}

op-add-identities

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/bin/bash
2+
3+
dirname="$(dirname "$(realpath $0)")"
4+
source "$dirname/common.sh"
5+
6+
# TODO: allow overriding this with an option
7+
temp_storage_root="$default_temp_storage_root"
8+
9+
op_signin $@
10+
11+
# Get all the items we care about
12+
# TODO: filter by a tag instead of by the server template?
13+
items=($(op list items | jq --raw-output '
14+
.[]
15+
| select(.templateUuid == "110")
16+
| .uuid
17+
'))
18+
for uuid in "${items[@]}"; do
19+
item_data="$(op get item "$uuid")"
20+
21+
item_title="$(echo "$item_data" | jq --raw-output '.overview.title')"
22+
# TODO: don't assume sections[0] is the right section
23+
item_fields="$(echo "$item_data" | jq '.details.sections[0].fields')"
24+
25+
host="$(echo "$item_fields" | jq --raw-output '
26+
.[] | select(.n == "url") | .v
27+
')"
28+
user="$(echo "$item_fields" | jq --raw-output '
29+
.[] | select(.n == "username") | .v
30+
')"
31+
private_key="$(echo "$item_fields" | jq --raw-output '
32+
.[] | select(.n == "ssh_private_key") | .v
33+
')"
34+
public_key="$(echo "$item_fields" | jq --raw-output '
35+
.[] | select(.n == "ssh_public_key") | .v
36+
')"
37+
38+
echo "Key: \"$item_title\" ($user@$host)"
39+
40+
configure_keys "$temp_storage_root" "$uuid" "$private_key" "$public_key" "$host" "$user"
41+
done
42+
43+
op signout
44+
45+
echo "Done."

op-create-identity

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#!/bin/bash
2+
3+
dirname="$(dirname "$(realpath $0)")"
4+
source "$dirname/common.sh"
5+
6+
# prints the script's usage options
7+
function print_help {
8+
echo "Usage:"
9+
echo " $0 -H <hostname> [options] [arguments to 'op signin']"
10+
echo " $0 -h"
11+
echo
12+
echo "Options:"
13+
echo " -h Print this help and exit"
14+
echo " -H <hostname>"
15+
echo " Remote hostname"
16+
echo " -i <identity>"
17+
echo " Use an existing identity instead of generating a new one (e.g."
18+
echo " ('-i id_rsa' will use private key 'id_rsa' and public key"
19+
echo " 'id_rsa.pub')"
20+
echo " -t <title>"
21+
echo " Title for the new 1Password item (default is user@host)"
22+
echo " -u <username>"
23+
echo " Remote username (default is current username)"
24+
echo " -y Perform all actions automatically"
25+
echo
26+
echo "See 'op signin --help' for additional arguments to 'op signin'."
27+
echo "(If you run 'op signin' at least once before running this script"
28+
echo "you shouldn't need any additional arguments.)"
29+
}
30+
31+
# prompts the user to continue (returns 0), skip (returns 1), or abort (exit the
32+
# script immediately), using the arguments as a prompt message
33+
function confirm {
34+
if [ ! -z $skip_confirms ]; then
35+
echo "$* (-y specified, continuing)"
36+
return 0
37+
fi
38+
39+
while true; do
40+
read -p "$* [(continue)/skip/abort]: " answer
41+
42+
case $answer in
43+
[Cc]|continue|"")
44+
return 0
45+
;;
46+
[Ss]|skip)
47+
return 1
48+
;;
49+
[Aa]|abort)
50+
echo "Aborted"
51+
exit 0
52+
;;
53+
esac
54+
done
55+
}
56+
57+
OPTIND=1
58+
ssh_host=""
59+
ssh_user="$(id -u -n)"
60+
title=""
61+
key_file=""
62+
skip_confirms=""
63+
while getopts "hH:i:t:u:y" opt; do
64+
case "$opt" in
65+
h)
66+
print_help
67+
exit 0
68+
;;
69+
H)
70+
ssh_host="$OPTARG"
71+
;;
72+
i)
73+
key_file="$OPTARG"
74+
;;
75+
t)
76+
title="$OPTARG"
77+
;;
78+
u)
79+
ssh_user="$OPTARG"
80+
;;
81+
y)
82+
skip_confirms="1"
83+
;;
84+
esac
85+
done
86+
shift $((OPTIND-1))
87+
[ "${1:-}" = "--" ] && shift
88+
89+
# TODO: allow overriding this with an option
90+
temp_storage_root="$default_temp_storage_root"
91+
92+
if [ -z $ssh_host ]; then
93+
echo "Option -H is required" >&2
94+
print_help
95+
exit 1
96+
fi
97+
98+
if [ -z "$key_file" ]; then
99+
cleanup_key_pair="1"
100+
key_file="$dirname/temp_id_rsa"
101+
# TODO: generate this somewhere it won't be written to disk (use temp dir?)
102+
# TODO: can we read the new keys into variables here and immediately delete
103+
# the file to avoid having to set another variable to remember to
104+
# delete it later?
105+
echo "Generating new keypair..."
106+
# TODO: option for customizing key comment
107+
ssh-keygen -f "$key_file" -N "" -C "$(id -un)@$(hostname) -> $ssh_user@$ssh_host" -q
108+
else
109+
if [ -f "$key_file" ] && [ -f "$key_file.pub" ]; then
110+
echo "Using existing keypair $key_file and $key_file.pub (specified by -f)"
111+
else
112+
echo "One of $key_file and $key_file.pub does not exist (specified by -f)" >&2
113+
exit 1
114+
fi
115+
fi
116+
117+
op_signin $@
118+
119+
template="$(cat $dirname/itemtemplate.json)"
120+
item_data="$(echo "$template" | jq \
121+
--arg ssh_host "$ssh_host" \
122+
--arg ssh_user "$ssh_user" \
123+
--rawfile ssh_private_key "$key_file" \
124+
--rawfile ssh_public_key "$key_file.pub" \
125+
'
126+
(.sections[0].fields[] | select(.n == "url")) |= (
127+
. | .v |= $ssh_host
128+
) |
129+
(.sections[0].fields[] | select(.n == "username")) |= (
130+
. | .v |= $ssh_user
131+
) |
132+
(.sections[0].fields[] | select(.n == "ssh_private_key")) |= (
133+
. | .v = $ssh_private_key
134+
) |
135+
(.sections[0].fields[] | select(.n == "ssh_public_key")) |= (
136+
. | .v = $ssh_public_key
137+
)
138+
'
139+
)"
140+
141+
if confirm "Saving new item in 1password"; then
142+
encoded_item_data="$(echo "$item_data" | op encode)"
143+
new_uuid="$(op create item Server --title "${title:-$ssh_host}" "$encoded_item_data" | jq '.uuid' --raw-output)"
144+
else
145+
# you can skip uploading the key to 1Password, but we still need a fake
146+
# UUID so the key can have a name in the filesystem if stored
147+
new_uuid="_local_only_$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 14 | head -n 1)"
148+
fi
149+
150+
if confirm "Copying public key to host: ssh-copy-id -i '$key_file' '$ssh_host'"; then
151+
ssh-copy-id -i "$key_file" "$ssh_host"
152+
fi
153+
154+
if confirm "Adding key for local use"; then
155+
# cat "$key_file" | ssh-add -
156+
configure_keys "$temp_storage_root" "$new_uuid" "$private_key" "$public_key" "$ssh_host" "$ssh_user"
157+
fi
158+
159+
if [ ! -z $cleanup_key_pair ]; then
160+
rm "$key_file" "$key_file.pub"
161+
fi
162+
163+
op signout
164+
165+
echo "Done."

0 commit comments

Comments
 (0)