Храните торговый ключ и секреты приложения вне диска сервера

erpc worker хранит торговый ключ и секреты приложения в 1Password, в вашей связке ключей или в зашифрованном keystore и передаёт их приложению в памяти, но никогда — на диск сервера.
Эта страница описывает ERPC CLI v0.1.0-rc.4 — пререлиз.

Как торговый ключ попадает в приложение

Приложение с кошельком получает свой ключ через канал при запуске. На сервере нет ни wallet.json, ни .env, ни копии ключа. erpc не записывает ключ ни на какой диск в незашифрованном виде: единственный файл, в который он его помещает, — файл keystore на вашей машине, зашифрованный вашей парольной фразой.
  • Где он хранится: erpc worker key new создаёт ключ в 1Password, если erpc login хранит сессию там, иначе — в связке ключей, иначе — в keystore. erpc.toml записывает ссылку на ключ и его открытый ключ.
  • На сервере: агент erpc worker держит ключ только в памяти. Он запрещает снимать с себя дамп, блокирует свою память и передаёт ключ приложению через файловый дескриптор; приложение стирает ключ после чтения.
  • После перезагрузки воркер возвращается заблокированным: приложение не работает, пока erpc worker start снова не отправит ключ. Агента, который упал или был убит, systemd запускает снова, и он так же возвращается заблокированным.
  • Обновление пакетов не перезапускает воркер. deploy указывает needrestart не трогать юниты воркера: в автоматическом режиме он откладывает их перезапуск, а при вопросе в терминале его ответ по умолчанию для них — «нет», поэтому обновление безопасности без участия человека не перезапускает приложение. Если это правило не удалось записать, развёртывание сообщает об этом и продолжается, и тогда воркер работает без этой защиты.
Торговый ключ указывается в [wallet] и больше нигде, и erpc читает его только для человека за терминалом: не для ИИ-инструмента и не через сервисный аккаунт 1Password. Строка [secrets], которая указывает ссылку [wallet] или аккаунт keychain: или keystore:, оканчивающийся на .wallet, отклоняется. В keystore запись, сохранённая как ключ, не открывается как секрет. В 1Password, а также под аккаунтом связки ключей с другим именем ключ ничем не помечен: если erpc.toml без [wallet] указывает такой элемент в [secrets], он читается как секрет.

Выбор места хранения значений

1Password не обязателен. erpc worker key new и erpc worker secret put сохраняют переданное им в первом подходящем месте из списка, если --store не указывает другое (--store 1password --vault <id>, --store keychain, --store keystore):
  1. 1Password, если erpc login хранит сессию там или указан --vault.
  2. Связка ключей ОС, где значение сохраняется и после перезагрузки: macOS, Windows и Linux с keyring рабочего стола в сессии с выполненным входом.
  3. Keystore erpc — на машине Linux без такого keyring: сервер без графического интерфейса, WSL, вход по SSH. --store keychain на такой машине отклоняется.
В связке ключей или в keystore ключ существует только на одной этой машине, без копий и без синхронизации, и другая машина не может прочитать ссылку keychain: или keystore:.
Потеря или стирание машины означает, что кошелёк утрачен: держите на нём немного средств или используйте 1Password, который может прочитать любая машина с выполненным входом в сейф. Войдите в 1Password с помощью erpc login --store 1password-desktop или через op signin там, где нет десктопного приложения.
Сервисный аккаунт 1Password может хранить секреты приложения, но erpc не читает через него торговый ключ. С 1Password каждое чтение может вызывать в приложении запрос на одобрение, и пока erpc ждёт, он показывает индикатор ожидания.

Сохранение секрета

Секреты, например API-токены и RPC-ключи, хранятся так же, но попадают в приложение как переменные окружения, а не через канал. erpc worker secret put <NAME> сохраняет значение, а [secrets] в erpc.toml содержит только ссылку на него.
bash
erpc worker secret put API_TOKEN --generate          # the bot's own API token, made for you and shown to nobody
erpc worker secret put ERPC_API_KEY --from-account   # the account's RPC key, read from your account (or SOLANA_RPC_ENDPOINT in [vars])
  • --generate создаёт значение: 32 случайных байта в виде 64 шестнадцатеричных символов.
  • --from-account сохраняет первый из RPC-ключей вашего аккаунта, который принимает общий RPC. Ключи перебираются по порядку, начиная с основного, — по одному getHealth на каждый, с 8 секундами на ответ. Ключ, на который пришёл ответ 401 или 403, отклоняется, и пробуется следующий; сохраняется только ключ, который RPC принял. Команда сообщает, основной ли это ключ аккаунта или другой (account_key с --json).
  • Ни одно из значений не выводится, в том числе с --json. Первое использование keystore или заблокированный keystore требуют человека за терминалом.
  • Ключи проверяются с этой машины, поэтому ключ, ограниченный адресом сервера, здесь отклоняется, хотя там он работает. Если отклонены все ключи, --from-account ничего не сохраняет, и вы сохраняете нужный ключ вручную — вводя его без отображения на экране или передавая через конвейер. Если к RPC не удалось обратиться, команда ничего не сохраняет и erpc.toml остаётся прежним; выполните ту же команду ещё раз.
Чтобы сохранить ключ вручную:
bash
erpc worker secret put ERPC_API_KEY        # asks for the value, unseen
  • --replace сохраняет новое значение для имени, которое уже задано. erpc worker secret list показывает секретные настройки по имени и место хранения каждой.
  • Настройка, которую предоставляет линия данных (GRPC_ENDPOINT, SHREDS_ENDPOINT, SOLANA_SEND_RPC_ENDPOINT), никогда не бывает секретом: её задают --grpc, --shreds и --unlimited.
Ссылка в [secrets] или [wallet] — одна из следующих:
СсылкаГде находится значение
op://<vault-id>/<item-id>/<field>1Password, по ID сейфа и элемента
keychain:<service>/<account>Связка ключей ОС на машине, где значение было сохранено
keystore:<service>/<account>Keystore erpc на машине, где значение было сохранено

Keystore

Keystore — это каталог ~/.erpc/keystore (keystore внутри ERPC_HOME, если он задан). В Linux и macOS его каталоги имеют права 0700, а файлы — 0600; в Windows erpc не задаёт собственных прав.
  • master.json хранит случайный 32-байтовый мастер-ключ, запечатанный ключом, который PBKDF2-HMAC-SHA256 (600 000 итераций) выводит из вашей парольной фразы. Каждая запись хранит одно значение, запечатанное ChaCha20-Poly1305 под собственным ключом.
  • Парольная фраза: первая команда key new или secret put, которая использует keystore, просит вас её выбрать — ввести дважды в терминале, не короче 12 символов. erpc принимает её только из этого скрытого запроса, никогда — из аргумента, переменной окружения или канала.
  • Разблокировка в Linux: после того как парольная фраза открыла keystore, разблокировка хранится в постоянном keyring ядра, в памяти, пока машина не перезапустится, пока keyring не простоит без использования весь свой срок бездействия (три дня, если система не задаёт иное) или пока не будет выполнен erpc worker secret lock. erpc worker secret unlock заранее запрашивает парольную фразу и сообщает, до какого времени действует разблокировка. В оболочке, запущенной без собственного входа (su без -, sudo -E), разблокировка не сохраняется, и каждая команда спрашивает парольную фразу.
  • Когда за терминалом никого нет, команда, которой нужен keystore, использует разблокировку, если она есть, а иначе завершается с ошибкой, называя erpc worker secret unlock. Разблокированный keystore выдаёт секреты, но не торговый ключ.
  • В macOS и Windows (--store keystore) разблокировка не сохраняется: каждая команда, которой нужен keystore, один раз запрашивает парольную фразу, и поэтому ей нужен терминал.

Что keystore защищает, а что нет

  • Сделайте резервную копию каталога keystore и запомните парольную фразу. erpc не может восстановить ни то, ни другое, и если нет хотя бы одного из них, содержимое keystore утрачено.
  • Тот, у кого есть файлы keystore и парольная фраза, имеет и ключ. PBKDF2 делает каждую попытку подбора медленной, но не делает безопасной парольную фразу, которую можно угадать.
  • Пока keystore разблокирован, любая программа, запущенная от вашего имени, может прочитать мастер-ключ из keyring ядра (программа, сессия которой не содержит ваш keyring, может подключить его к себе) и открыть любую запись, в том числе запись ключа. Собственные правила erpc (ключ, указанный в [wallet], читается только для человека за терминалом и никогда — для ИИ-инструмента; ключ, сохранённый в keystore, никогда не открывается как секрет) — это то, что делает erpc; на другие программы, запущенные от вашего имени, они не распространяются. Когда закончите, выполните erpc worker secret lock.
  • Имена записей не скрыты, а размер записи показывает длину значения.
  • Изменённый файл не открывается, а более старая копия той же записи, возвращённая на её место, открывается.
  • erpc worker secret rm удаляет запись секрета в keystore вместе с его строкой в erpc.toml (если ту же запись не указывает другая настройка приложения) и оставляет значение в 1Password или в связке ключей там, где оно есть. Команда не затирает диск, и копии в ваших резервных копиях остаются.
  • erpc никогда не удаляет и не перезаписывает запись торгового ключа. Чтобы заменить ключ, переведите средства, затем сами удалите его файл и [wallet].
  • Настройку нельзя назвать WALLET ни в каком регистре — ни в keystore, ни в связке ключей; --store 1password принимает это имя.

Работа с торговым ключом

КомандаЧто делает
erpc worker key newСоздаёт торговый ключ и выводит открытый ключ, который нужно пополнить
erpc worker key import <FILE>Перемещает файл ключа в 1Password, связку ключей или keystore, затем предлагает стереть файл
erpc worker key showПоказывает открытый ключ и место хранения ключа
erpc worker key balanceПоказывает баланс ключа через собственную настройку RPC приложения
erpc worker key send --to <ADDRESS> --sol <N>Подписывает один перевод в памяти после вашего «да» в терминале. --all отправляет всё, кроме комиссии
Если erpc завершил работу, не зная, прошёл ли перевод, проверьте его по подписи, прежде чем отправлять снова: повторная отправка — это второй перевод.