Linux и Ubuntu, настройка прокси для curl, apt, Docker и systemd
03.10.26
Куда прописать адрес, чтобы через прокси пошла именно нужная программа?

Одного переключателя на всю систему в Linux нет, и настройка прокси в Linux раскидана по нескольким местам. Общей галочки на всю систему здесь нет: терминал берёт адрес из переменных окружения, apt читает свой конфиг, рабочий стол GNOME хранит собственную базу настроек, службы systemd и Docker живут отдельно от всех. Отсюда вечная жалоба: curl ходит через прокси, пакеты качаются напрямую. Ниже шесть мест по порядку. У каждого своя команда проверки.
Мой случай. Парсер на сервере запускался из консоли и честно ходил через пул, я перенёс его в службу systemd, и через 20 минут целевой сайт выдал бан на адрес самого сервера, потому что служба не видела ни одной переменной из /etc/environment и всё это время шла напрямую. С тех пор проверяю каждый слой отдельно.
Сначала решите, кому нужен прокси
Прописывать один адрес во все шесть мест незачем. Нужен только скрипт? Хватит переменных в его окружении. Сервер стоит за шлюзом компании и без прокси не обновляется? Тогда apt. Сбор данных крутится в фоне? Значит, systemd. Шпаргалка ниже показывает, какая программа откуда берёт адрес.
Строка подключения в Linux почти везде одна: схема, логин, пароль, адрес и порт. Если провайдер выдал данные в формате IP:PORT:LOGIN:PASS, переставьте поля:
http://LOGIN:PASS@IP:PORT
socks5h://LOGIN:PASS@IP:PORTБуква h в socks5h значит, что имена сайтов разрешает сам прокси. Без неё запросы DNS уходят с вашей ма шины через ваш собственный резолвер.
Если в пароле есть символы @, двоеточие или слеш, их кодируют: @ превращается в %40, двоеточие в %3A, слеш в %2F. Иначе программа разрежет строку в неправильном месте, отправит прокси обрубок пароля и получит 407.
Настройка прокси в Linux через переменные окружения
Самый частый способ. Переменные понимают curl, wget, pip, git и большинство скриптов на Python и Go. Для текущей сессии терминала:
export http_proxy="http://LOGIN:PASS@IP:PORT"
export https_proxy="http://LOGIN:PASS@IP:PORT"
export no_proxy="localhost,127.0.0.1,.local"Обратите внимание на https_proxy. В нём тоже стоит http://, потому что это адрес самого прокси: программа подключается к нему обычным HTTP и уже через него открывает зашифрованный туннель к сайту. Напишете https://, и клиент начнёт говорить с прокси по TLS на порту, который TLS не ждёт.
Регистр букв решает
На этом спотыкаются даже опытные админы. curl читает http_proxy только строчными буквами, вариант HTTP_PROXY он игнорирует намеренно, с 2001 года, из соображений безопасности. Остальные переменные (https_proxy, no_proxy, all_proxy) curl принимает в обоих регистрах, причём строчный вариант главнее. Другие программы ведут себя по-своему. Рецепт один: прописывайте обе версии с одинаковым значением.
Переменная all_proxy задаёт прокси для всех протоколов разом и удобна для SOCKS5, curl поймёт all_proxy=socks5h://... без лишних вопросов. wget SOCKS не поддерживает вовсе. Ему нужен HTTP-адрес, поэтому для старых утилит купить http прокси стоит заранее, для всего остального подойдёт SOCKS5.
В no_proxy перечисляют адреса, которые идут напрямую: локальные службы, внутренние домены. Записи через запятую, без пробелов. Точка в начале, например .local, у curl означает домен со всеми поддоменами. Диапазоны вида 10.0.0.0/8 curl понимает с версии 7.86, wget их не понимает.
Чтобы переменные пережили закрытие терминала, допишите строки export в ~/.bashrc или ~/.zshrc и выполните source. Это настройка одного пользователя.

/etc/environment и sudo: прокси на всю систему
Файл /etc/environment читает при входе модуль pam_env, и действует он для всех пользователей машины. Формат строгий: одна строка, одна пара КЛЮЧ=значение. Слово export не нужно, подстановка не работает, запись $HOME так и останется текстом из пяти символов.
http_proxy="http://LOGIN:PASS@IP:PORT"
https_proxy="http://LOGIN:PASS@IP:PORT"
no_proxy="localhost,127.0.0.1"
HTTP_PROXY="http://LOGIN:PASS@IP:PORT"
HTTPS_PROXY="http://LOGIN:PASS@IP:PORT"
NO_PROXY="localhost,127.0.0.1"Сработает после нового входа. Новая вкладка терминала не поможет: выйдите из сеанса или переподключитесь по SSH.
Вторая ловушка сидит в sudo. Он по умолчанию сбрасывает окружение, и команда под sudo прокси уже не видит. Создайте файл командой sudo visudo -f /etc/sudoers.d/proxy и впишите одну строку:
Defaults env_keep += "http_proxy https_proxy no_proxy HTTP_PROXY HTTPS_PROXY NO_PROXY"И помните, что /etc/environment открыт на чтение всем пользователям, а пароль от прокси лежит в нём открытым текстом. На общем сервере удобнее авторизация по IP. Тогда в строке остаются только адрес и порт.
apt и snap: свои файлы, свои правила
apt умеет брать прокси из http_proxy, только под sudo без env_keep он эту переменную не увидит. Надёжнее отдельный конфиг. Создайте файл /etc/apt/apt.conf.d/95proxy:
Acquire::http::Proxy "http://LOGIN:PASS@IP:PORT/";
Acquire::https::Proxy "http://LOGIN:PASS@IP:PORT/";Точка с запятой в конце строки обязательна. Забыли её, и apt при следующем запуске остановится с ошибкой Syntax error и пометкой Extra junk at end of file. Что apt видит в собранной конфигурации, покажет apt-config dump | grep -i proxy.
С версии 1.3 apt понимает SOCKS5: в строке Acquire можно указать socks5h://LOGIN:PASS@IP:PORT. Удобно, если у вас socks5 прокси сервер с авторизацией и заводить отдельный HTTP-адрес ради обновлений не хочется.
Пакеты snap ставит демон snapd, и ни переменные, ни конфиг apt его не касаются. Для него своя команда:
sudo snap set system proxy.http="http://LOGIN:PASS@IP:PORT"
sudo snap set system proxy.https="http://LOGIN:PASS@IP:PORT"Текущие значения выводит snap get system proxy.
Ubuntu с рабочим столом: Настройки и gsettings
В графической Ubuntu прокси включают в приложении «Настройки», раздел «Сеть», пункт прокси (в английском интерфейсе Network Proxy). Режимов три: выключен, вручную и автоматически по адресу PAC-файла. В ручном режиме есть отдельные поля HTTP, HTTPS, FTP и SOCKS, у каждого адрес и порт, ниже список исключений.
Полей для логина и пароля там нет. Это главный сюрприз Ubuntu. Если прокси требует пароль, задайте его через gsettings:
gsettings set org.gnome.system.proxy mode 'manual'
gsettings set org.gnome.system.proxy.http host 'IP'
gsettings set org.gnome.system.proxy.http port PORT
gsettings set org.gnome.system.proxy.http use-authentication true
gsettings set org.gnome.system.proxy.http authentication-user 'LOGIN'
gsettings set org.gnome.system.proxy.http authentication-password 'PASS'
gsettings set org.gnome.system.proxy ignore-hosts "['localhost', '127.0.0.0/8']"Логин из gsettings понимают далеко не все программы, поэтому на рабочем столе привязка по IP обычно избавляет от лишней возни. Эти настройки читают Chromium, приложения GNOME и всё, что построено на libproxy. Терминал, apt и Docker их не видят. Включили прокси в «Настройках», открыли консоль, а curl идёт напрямую: так и задумано, переменные окружения задаются отдельно. Firefox держит собственные настройки и по умолчанию просто следует системным, детали есть в статье о прокси в Chrome и Firefox.

Службы systemd и Docker живут отдельно
Службы systemd не читают ни ~/.bashrc, ни /etc/environment. Ровно на этом я и попался. Прокси конкретной службе задают через drop-in: команда sudo systemctl edit parser.service откроет редактор, впишите туда
[Service]
Environment="http_proxy=http://LOGIN:PASS@IP:PORT"
Environment="https_proxy=http://LOGIN:PASS@IP:PORT"и перезапустите службу через sudo systemctl restart parser.service. Важная мелочь: в файлах systemd знак процента служебный, поэтому закодированный пароль вида pa%40ss пишется как pa%%40ss.
Есть ещё параметр DefaultEnvironment в секции [Manager] файла /etc/systemd/system.conf, он раздаёт переменные всем службам сразу. Пользоваться им стоит осторожно. Под прокси уйдут и системные демоны, которым он ни к чему.
Docker устроен в два этажа. Нижний этаж это демон dockerd, который сам скачивает образы. Ему прокси задают в /etc/docker/daemon.json или таким же drop-in файлом /etc/systemd/system/docker.service.d/http-proxy.conf:
[Service]
Environment="HTTP_PROXY=http://LOGIN:PASS@IP:PORT"
Environment="HTTPS_PROXY=http://LOGIN:PASS@IP:PORT"
Environment="NO_PROXY=localhost,127.0.0.1"После правки нужны sudo systemctl daemon-reload и sudo systemctl restart docker. Верхний этаж это контейнеры и сборка образов: демон им свой прокси не передаёт. Для них есть блок proxies в ~/.docker/config.json:
{
"proxies": {
"default": {
"httpProxy": "http://LOGIN:PASS@IP:PORT",
"httpsProxy": "http://LOGIN:PASS@IP:PORT",
"noProxy": "localhost,127.0.0.1"
}
}
}Перезапускать Docker не надо. Новые контейнеры получат переменные при старте, уже запущенные останутся как были.
Через что ходит каждая программа: проверка по слоям
Слои проверяются по одному, на каждый одна команда:
env | grep -i proxy выводит переменные текущей сессии. Пусто, значит, терминал идёт напрямую.
curl -s https://api.ipify.org без ключа -x должен вернуть IP прокси.
sudo env | grep -i proxy показывает, не потерял ли sudo переменные.
apt-config dump | grep -i proxy выводит то, что видит apt.
gsettings get org.gnome.system.proxy mode отвечает 'manual', если прокси включён на рабочем столе.
systemctl show docker --property=Environment показывает переменные демона Docker.
Весь проход занимает минуту.
Если во втором шаге вернулся адрес сервера, переменные не применились: ищите опечатку в имени или неверный регистр. Если curl отвечает кодом 407, логин или пароль не дошли до прокси, чаще всего из-за символа @ без кодирования. Что означает каждый код ответа и кто его прислал, разобрано в справке по ошибкам прокси-серв ера.
Когда Linux настроен, а сайт отвечает 429
Последний сценарий уже за пределами Linux. Все шесть мест прописаны, трафик идёт через прокси, и всё равно целевой сайт после нескольких сотен запросов начинает отдавать 429 или 403. Это лимит на адрес. Один IP выдерживает ограниченный поток, как бы аккуратно ни был настроен systemd.
Снимается это пулом, где адрес меняется сам, плюс разумным темпом запросов. Аренда прокси у WinGate даёт пул с новым IP на каждый запрос: ipv4 socks5 прокси и HTTP, трафик безлимитный, потоков до 5000. В Linux это та же с трока в http_proxy или all_proxy, ничего из описанного выше переделывать не придётся. Авторизация по IP уберёт пароль из /etc/environment. Пропишите адрес в службу, запустите прогон и посчитайте, сколько запросов пройдёт без 429. Адреса для такого прогона дают на бесплатном тесте до 2 часов.
Свежие статьи
