Оба клиента находятся на одном ноутбуке, проходят через один и тот же туннель, и подключается только один из них. Это сравнение кажется доказательством избирательности VPN, хотя куда более частое объяснение состоит в том, что два клиента обращаются к разным адресам. GUI хранит собственные хост, порт, роль и настройки TLS и тихо строит SSH-перенаправление, когда оно нужно, а psql собирает соединение из значений libpq по умолчанию, которые ваша оболочка, возможно, уже задала. Воспроизвести работающее соединение — значит прочитать настройки GUI, а не переустанавливать туннель.

Миф: один работающий клиент доказывает путь

Если GUI подключается, туннель передаёт трафик, и это единственное, что доказывает GUI. Он ничего не говорит о том, какой адрес разрешает psql, под какой ролью проходит аутентификация и покидает ли машину открытый им сокет. Путь общий; параметры — нет.

Локальное перенаправление увеличивает разрыв. GUI может подключаться к 127.0.0.1 на порту, который он перенаправил через SSH, и этот слушатель существует только пока длится сеанс GUI. psql, направленный на localhost без перенаправления, получает отказ в соединении, не имеющий отношения к VPN.

Прочитайте конечную точку, которую GUI действительно использует

Откройте свойства соединения и скопируйте четыре значения: хост, порт, базу данных и роль. Затем поищите вкладку туннеля, потому что SSH-перенаправление превращает частный адрес в локальный порт и меняет то, что нужно сообщить psql.

SettingWhat the GUI holdsWhat psql uses when the value is absent
DestinationA saved hostname, sometimes behind a forwardA local Unix socket, not the remote endpoint
PortThe direct port or a forwarded local port5432, or the port from a service file
Role and databaseStored per connection profileYour operating-system user name
TLS identityIts own CA bundle and verification modeWhatever PGSSLMODE or the service file says
Startup filesNot read at allRead unless -X is passed

Каждая строка терпит неудачу на своём уровне, и полученное сообщение называет этот уровень. Вставка дружественного имени сохранённого профиля в psql не передаёт ни одного из перечисленных значений, а имя службы разрешается через файл, который терминал, возможно, никогда не прочитает.

Что libpq наследует от вашей оболочки

psql — тонкий клиент поверх libpq, и libpq читает окружение раньше всего остального. Переменные вроде PGHOST, PGHOSTADDR, PGPORT, PGDATABASE, PGUSER, PGSERVICE и PGSSLMODE могут каждая перенаправить часть соединения. Они наследуются, а не вводятся, поэтому ничто в выполненной команде их не показывает. Экспортированная PGSSLMODE тоже может изменить то, что принимает клиент, без единого слова в командной строке.

Две из этих переменных заслуживают подозрения в первую очередь. PGHOSTADDR задаёт числовой адрес, который обходит разрешение имён, пока имя хоста по-прежнему определяет идентичность сертификата, а PGSERVICE вытаскивает хост, базу данных и выбор TLS из файла службы. Выгружать всё окружение для отладки — дурная привычка, так как в нём могут быть пароли и посторонние токены; лучше выберите нужные переменные.

Пропуск и host, и hostaddr выбирает сокет домена Unix, а не сетевой адрес. Поэтому вызов psql без -h может локально завершиться успешно или с ошибкой сокета, так и не коснувшись маршрута, который использует GUI.

Передайте те же параметры вручную

Имея значения GUI, передайте их явно и пропустите файл запуска, который мог бы добавить переопределения. Строка подключения ниже приведена для примера: замените конечную точку, порт, базу данных и роль значениями, утверждёнными вашим администратором, и сохраните конфигурацию доверия, которая уже работает.

psql -X "host=db.internal.example port=6432 dbname=orders user=report_ro sslmode=verify-full connect_timeout=5"

Три детали в этой команде намеренны. -X подавляет файл запуска psql, хотя не подавляет окружение libpq или определение службы. connect_timeout=5 не даёт маршруту в никуда подвесить терминал на минуты. sslmode=verify-full оставляет включёнными проверки сертификата и имени хоста, а это именно та часть, которую люди отключают при появлении ошибки TLS.

Если утверждённая конечная точка стоит за пулером, порт 6432 и порт 5432 могут достигать одних и тех же данных через разные идентичности сервера, и проверка может не пройти из-за имени пулера, а не базы данных. Исправьте имя в строке подключения вместо ослабления режима.

Пусть сообщение об ошибке выбирает следующий шаг

Текст сбоя говорит, как далеко продвинулась попытка, и это лучше любого предположения о туннеле. Имя, которое не разрешается, терпит неудачу ещё до появления сокета, тайм-аут означает, что маршрут или брандмауэр отбросил пакеты, отказ означает, что что-то ответило и отклонило, а ошибка аутентификации означает, что сеть уже работала.

psql -X -w "host=db.internal.example port=6432 dbname=orders user=report_ro sslmode=verify-full connect_timeout=5" -c '\conninfo'
env | grep -E '^PG'
psql --version

Сравните вывод \conninfo с живым сеансом GUI: тот же хост, тот же порт, та же роль, то же состояние TLS. Когда эти четыре совпадут, VPN больше нечего объяснять. Пока они не совпали, любое дополнительное изменение туннеля — догадка о соединении, которое терминал никогда не устанавливал.

Похожие статьи