Перейти к основному содержимому

Аутентификация API и TLS

Узел защищает свои владельческие поверхности с помощью HTTP Basic по секретному файлу на диске. Кошелёк защищает свой Owner API аналогично, по собственному секретному файлу, а Owner API v3 дополнительно требует токен, полученный с паролем кошелька. Внешние поверхности не требуют учётных данных, поскольку интерактивный перевод требует, чтобы принимающая сторона была доступна для стороннего участника. Терминация TLS остаётся на усмотрение оператора.

Поверхности​

ПоверхностьПорт по умолчаниюУчётные данныеКто должен вызывать
Узел /v1/*3413Basic, api_secret_path, на каждом маршруте кроме GET /v1/versionВы, с той же машины
Узел /v2/owner3413Basic, api_secret_path. Один простой вызов get_status принимается без негоВы, с той же машины
Узел /v2/foreign3413Опциональный Basic, foreign_api_secret_pathВсё, что читает цепочку
Owner /v3/owner кошелька3420Basic, api_secret_path кошелька, и токен из open_wallet внутри зашифрованного конвертаВы, с той же машины
Owner /v2/owner кошелька3420Basic, api_secret_path кошелька. Заменён на /v3/ownerНичего. Привяжите слушатель к loopback
Foreign /v2/foreign кошелька3415НетКонтрагент, оплачивающий вам

Foreign API кошелька принимает слейт (Slate), возвращает частично подписанный слейт, формирует coinbase для майнера, настроенного на выплату в этот кошелёк, и сообщает свою версию. Ограничьте доступ к нему на уровне прокси.

Учётные данные /v1 узла распространяются на весь префикс, включая операции чтения цепочки, открытые на /v2/foreign. Единственное исключение — точный запрос GET /v1/version, который отвечает всем; POST к тому же пути по-прежнему требует учётных данных. См. поверхность REST v1.

api_secret_path указывает на разные файлы в конфигурации каждого бинарника. Обе поверхности узла читают файл узла — .api_secret. Файл кошелька называется .owner_api_secret и защищает собственный слушатель Owner кошелька.

Доступ владельца к кошельку​

/v3/owner имеет два уровня защиты, и вызов должен пройти оба.

Первый — HTTP Basic на слушателе. Имя пользователя epic, пароль — содержимое .owner_api_secret кошелька, realm EpicOwnerAPI. Он охватывает всё на слушателе Owner, кроме /v2/foreign, то есть /v3/owner и /v2/owner одинаково, и проверяется до выполнения любого метода (controller/src/controller.rs:143). Закомментируйте api_secret_path кошелька — и этот уровень не устанавливается.

Второй — токен. Каждый метод в трейте v3 принимает его (api/src/owner_rpc_s.rs:78), а получение токена означает завершение ECDH-рукопожатия и последующий вызов open_wallet с паролем кошелька. См. зашифрованное рукопожатие.

Первый вызов устанавливает общий ключ. Он несёт только учётные данные Basic и ничего более:

curl -s -u epic:$(cat ~/.epic/main/.owner_api_secret) \
-d '{"jsonrpc":"2.0","method":"init_secure_api","params":{"ecdh_pubkey":"<your compressed public key, hex>"},"id":1}' \
http://127.0.0.1:3420/v3/owner

Без -u слушатель отвечает 401 до начала рукопожатия. Замените ~/.epic/main на каталог кошелька, если кошелёк находится в другом месте.

Помимо этих учётных данных, каждый метод на /v3/owner принимает token, а токен получают через open_wallet, которому требуется пароль кошелька. owner_api_interface по умолчанию равен 127.0.0.1; для удалённого доступа используйте туннель до loopback. Слушатель — controller/src/controller.rs:123.

Полный список методов, зашифрованный конверт и информация о том, какие вызовы перемещают средства, находятся на странице wallet Owner API.

Учётные данные узла​

Префикс /v1 узла и /v2/owner используют HTTP Basic с именем пользователя буквально epic и содержимым секретного файла в качестве пароля:

Authorization: Basic base64("epic:" + <secret file contents>)

Узел создаёт ~/.epic/<network>/.api_secret при первом запуске и указывает api_secret_path на него, поэтому это работает без настройки:

curl -u epic:$(cat ~/.epic/main/.api_secret) \
-d '{"jsonrpc":"2.0","method":"get_status","params":[],"id":1}' \
http://127.0.0.1:3413/v2/owner

Секретные файлы содержат одну 20-символьную случайную буквенно-цифровую строку без завершающего символа новой строки (config/src/config.rs:84), а сравнение выполняется за постоянное время (basic_auth_matches()). Realm — EpicAPI на поверхности owner и EpicForeignAPI на foreign. Отклонённый вызов, на любом бинарнике, возвращает 401 с заголовком WWW-Authenticate и телом ошибки JSON-RPC:

{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Unauthorized"}, "id": null}

Два вызова обрабатываются без учётных данных:

  • GET /v1/version — именно этот метод и путь (api/src/handlers.rs:118).
  • Один get_status на /v2/owner, отправленный отдельно, а не в пакете, с params равным [] или null и телом не более 4 КиБ. Возвращает тот же статус, что и аутентифицированный вызов. Любой другой метод, пакет или тело, которое не парсится, получает 401 (is_legacy_status_request()).

Остальное поведение следует из принципа отказа узла в закрытом состоянии:

  • Если api_secret_path закомментирован или указывает на несуществующий файл, секрета для сравнения нет, поэтому каждый защищённый вызов возвращает 401 всем, с -u или без. Отвечают только два вызова, описанных выше. Комментирование ключа блокирует /v1 и /v2/owner; оно не открывает их.
  • Пустой .api_secret перегенерируется с новым значением, а не читается как «нет секрета», поэтому очистка файла не отключает аутентификацию (config/src/config.rs:96).
  • OPTIONS пропускается до проверки учётных данных, что обеспечивает работу CORS preflight.

Опциональные учётные данные на внешней поверхности узла​

foreign_api_secret_path заставляет /v2/foreign узла также требовать учётные данные. Создайте секрет самостоятельно и укажите абсолютный путь:

head -c 40 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c 20 > ~/.epic/main/.foreign_api_secret
[server]
foreign_api_secret_path = "/home/you/.epic/main/.foreign_api_secret"

Realm — EpicForeignAPI, имя пользователя по-прежнему epic. Проверьте с помощью:

curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:3413/v2/foreign
# 401 после установки секрета
epic-wallet 4.1.0 cannot use a node with a foreign secret

Кошелёк никогда не отправляет учётные данные на /v2/foreign узла, что бы ни было в его собственном .api_secret, поэтому каждое выполняемое им чтение цепочки получает 401 и останавливается с Unauthorized (HTTP 401). Ошибка предлагает проверить node_api_secret_path, что не поможет. На любом узле, с которым взаимодействует кошелёк, не создавайте файл, на который указывает foreign_api_secret_path (ключ установлен по умолчанию и безвреден при отсутствии файла), или закомментируйте ключ, а ограничение поверхности foreign реализуйте на уровне прокси.

Учётные данные, которые кошелёк передаёт узлу​

node_api_secret_path указывает в другую сторону: это секрет узла, который хранит кошелёк, и по умолчанию он равен <wallet dir>/.api_secret. Кошелёк отправляет его только при вызовах /v2/owner, кроме get_status, и никогда — на /v2/foreign или /v2/tor (impls/src/node_clients/http.rs:115).

На практике это один вызов, get_onion_addresses, который кошелёк выполняет при финализации перевода через epicbox, чтобы опубликовать транзакцию через Tor. Отсутствующий или несовпадающий секрет приводит к тому, что этот запрос возвращает пустой результат, и кошелёк публикует транзакцию обычным способом. Всё остальное, что кошелёк запрашивает у узла — балансы, выходы (outputs), ядра (kernels), публикацию транзакции и проверку синхронизации — направляется на эндпоинты, не требующие учётных данных. Проверка синхронизации использует Foreign API ready_for_txs, и только узел, у которого этот метод отсутствует, возвращает вместо него неаутентифицированный get_status.

Поэтому кошелёк, у которого .api_secret отличается от секрета узла, всё равно работает. Ответ 401 от узла указывает на другое: на foreign_api_secret_path на узле, описанный выше, или на узел старше 4.1.0. Второй случай прочитан из кода, а не проверен на практике: узел 4.0.x с секретом помещает весь /v2/owner за Basic auth и поэтому отклоняет неаутентифицированный get_status от кошелька.

TLS​

Оба бинарника принимают одну и ту же пару, по умолчанию закомментированную:

tls_certificate_file = "/path/to/fullchain.pem"
tls_certificate_key = "/path/to/privkey.pem"

Указание сертификата переключает слушатель с HTTP на HTTPS. Указание сертификата без ключа является ошибкой при запуске. Сертификаты читаются как PEM-цепочка; ключи могут быть в формате PKCS#1, PKCS#8 или SEC1 PEM. Файл, который не удаётся открыть, разобрать или который не соответствует своему сертификату, останавливает процесс вместо отката к HTTP, поэтому неверная пара явно завершается ошибкой при запуске.

Встроенные подкоманды epic client обращаются к узлу через http:// (src/bin/cmd/client.rs:160). Используйте curl для работы с TLS-слушателем.

Далее​