Skip to content

Commit 0b20d46

Browse files
committed
port ssh keys from build.func into cloudinit
1 parent e7057f2 commit 0b20d46

1 file changed

Lines changed: 105 additions & 37 deletions

File tree

misc/cloud-init.func

Lines changed: 105 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -60,20 +60,54 @@ function _ci_ssh_discover_files() {
6060
cand+=(/root/.ssh/*.pub)
6161
cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*)
6262
shopt -u nullglob
63-
printf '%s\n' "${cand[@]}"
63+
printf '%s\0' "${cand[@]}"
6464
}
6565

6666
# ------------------------------------------------------------------------------
67-
# _ci_ssh_count_keys - Counts available SSH keys on the system
68-
# ------------------------------------------------------------------------------
69-
function _ci_ssh_count_keys() {
70-
local count=0
71-
while IFS= read -r file; do
72-
[[ -f "$file" && -r "$file" ]] || continue
73-
local keys=$(_ci_ssh_extract_keys_from_file "$file" | wc -l)
74-
count=$((count + keys))
75-
done < <(_ci_ssh_discover_files)
76-
echo "$count"
67+
# _ci_ssh_build_choices - Builds whiptail checklist from SSH key files
68+
#
69+
# Sets: CI_SSH_CHOICES (array), CI_SSH_COUNT (int), CI_SSH_MAPFILE (path)
70+
# ------------------------------------------------------------------------------
71+
function _ci_ssh_build_choices() {
72+
local -a files=("$@")
73+
CI_SSH_CHOICES=()
74+
CI_SSH_COUNT=0
75+
CI_SSH_MAPFILE="$(mktemp)"
76+
local id key typ fp cmt base
77+
78+
for f in "${files[@]}"; do
79+
[[ -f "$f" && -r "$f" ]] || continue
80+
base="$(basename -- "$f")"
81+
# Skip known_hosts and private keys
82+
case "$base" in
83+
known_hosts | known_hosts.* | config) continue ;;
84+
id_*) [[ "$f" != *.pub ]] && continue ;;
85+
esac
86+
87+
while IFS= read -r key; do
88+
[[ -n "$key" ]] || continue
89+
90+
typ=""
91+
fp=""
92+
cmt=""
93+
read -r _typ _b64 _cmt <<<"$key"
94+
typ="${_typ:-key}"
95+
cmt="${_cmt:-}"
96+
97+
# Get fingerprint via ssh-keygen if available
98+
if command -v ssh-keygen >/dev/null 2>&1; then
99+
fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')"
100+
fi
101+
102+
# Shorten long comments
103+
[[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..."
104+
105+
CI_SSH_COUNT=$((CI_SSH_COUNT + 1))
106+
id="K${CI_SSH_COUNT}"
107+
echo "${id}|${key}" >>"$CI_SSH_MAPFILE"
108+
CI_SSH_CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }${base}" "OFF")
109+
done < <(_ci_ssh_extract_keys_from_file "$f")
110+
done
77111
}
78112

79113
# ------------------------------------------------------------------------------
@@ -84,35 +118,49 @@ function _ci_ssh_count_keys() {
84118
# ------------------------------------------------------------------------------
85119
function configure_cloudinit_ssh_keys() {
86120
local backtitle="Proxmox VE Helper Scripts"
87-
local default_key_count=$(_ci_ssh_count_keys)
88121
local ssh_key_mode
89122

90123
# Create temp file for selected keys
91124
CLOUDINIT_SSH_KEYS_TEMP="$(mktemp)"
125+
: >"$CLOUDINIT_SSH_KEYS_TEMP"
126+
127+
# Discover keys and build choices
128+
IFS=$'\0' read -r -d '' -a _def_files < <(_ci_ssh_discover_files && printf '\0')
129+
_ci_ssh_build_choices "${_def_files[@]}"
130+
local default_key_count="$CI_SSH_COUNT"
92131

93132
if [[ "$default_key_count" -gt 0 ]]; then
94133
ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \
95134
"Provision SSH keys for Cloud-Init VM:" 14 72 4 \
96-
"host" "Import all keys from host ($default_key_count found)" \
135+
"found" "Select from detected keys (${default_key_count})" \
97136
"manual" "Paste a single public key" \
98-
"file" "Specify path to authorized_keys file" \
137+
"folder" "Scan another folder (path or glob)" \
99138
"none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1
100139
else
101140
ssh_key_mode=$(whiptail --backtitle "$backtitle" --title "SSH KEY SOURCE" --menu \
102141
"No host keys detected. Choose:" 12 72 3 \
103142
"manual" "Paste a single public key" \
104-
"file" "Specify path to authorized_keys file" \
143+
"folder" "Scan another folder (path or glob)" \
105144
"none" "No SSH keys (password auth only)" 3>&1 1>&2 2>&3) || return 1
106145
fi
107146

108147
case "$ssh_key_mode" in
109-
host)
110-
# Import all keys from host
111-
while IFS= read -r file; do
112-
_ci_ssh_extract_keys_from_file "$file" >>"$CLOUDINIT_SSH_KEYS_TEMP"
113-
done < <(_ci_ssh_discover_files)
114-
local imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
115-
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} keys imported from host${CL}"
148+
found)
149+
# Show checklist with individual keys
150+
local selection
151+
selection=$(whiptail --backtitle "$backtitle" --title "SELECT SSH KEYS" \
152+
--checklist "Select one or more keys to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1
153+
154+
for tag in $selection; do
155+
tag="${tag%\"}"
156+
tag="${tag#\"}"
157+
local line
158+
line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-)
159+
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP"
160+
done
161+
local imported
162+
imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
163+
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) selected${CL}"
116164
;;
117165
manual)
118166
local pubkey
@@ -124,33 +172,53 @@ function configure_cloudinit_ssh_keys() {
124172
else
125173
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (empty input)${CL}"
126174
CLOUDINIT_SSH_KEYS=""
127-
rm -f "$CLOUDINIT_SSH_KEYS_TEMP"
175+
rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null
128176
return 0
129177
fi
130178
;;
131-
file)
132-
local keyfile
133-
keyfile=$(whiptail --backtitle "$backtitle" --title "SSH KEY FILE" \
134-
--inputbox "Enter path to authorized_keys file:" 10 60 "/root/.ssh/authorized_keys" 3>&1 1>&2 2>&3) || return 1
135-
if [[ -f "$keyfile" ]]; then
136-
_ci_ssh_extract_keys_from_file "$keyfile" >"$CLOUDINIT_SSH_KEYS_TEMP"
137-
local imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
138-
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} keys from ${keyfile}${CL}"
139-
else
140-
echo -e "${ROOTSSH:- 🔑 }${BOLD}${RD}File not found: ${keyfile}${CL}"
141-
CLOUDINIT_SSH_KEYS=""
142-
rm -f "$CLOUDINIT_SSH_KEYS_TEMP"
143-
return 1
179+
folder)
180+
local glob_path
181+
glob_path=$(whiptail --backtitle "$backtitle" --title "SCAN FOLDER/GLOB" \
182+
--inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub):" 10 72 3>&1 1>&2 2>&3) || return 1
183+
if [[ -n "$glob_path" ]]; then
184+
shopt -s nullglob
185+
local -a _scan_files=($glob_path)
186+
shopt -u nullglob
187+
if [[ "${#_scan_files[@]}" -gt 0 ]]; then
188+
_ci_ssh_build_choices "${_scan_files[@]}"
189+
if [[ "$CI_SSH_COUNT" -gt 0 ]]; then
190+
local folder_selection
191+
folder_selection=$(whiptail --backtitle "$backtitle" --title "SELECT FOLDER KEYS" \
192+
--checklist "Select key(s) to import:" 20 140 10 "${CI_SSH_CHOICES[@]}" 3>&1 1>&2 2>&3) || return 1
193+
for tag in $folder_selection; do
194+
tag="${tag%\"}"
195+
tag="${tag#\"}"
196+
local line
197+
line=$(grep -E "^${tag}\|" "$CI_SSH_MAPFILE" | head -n1 | cut -d'|' -f2-)
198+
[[ -n "$line" ]] && printf '%s\n' "$line" >>"$CLOUDINIT_SSH_KEYS_TEMP"
199+
done
200+
local imported
201+
imported=$(wc -l <"$CLOUDINIT_SSH_KEYS_TEMP")
202+
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}${imported} key(s) from folder${CL}"
203+
else
204+
whiptail --backtitle "$backtitle" --msgbox "No keys found in: $glob_path" 8 60
205+
fi
206+
else
207+
whiptail --backtitle "$backtitle" --msgbox "Path/glob returned no files." 8 60
208+
fi
144209
fi
145210
;;
146211
none | *)
147212
echo -e "${ROOTSSH:- 🔑 }${BOLD}${DGN}SSH Keys: ${BGN}none (password auth only)${CL}"
148213
CLOUDINIT_SSH_KEYS=""
149-
rm -f "$CLOUDINIT_SSH_KEYS_TEMP"
214+
rm -f "$CLOUDINIT_SSH_KEYS_TEMP" "$CI_SSH_MAPFILE" 2>/dev/null
150215
return 0
151216
;;
152217
esac
153218

219+
# Cleanup mapfile
220+
rm -f "$CI_SSH_MAPFILE" 2>/dev/null
221+
154222
# Set the variable for setup_cloud_init to use
155223
if [[ -s "$CLOUDINIT_SSH_KEYS_TEMP" ]]; then
156224
CLOUDINIT_SSH_KEYS="$CLOUDINIT_SSH_KEYS_TEMP"

0 commit comments

Comments
 (0)