Сценарий для тех, кто сдаёт серверы своим клиентам. Скрипт добавляет публичный ключ клиента в аккаунт (если его там ещё нет), заказывает сервер с этим ключом, ждёт, пока на него можно войти по SSH, а когда клиент сменился - переустанавливает ОС с ключом нового клиента. Пароль сервера при этом тоже работает, вход - пользователем сервера с правами sudo.

Нужно: ключ API с правом write и «Может тратить деньги» (сервер платный), curl, jq, ssh. Публичные ключи - файлы .pub, закрытые лежат рядом без .pub и никуда не передаются. Подробнее - «Серверы», разделы «SSH-ключи» и «Переустановка ОС».

#!/usr/bin/env bash
set -euo pipefail
API=https://api.fatmetal.ru/v1/public
H="X-Api-Key: $FATMETAL_KEY"
J='Content-Type: application/json'
ZONE=center-2                         # зона сервера (GET /zones)
NAME=client-42                        # имя сервера
PUBKEY=${PUBKEY:-$HOME/.ssh/id_ed25519.pub}   # публичный ключ; закрытый - рядом, без .pub
SSH_KEY=${PUBKEY%.pub}

# 1. Ключ в аккаунте: ищем по содержимому, нет - добавляем
KEY_BODY=$(cut -d' ' -f1,2 "$PUBKEY")
KEY_ID=$(curl -sf "$API/user-ssh-keys" -H "$H" \
  | jq -r --arg k "$KEY_BODY" '.data[] | select(.public_key==$k) | .id')
if [ -z "$KEY_ID" ]; then
  KEY_ID=$(jq -n --arg n "$NAME" --arg k "$(cat "$PUBKEY")" '{name:$n, public_key:$k}' \
    | curl -sf -X POST "$API/user-ssh-keys" -H "$H" -H "$J" -d @- | jq -r '.data.id')
fi

# 2. Тариф и образ ОС для зоны: самый дешёвый тариф первого класса процессора
#    и Debian 13 (подробнее - «Серверы»)
CLASS=$(curl -sf "$API/zones" -H "$H" | jq -r ".data.zones[] | select(.id==\"$ZONE\") | .cpu_classes[0].id")
SERVICE=$(curl -sf "$API/servicesv2?type=vm" -H "$H" \
  | jq -r "[.data[] | select(.category==\"$CLASS\" and (.custom|not))] | sort_by(.price.month) | .[0].service_id")
SYSTEM=$(curl -sf "$API/systemsv2?type=vm" -H "$H" \
  | jq -r ".data[] | select(.name==\"Debian\" and (.version|startswith(\"13\"))) | .os_ids[\"$ZONE\"] // empty")
[ -n "$SYSTEM" ] || { echo "Debian 13 в зоне $ZONE недоступен"; exit 1; }

# 3. Сервер с ключом
ID=$(curl -sf -X POST "$API/user-service" -H "$H" -H "$J" \
  -d "{\"service\":$SERVICE, \"type\":\"vm\", \"system\":\"$SYSTEM\", \"zone\":\"$ZONE\",
       \"class\":\"$CLASS\", \"name\":\"$NAME\", \"ssh_key_ids\":[\"$KEY_ID\"]}" \
  | jq -r '.data.user_service_id')

# Ждём, пока сервер заработает, и потом ещё, пока поднимется SSH
wait_ssh() {
  for i in $(seq 1 30); do
    S=$(curl -sf "$API/user-services?id=$ID" -H "$H" | jq ".data[\"$ID\"].settings")
    [ "$(echo "$S" | jq -r .status)" = Running ] && break
    sleep 10
  done
  IP=$(echo "$S" | jq -r .ip); USER=$(echo "$S" | jq -r .system.user)
  for i in $(seq 1 30); do
    ssh -i "$SSH_KEY" -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 \
      "$USER@$IP" true 2>/dev/null && return 0
    sleep 10
  done
  echo "SSH на $IP не ответил"; return 1
}
wait_ssh
echo "сервер готов: ssh -i $SSH_KEY $USER@$IP"
ssh -i "$SSH_KEY" "$USER@$IP" 'uname -n'

# 4. Клиент сменился: переустановить ОС с другим ключом. Диск стирается.
#    Без ssh_key_ids на сервере остались бы прежние ключи.
NEW_PUBKEY=${NEW_PUBKEY:-}
if [ -n "$NEW_PUBKEY" ]; then
  NEW_ID=$(jq -n --arg n "$NAME-next" --arg k "$(cat "$NEW_PUBKEY")" '{name:$n, public_key:$k}' \
    | curl -sf -X POST "$API/user-ssh-keys" -H "$H" -H "$J" -d @- | jq -r '.data.id')
  curl -sf -X PUT "$API/user-service-system" -H "$H" -H "$J" \
    -d "{\"id\":$ID, \"system\":\"$SYSTEM\", \"ssh_key_ids\":[\"$NEW_ID\"]}" > /dev/null
  ssh-keygen -R "$IP" > /dev/null 2>&1 || true    # у переустановленного сервера новый ключ хоста
  sleep 60                                         # сервер выключается и ставится заново
  SSH_KEY=${NEW_PUBKEY%.pub}
  wait_ssh
  echo "переустановлен, вход по новому ключу: ssh -i $SSH_KEY $USER@$IP"
fi

Запуск: PUBKEY=~/.ssh/client.pub ./recipe.sh, для смены клиента добавьте NEW_PUBKEY=~/.ssh/next-client.pub.

Что важно:

  • Ключ ищется в аккаунте по содержимому, поэтому повторный запуск не добавит его второй раз.
  • Тариф - самый дешёвый готовый: у произвольной конфигурации (custom) в каталоге нулевая цена, её скрипт пропускает.
  • Переустановка стирает диск. Без ssh_key_ids на сервере остались бы ключи прежнего клиента - передавайте новый ключ всегда.
  • У переустановленного сервера новый ключ хоста, поэтому скрипт убирает старую запись из known_hosts, иначе ssh откажется подключаться.
  • Новый пароль сервера после переустановки - в settings.system.password (GET /user-services?id=).