Valentin Lab
4 years ago
2 changed files with 358 additions and 9 deletions
-
24README.org
-
343bin/0km
@ -0,0 +1,343 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
|
||||
|
. /etc/shlib |
||||
|
|
||||
|
include common |
||||
|
include parse |
||||
|
include cmdline |
||||
|
include config |
||||
|
|
||||
|
|
||||
|
[[ "${BASH_SOURCE[0]}" != "${0}" ]] && SOURCED=true |
||||
|
|
||||
|
version=0.1 |
||||
|
desc='Manage 0k related installs' |
||||
|
help="" |
||||
|
|
||||
|
|
||||
|
is-port-open() { |
||||
|
local host="$1" port="$2" timeout=5 |
||||
|
start="$SECONDS" |
||||
|
debug "Testing if $host's port $2 is open ..." |
||||
|
while true; do |
||||
|
timeout 1 bash -c "</dev/tcp/${host}/${port}" >/dev/null 2>&1 && break |
||||
|
sleep 0.2 |
||||
|
if [ "$((SECONDS - start))" -gt "$timeout" ]; then |
||||
|
return 1 |
||||
|
fi |
||||
|
done |
||||
|
} |
||||
|
|
||||
|
resolve() { |
||||
|
local ent hostname="$1" |
||||
|
debug "Resolving $1 ..." |
||||
|
if ent=$(getent ahosts "$hostname"); then |
||||
|
ent=$(echo "$ent" | egrep ^"[0-9]+.[0-9]+.[0-9]+.[0-9]+\s+" | \ |
||||
|
head -n 1 | awk '{ print $1 }') |
||||
|
debug " .. resolved $1 to $ent." |
||||
|
echo "$ent" |
||||
|
else |
||||
|
debug " .. couldn't resolve $1." |
||||
|
return 1 |
||||
|
fi |
||||
|
} |
||||
|
|
||||
|
|
||||
|
set_errlvl() { return "${1:-1}"; } |
||||
|
|
||||
|
export master_pid=$$ |
||||
|
ssh:open() { |
||||
|
local hostname ssh_cmd ssh_options |
||||
|
ssh_cmd=(ssh) |
||||
|
ssh_options=() |
||||
|
while [ "$#" != 0 ]; do |
||||
|
case "$1" in |
||||
|
"--stdin-password") |
||||
|
ssh_cmd=(sshpass "${ssh_cmd[@]}") |
||||
|
;; |
||||
|
-o) |
||||
|
ssh_options+=("$1" "$2") |
||||
|
shift |
||||
|
;; |
||||
|
*) |
||||
|
[ -z "$hostname" ] && hostname="$1" || { |
||||
|
err "Surnumerous positional argument '$1'. Expecting only hostname." |
||||
|
return 1 |
||||
|
} |
||||
|
;; |
||||
|
esac |
||||
|
shift |
||||
|
done |
||||
|
"${ssh_cmd[@]}" -o ControlPath=/tmp/ssh-control-master-${master_pid} \ |
||||
|
-o ControlMaster=auto -o ControlPersist=900 \ |
||||
|
-o ConnectTimeout=5 -o "StrictHostKeyChecking=no" \ |
||||
|
"${ssh_options[@]}" \ |
||||
|
"$hostname" "$@" -- true || { |
||||
|
err Failed: ssh -o ControlPath=/tmp/ssh-control-master-${master_pid} \ |
||||
|
-o ControlMaster=auto -o ControlPersist=900 \ |
||||
|
"$hostname" "$@" -- true |
||||
|
return 1 |
||||
|
} |
||||
|
trap_add EXIT,INT 'ssh:quit "$hostname"' |
||||
|
} |
||||
|
|
||||
|
|
||||
|
ssh:open-try() { |
||||
|
local opts hostnames |
||||
|
opts=() |
||||
|
hostnames=() |
||||
|
while [ "$#" != 0 ]; do |
||||
|
case "$1" in |
||||
|
-o) |
||||
|
opts+=("$1" "$2") |
||||
|
shift |
||||
|
;; |
||||
|
*) |
||||
|
hostnames+=("$1") |
||||
|
;; |
||||
|
esac |
||||
|
shift |
||||
|
done |
||||
|
password='' |
||||
|
for host in "${hostnames[@]}"; do |
||||
|
debug "Trying $host with publickey." |
||||
|
ssh:open -o PreferredAuthentications=publickey \ |
||||
|
"${opts[@]}" \ |
||||
|
"$host" >/dev/null 2>&1 && { |
||||
|
echo "$host"$'\n'"$password"$'\n' |
||||
|
return 0 |
||||
|
} |
||||
|
debug " .. failed connecting to $host with publickey." |
||||
|
done |
||||
|
local times=0 password |
||||
|
while [ "$((++times))" -le 3 ]; do |
||||
|
read -sp "$HOST's password: " password |
||||
|
errlvl="$?" |
||||
|
echo >&2 |
||||
|
if [ "$errlvl" -gt 0 ]; then |
||||
|
exit 1 |
||||
|
fi |
||||
|
for host in "${hostnames[@]}"; do |
||||
|
debug "Trying $host with password ($times/3)" |
||||
|
echo "$password" | ssh:open -o PreferredAuthentications=password \ |
||||
|
--stdin-password \ |
||||
|
"${opts[@]}" \ |
||||
|
"$host" >/dev/null 2>&1 && { |
||||
|
echo "$host"$'\n'"$password"$'\n' |
||||
|
return 0 |
||||
|
} |
||||
|
debug " .. failed connecting to $host with password." |
||||
|
done |
||||
|
err "login failed. Try again... ($((times+1))/3)" |
||||
|
done |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
|
||||
|
ssh:run() { |
||||
|
local hostname="$1" ssh_options cmd |
||||
|
shift |
||||
|
|
||||
|
ssh_options=() |
||||
|
cmd=() |
||||
|
while [ "$#" != 0 ]; do |
||||
|
case "$1" in |
||||
|
"--") |
||||
|
shift |
||||
|
cmd+=("$@") |
||||
|
break |
||||
|
;; |
||||
|
*) |
||||
|
ssh_options+=("$1") |
||||
|
;; |
||||
|
esac |
||||
|
shift |
||||
|
done |
||||
|
#echo "$DARKCYAN$hostname$NORMAL $WHITE\$$NORMAL" "$@" |
||||
|
debug "Running cmd: ${cmd[@]}" |
||||
|
for arg in "${cmd[@]}"; do |
||||
|
debug "$arg" |
||||
|
done |
||||
|
{ |
||||
|
{ |
||||
|
ssh -o ControlPath=/tmp/ssh-control-master-${master_pid} \ |
||||
|
-o ControlMaster=auto -o ControlPersist=900 \ |
||||
|
-o "StrictHostKeyChecking=no" \ |
||||
|
"$hostname" "${ssh_options[@]}" -- "${cmd[@]}" |
||||
|
} 3>&1 1>&2 2>&3 | sed -r "s/^/$DARKCYAN$hostname$NORMAL $DARKRED\!$NORMAL /g" |
||||
|
set_errlvl "${PIPESTATUS[0]}" |
||||
|
} 3>&1 1>&2 2>&3 |
||||
|
} |
||||
|
|
||||
|
ssh:quit() { |
||||
|
local hostname="$1" |
||||
|
shift |
||||
|
ssh -o ControlPath=/tmp/ssh-control-master-${master_pid} \ |
||||
|
-o ControlMaster=auto -o ControlPersist=900 -O exit \ |
||||
|
"$hostname" 2>/dev/null |
||||
|
} |
||||
|
|
||||
|
|
||||
|
is_ovh_domain_name() { |
||||
|
local domain="$1" |
||||
|
|
||||
|
[[ "$domain" == *.ovh.net ]] && return 0 |
||||
|
[[ "$domain" == "ns"*".ip-"*".eu" ]] && return 0 |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
is_ovh_hostname() { |
||||
|
local domain="$1" |
||||
|
|
||||
|
[[ "$domain" =~ ^vps-[0-9a-f]*$ ]] && return 0 |
||||
|
[[ "$domain" =~ ^vps[0-9]*$ ]] && return 0 |
||||
|
return 1 |
||||
|
} |
||||
|
|
||||
|
|
||||
|
[ "$SOURCED" ] && return 0 |
||||
|
|
||||
|
## |
||||
|
## Command line processing |
||||
|
## |
||||
|
|
||||
|
|
||||
|
cmdline.spec.gnu |
||||
|
cmdline.spec.reporting |
||||
|
|
||||
|
cmdline.spec.gnu vps-setup |
||||
|
|
||||
|
cmdline.spec::cmd:vps-setup:run() { |
||||
|
|
||||
|
: :posarg: HOST 'Target host to check/fix ssh-access' |
||||
|
|
||||
|
depends sshpass shyaml |
||||
|
|
||||
|
KEY_PATH="ssh-access.public-keys" |
||||
|
local keys=$(config get-value -y "ssh-access.public-keys") || true |
||||
|
if [ -z "$keys" ]; then |
||||
|
err "No ssh publickeys configured in config file." |
||||
|
echo " Looking for keys in ${WHITE}${KEY_PATH}${NORMAL}" \ |
||||
|
"in config file." >&2 |
||||
|
config:exists --message 2>&1 | prefix " " |
||||
|
if [ "${PIPESTATUS[0]}" == "0" ]; then |
||||
|
echo " Config file found in $(config:filename)" |
||||
|
fi |
||||
|
return 1 |
||||
|
fi |
||||
|
local tkey=$(e "$keys" | shyaml get-type) |
||||
|
if [ "$tkey" != "sequence" ]; then |
||||
|
err "Value type of ${WHITE}${KEY_PATH}${NORMAL} unexpected (is $tkey, expecting sequence)." |
||||
|
echo " Check content of $(config:filename), and make sure to use a sequence." >&2 |
||||
|
return 1 |
||||
|
fi |
||||
|
|
||||
|
local IP NAME keys host_pass_connected |
||||
|
if ! IP=$(resolve "$HOST"); then |
||||
|
err "'$HOST' name unresolvable." |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
NAME="$HOST" |
||||
|
if [ "$IP" != "$HOST" ]; then |
||||
|
NAME="$HOST ($IP)" |
||||
|
fi |
||||
|
|
||||
|
if ! is-port-open "$IP" "22"; then |
||||
|
err "$NAME unreachable or port 22 closed." |
||||
|
exit 1 |
||||
|
fi |
||||
|
debug "Host $IP's port 22 is open." |
||||
|
if ! host_pass_connected=$(ssh:open-try \ |
||||
|
{root,debian}@"$HOST"); then |
||||
|
err "Could not connect to {root,debian}@$HOST with publickey nor password." |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
read-0a host password <<<"$host_pass_connected" |
||||
|
|
||||
|
sudo_if_necessary= |
||||
|
if [ "$password" -o "${host%%@*}" != "root" ]; then |
||||
|
if ! ssh:run "$host" -- sudo -n true >/dev/null 2>&1; then |
||||
|
err "Couldn't do a password-less sudo from $host." |
||||
|
echo " This is not yet supported." |
||||
|
exit 1 |
||||
|
else |
||||
|
sudo_if_necessary=sudo |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
Section Checking access |
||||
|
while read-0 key; do |
||||
|
prefix="${key%% *}" |
||||
|
if [ "$prefix" != "ssh-rsa" ]; then |
||||
|
err "Unsupported key:"$'\n'"$key" |
||||
|
return 1 |
||||
|
fi |
||||
|
label="${key##* }" |
||||
|
Elt "considering adding key ${DARKYELLOW}$label${NORMAL}" |
||||
|
dest="/root/.ssh/authorized_keys" |
||||
|
if ssh:run "$host" -- $sudo_if_necessary grep "\"$key\"" "$dest" >/dev/null 2>&1; then |
||||
|
print_info "already present" |
||||
|
print_status noop |
||||
|
Feed |
||||
|
else |
||||
|
if echo "$key" | ssh:run "$host" -- $sudo_if_necessary tee -a "$dest" >/dev/null; then |
||||
|
print_info added |
||||
|
else |
||||
|
echo |
||||
|
Feedback failure |
||||
|
return 1 |
||||
|
fi |
||||
|
Feedback success |
||||
|
fi |
||||
|
done < <(e "$keys" | shyaml get-values-0) |
||||
|
|
||||
|
Section Checking ovh hostname file |
||||
|
Elt "Checking /etc/ovh-hostname" |
||||
|
if ! ssh:run "$host" -- $sudo_if_necessary [ -e "/etc/ovh-hostname" ]; then |
||||
|
print_info "creating" |
||||
|
ssh:run "$host" -- $sudo_if_necessary cp /etc/hostname /etc/ovh-hostname |
||||
|
ovhname=$(ssh:run "$host" -- $sudo_if_necessary cat /etc/ovh-hostname) |
||||
|
Elt "Checking /etc/ovh-hostname: $ovhname" |
||||
|
Feedback || return 1 |
||||
|
else |
||||
|
ovhname=$(ssh:run "$host" -- $sudo_if_necessary cat /etc/ovh-hostname) |
||||
|
Elt "Checking /etc/ovh-hostname: $ovhname" |
||||
|
print_info "already present" |
||||
|
print_status noop |
||||
|
Feed |
||||
|
fi |
||||
|
|
||||
|
if ! is_ovh_domain_name "$HOST" && ! str_is_ipv4 "$HOST"; then |
||||
|
Section Checking hostname |
||||
|
Elt "Checking /etc/hostname..." |
||||
|
if [ "$old" != "$HOST" ]; then |
||||
|
old="$(ssh:run "$host" -- $sudo_if_necessary cat /etc/hostname)" |
||||
|
Elt "Hostname is '$old'" |
||||
|
if is_ovh_hostname "$old"; then |
||||
|
Elt "Hostname '$old' --> '$HOST'" |
||||
|
print_info "creating" |
||||
|
echo "$HOST" | ssh:run "$host" -- $sudo_if_necessary tee /etc/hostname >/dev/null && |
||||
|
ssh:run "$host" -- $sudo_if_necessary hostname "$HOST" |
||||
|
Feedback || return 1 |
||||
|
else |
||||
|
print_info "not changing" |
||||
|
print_status noop |
||||
|
Feed |
||||
|
fi |
||||
|
|
||||
|
else |
||||
|
print_info "already set" |
||||
|
print_status noop |
||||
|
Feed |
||||
|
fi |
||||
|
else |
||||
|
info "Not changing domain as '$HOST' doesn't seem to be final domain." |
||||
|
fi |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
cmdline::parse "$@" |
Write
Preview
Loading…
Cancel
Save
Reference in new issue