Configurar proxy en Linux: terminal, apt, Docker y Git

Publicado:

19 min de lectura

Acar Diveroli
Autor: Acar Diveroli
Marco de esquinas en cruz: logo de Proxynet, un aspa y el de Linux, sobre filas de IP tenues; etiqueta INTEGRATION

Definiste el proxy en un servidor Linux, curl funciona y, sin embargo, sudo apt update sigue yendo a la dirección antigua, git clone se queda colgado y docker build no llega a la red. Eso no significa que hayas configurado algo mal. En Linux no existe un "interruptor de proxy" central: las variables de entorno de tu shell son una capa, el archivo de configuración propio de cada herramienta es la segunda y los servicios que corren bajo systemd son una tercera capa, totalmente aparte.

Este artículo recorre esas tres capas en orden. Primero la configuración temporal y permanente con variables de entorno, la diferencia entre mayúsculas y minúsculas, las reglas de escritura de no_proxy y por qué sudo no arrastra tu entorno. Después la configuración de apt, git, pip, npm y Docker una por una, por qué el ajuste del escritorio no afecta a la terminal, los pasos de verificación y la línea para deshacer cada ajuste.

¿Por qué el proxy en Linux no se configura en un solo sitio?

En Windows y macOS el sistema operativo guarda un registro de proxy del sistema y la mayoría de los programas lo lee. En Linux no existe ese registro. En su lugar hay una convención que viene de los años noventa: al ejecutarse, un programa mira las variables de entorno http_proxy, https_proxy, all_proxy y no_proxy y las usa si están definidas. No es un estándar, es una costumbre extendida. Tienes que saber herramienta por herramienta cuál la respeta.

Separar las tres capas resuelve la mayoría de los problemas:

  1. El entorno del shell. Las variables que defines con export se heredan a los procesos que lanzas desde ese shell. Al cerrar la terminal desaparecen.
  2. La configuración propia de la herramienta. apt, git, pip, npm y Docker tienen sus propios archivos. Esos archivos funcionan al margen de las variables de entorno y suelen tener prioridad sobre ellas.
  3. El entorno del servicio. Un servicio arrancado por systemd no nace de tu shell, así que nunca ve tus variables. Lee de su propio archivo de unidad.

Para entender por qué un ajuste "no funciona", primero hay que preguntarse a qué capa mira esa herramienta.

¿En qué formato se escribe la dirección del proxy?

En todos los métodos siguientes la dirección se escribe igual:

text
http://user:pass@pr.proxynet.io:8000

El esquema http:// es el protocolo de la conexión que se establece con el proxy y se mantiene igual aunque vayas a direcciones HTTPS. La parte user:pass@ solo hace falta si te autenticas con usuario y contraseña; si autorizaste la IP de tu servidor en el panel, la omites por completo. Explicamos la diferencia entre ambos métodos en Autenticación de proxy: user:pass o lista blanca de IP.

Si tu contraseña contiene @, :, # o /, escríbelos dentro de la dirección con codificación porcentual: %40 para @ y %23 para #. Una @ sin codificar parte la dirección por el sitio equivocado y el resultado suele ser un 407 Proxy Authentication Required. Los detalles de la codificación y las diferencias entre bibliotecas están en la sección de credenciales de Cómo usar un proxy en Node.js: Axios y node-fetch.

Configuración temporal con variables de entorno

La vía más rápida es definir las variables para esa sesión:

bash
export http_proxy="http://user:pass@pr.proxynet.io:8000"
export https_proxy="$http_proxy"
export no_proxy="localhost,127.0.0.1,.tuempresa.local"

curl -s https://api.ipify.org; echo

Si la salida muestra la IP del proxy y no la tuya, el ajuste funciona. Para quitar las variables basta con unset http_proxy https_proxy no_proxy.

Qué son estas variables, qué herramientas las leen y cómo se relacionan con archivos propios como .wgetrc lo explicamos en detalle en Proxy con wget: comandos y ejemplos, y no lo repetimos aquí. Para las opciones del lado de cURL y el uso de SOCKS5 puedes consultar Cómo usar cURL con proxy: comandos y ejemplos.

Dos notas prácticas: si no escribes export, la variable se queda solo en el shell y no pasa a los programas que ejecutas. Si quieres un ajuste temporal para un único comando, pon la variable delante del comando y solo ese comando se verá afectado:

bash
https_proxy="http://user:pass@pr.proxynet.io:8000" curl -s https://api.ipify.org

http_proxy y HTTP_PROXY: ¿por qué importan las mayúsculas?

En Linux los nombres de las variables de entorno distinguen mayúsculas y minúsculas: http_proxy y HTTP_PROXY son dos variables distintas. Las herramientas no las tratan igual.

cURL y todo lo que usa libcurl acepta las variables de proxy tanto en minúsculas como en mayúsculas, y da prioridad a la versión en minúsculas. La única excepción es http_proxy: cURL solo la lee en minúsculas. El motivo es la seguridad. Cuando se ejecuta un script CGI, el servidor convierte las cabeceras de la petición entrante en variables de entorno con el prefijo HTTP_, es decir, una cabecera Proxy: enviada de forma remota puede rellenar la variable HTTP_PROXY. Los problemas de seguridad que este comportamiento causó en el pasado están descritos en la documentación de cURL.

En la práctica te bastan dos reglas:

  • Toma las minúsculas como referencia. Escribe http_proxy, https_proxy, no_proxy.
  • Define ambas. Algunas herramientas escritas en Java y en Go solo buscan la versión en mayúsculas. Poner los dos grupos al mismo valor es lo que menos sorpresas da.
bash
export http_proxy="http://user:pass@pr.proxynet.io:8000"
export https_proxy="$http_proxy"
export no_proxy="localhost,127.0.0.1"
# define tambien los gemelos en mayusculas
export HTTP_PROXY="$http_proxy" HTTPS_PROXY="$https_proxy" NO_PROXY="$no_proxy"

all_proxy es el valor por defecto independiente del protocolo; si existe una variable específica del protocolo, esa tiene prioridad. Si vas a usar SOCKS5, prefiere la forma all_proxy="socks5h://user:pass@pr.proxynet.io:1080". Con el esquema socks5h la resolución del nombre de dominio se hace en el lado del proxy. Encontrarás las diferencias en nuestra página de Proxies SOCKS5 y en Diferencia entre SOCKS y HTTP: ¿cuál elegir?.

¿Cómo se escribe no_proxy?

no_proxy es una lista separada por comas de direcciones que no deben pasar por el proxy. No tiene un estándar, así que verás pequeñas diferencias entre herramientas. En nuestras pruebas con cURL 8.21 el comportamiento es este:

EscrituraResultado
example.comexample.com y www.example.com van sin proxy, sea cual sea el puerto
.example.comcubre los subdominios y el propio dominio
sub.example.comsolo ese subdominio; el example.com superior sigue pasando por el proxy
EXAMPLE.COMno distingue mayúsculas, coincide
example.com:80no coincide, no se admite indicar el puerto
10.0.0.0/8la notación CIDR funciona desde cURL 7.86
*un solo asterisco deja todas las direcciones fuera del proxy

Hay dos trampas. La primera es indicar el puerto: no_proxy es una lista de hosts, así que si escribes servidor:puerto esa línea no coincide con nada y la petición va al proxy en silencio. La segunda es que CIDR no funciona en todas partes; el módulo urllib de Python no reconoce la entrada 10.0.0.0/8 en esa misma lista y envía la dirección 10.1.2.3 al proxy. Si quieres dejar la red interna fuera del lado de Python, escribe las direcciones una a una o por nombre de dominio.

Pon al menos esto en la lista: localhost, 127.0.0.1, tu dominio de red interna si lo tienes y la red de contenedores. De lo contrario, la petición a tu servidor de desarrollo local también dará la vuelta por el proxy y acabará en un tiempo de espera agotado.

Hacer permanente el ajuste: ~/.bashrc y /etc/environment

Al cerrar la terminal, las líneas export se pierden. Para la permanencia hay dos sitios y su alcance es distinto.

~/.bashrc para un solo usuario. Añade las mismas líneas export al final del archivo y luego vuelve a leerlo con source ~/.bashrc. Este archivo se ejecuta en shells interactivos, es decir, se aplica cuando entras por SSH y escribes comandos. Si usas Zsh el equivalente es ~/.zshrc y las líneas valen igual; si usas fish el archivo es ~/.config/fish/config.fish y la escritura pasa a ser set -gx http_proxy "…".

/etc/environment para todo el sistema. Este archivo no lo lee el shell, sino el módulo pam_env de PAM. Las variables se definen para cada usuario que abre sesión. Su formato es estricto: un CLAVE=VALOR por línea, y la palabra clave export se acepta por compatibilidad como dice la documentación pero se ignora, y no hay expansión del shell. Es decir, una referencia como $http_proxy no funciona aquí, tienes que escribir el valor explícitamente:

ini
# contenido de /etc/environment, sin comillas y en plano
http_proxy=http://user:pass@pr.proxynet.io:8000
https_proxy=http://user:pass@pr.proxynet.io:8000
no_proxy=localhost,127.0.0.1

El cambio se aplica a las sesiones nuevas; para verlo en tu sesión SSH abierta, sal y vuelve a entrar. Para deshacerlo basta con borrar las líneas y renovar la sesión.

En ambos archivos la contraseña queda en texto plano. Si el servidor tiene más de un usuario, prefiere el archivo de usuario a /etc/environment, o autoriza la IP del servidor en el panel y pasa a un uso sin contraseña. Para este escenario, que necesita una dirección de salida fija, encajan los paquetes Proxies ISP y Proxies de centro de datos.

¿Por qué sudo no ve tu configuración de proxy?

Si sudo apt update ignora el proxy, la causa suele ser esta: por seguridad, sudo sustituye la mayoría de las variables de entorno del usuario que lo invoca por un entorno limpio. Con la opción -E le indicas que quieres conservar el entorno del usuario; como señala el manual de sudo, la política de seguridad puede rechazar esa petición.

bash
sudo -E apt update

Si el comando sigue sin usar el proxy, quedan dos posibilidades: o la configuración de sudoers no permite el paso de esas variables, o la herramienta ya no mira la variable de entorno sino su propio archivo. Para apt lo segundo es una solución más sólida y es el tema de la siguiente sección.

Configuración de proxy en apt

apt también lee las variables de entorno, pero su sitio de verdad es el directorio /etc/apt/apt.conf.d/. Un archivo de fragmento que dejes ahí resuelve tanto el problema de sudo como las actualizaciones que corren por cron.

text
// /etc/apt/apt.conf.d/95proxy
Acquire::http::Proxy "http://user:pass@pr.proxynet.io:8000";
Acquire::https::Proxy "http://user:pass@pr.proxynet.io:8000";

El punto y coma al final de la línea es obligatorio. Para dejar un servidor concreto fuera del proxy, escribes la línea propia de ese host con la palabra clave DIRECT:

text
Acquire::http::Proxy::repo.tuempresa.local "DIRECT";

Hay un detalle en el nombre del archivo: según el manual de apt.conf, de los fragmentos de ese directorio solo se leen los que no tienen extensión o la tienen conf y cuyo nombre contiene únicamente letras, cifras, guiones, guiones bajos y puntos. Es decir, 95proxy y 95proxy.conf son válidos; 95proxy.bak se ignora y apt lo avisa con un mensaje. Para deshacer el ajuste, borra el archivo o sácalo del directorio, no hace falta ningún comando adicional.

Configuración de proxy en git

git usa libcurl para las direcciones HTTP y HTTPS, así que ya lee las variables http_proxy y https_proxy. Si quieres un ajuste permanente y explícito, escríbelo en su propia configuración:

bash
git config --global http.proxy "http://user:pass@pr.proxynet.io:8000"

git config --global --get http.proxy      # verifica
git config --global --unset http.proxy    # deshaz

Si quieres que valga solo para un servidor concreto, puedes condicionarlo a la dirección. Así vas directo a tu servidor git interno y por el proxy hacia fuera:

bash
git config --global http.https://github.com.proxy "http://user:pass@pr.proxynet.io:8000"

Un detalle que salió en nuestra prueba causa problemas a menudo. El valor por defecto del ajuste http.proxyAuthMethod de git es anyauth y, según la documentación de git-config, ese modo espera que el proxy responda a una petición sin credenciales con un 407 y una cabecera Proxy-Authenticate. En los proxies que no completan esa ronda de descubrimiento, git se detiene con el error Proxy CONNECT aborted. El mismo comando pasa a la primera con basic:

bash
git config --global http.proxyAuthMethod basic

Si quieres probar el ajuste para un solo comando, la opción -c no toca la configuración en absoluto: git -c http.proxy=... ls-remote <dirección>. Si haces las descargas por SSH, ninguno de estos ajustes tiene efecto; para SSH hace falta la línea ProxyCommand en ~/.ssh/config.

Configuración de proxy en pip

pip tiene tres vías y el orden de prioridad está escrito en la documentación de pip: las opciones de línea de comandos tienen prioridad sobre las variables de entorno, y estas sobre el archivo de configuración.

bash
# 1) de una sola vez
pip install --proxy "http://user:pass@pr.proxynet.io:8000" requests

# 2) variable de entorno; el patron PIP_<OPCION> vale para cada opcion
export PIP_PROXY="http://user:pass@pr.proxynet.io:8000"

Para un ajuste permanente usa el archivo ~/.config/pip/pip.conf. /etc/pip.conf para todo el sistema y $VIRTUAL_ENV/pip.conf para un único entorno virtual aceptan el mismo formato:

ini
[global]
proxy = http://user:pass@pr.proxynet.io:8000

Si no tienes claro qué archivo se está leyendo, pip config debug lista todas las rutas y los valores vigentes en ese momento. Para deshacerlo escribe pip config unset global.proxy o borra la línea del archivo.

Configuración de proxy en npm

npm lee los archivos .npmrc en el orden proyecto, usuario, global e incorporado; el de delante tiene prioridad sobre el de detrás. Según la documentación de npm, también se respetan las variables de entorno HTTP_PROXY y HTTPS_PROXY, y el valor por defecto de la opción noproxy es la variable NO_PROXY.

bash
npm config set proxy "http://user:pass@pr.proxynet.io:8000"
npm config set https-proxy "http://user:pass@pr.proxynet.io:8000"
npm config set noproxy "localhost,127.0.0.1,registry.tuempresa.local"

npm config delete proxy && npm config delete https-proxy   # deshaz

Para que el ajuste valga solo en un repositorio, añade --location=project a los comandos; npm escribirá entonces los valores en el archivo .npmrc de la raíz del proyecto. No subas ese archivo al control de versiones, contiene una contraseña.

Una pequeña sorpresa: el comando npm config get https-proxy no muestra el valor, sino un aviso de "protected". Las opciones que contienen credenciales están cerradas a la lectura. Para ver el valor, abre el archivo .npmrc directamente.

Configuración de proxy en Docker

En Docker no hay un único ajuste: el proceso en segundo plano y los contenedores lo leen de dos sitios independientes, y encima está la clásica trampa de la dirección. De ahí viene la mayor parte de la confusión.

1. El proceso en segundo plano (descarga de imágenes). docker pull y docker push los hace dockerd, no tu shell. Según la documentación de Docker, el ajuste se escribe en la clave proxies del archivo /etc/docker/daemon.json:

json
{
  "proxies": {
    "http-proxy": "http://user:pass@pr.proxynet.io:8000",
    "https-proxy": "http://user:pass@pr.proxynet.io:8000",
    "no-proxy": "localhost,127.0.0.1,.tuempresa.local"
  }
}

El mismo trabajo también se hace con un archivo de fragmento del lado de systemd. En el archivo /etc/systemd/system/docker.service.d/http-proxy.conf se escriben estas líneas:

ini
[Service]
Environment="HTTP_PROXY=http://user:pass@pr.proxynet.io:8000"
Environment="HTTPS_PROXY=http://user:pass@pr.proxynet.io:8000"
Environment="NO_PROXY=localhost,127.0.0.1,.tuempresa.local"

En ambos métodos el ajuste entra en vigor después de sudo systemctl daemon-reload y sudo systemctl restart docker. Este patrón no es exclusivo de Docker: a todo servicio que corre bajo systemd se le presenta el proxy de esta manera, porque el servicio no nace de tu shell y nunca lee tu ~/.bashrc.

2. Contenedores y compilaciones. Que la aplicación dentro del contenedor salga a la red es otro asunto. Como se explica en su propia documentación, la CLI de Docker lee el bloque proxies.default del archivo ~/.docker/config.json y pasa esos valores como variables de entorno a los contenedores y compilaciones nuevos:

json
{
  "proxies": {
    "default": {
      "httpProxy": "http://user:pass@pr.proxynet.io:8000",
      "httpsProxy": "http://user:pass@pr.proxynet.io:8000",
      "noProxy": "localhost,127.0.0.1"
    }
  }
}

Los ajustes de este archivo no afectan al proceso en segundo plano; solo pasan al entorno de contenedores y compilaciones, y solo a los creados después. Para un uso puntual, docker run --env HTTP_PROXY=... y docker build --build-arg HTTP_PROXY=... hacen lo mismo.

3. La trampa de la dirección. Si el proxy corre en la propia máquina, no escribas http://127.0.0.1:8000 dentro del contenedor. El contenedor tiene su propio espacio de red y ese 127.0.0.1 es el contenedor mismo. Para llegar a la máquina anfitriona, usa su dirección de red.

¿Por qué el ajuste del escritorio no afecta a la terminal?

En Ubuntu, la definición que haces en Configuración > Red > Proxy de red se escribe en el almacén de ajustes propio de GNOME y afecta a las aplicaciones que leen esos valores: el navegador de GNOME, el centro de software, la mayoría de las aplicaciones de escritorio. El shell que abres en la terminal no lee ese almacén, así que curl y git no se ven afectados. Si quieres hacer el mismo ajuste desde la línea de comandos, se usan gsettings set org.gnome.system.proxy mode 'manual' y las claves host y port correspondientes, pero para el lado de la terminal sigue haciendo falta la variable de entorno.

La consecuencia práctica es esta: si el servidor no tiene escritorio, sáltate esta sección por completo. Si trabajas en un escritorio, tienes que hacer los dos ajustes. Para los equivalentes en otros sistemas operativos puedes ver Configuración de proxy en Windows y Chrome, Cómo configurar un proxy en Mac y Safari y Cómo configurar un proxy en un teléfono Android.

Tabla de herramienta, archivo y forma de deshacer

HerramientaDónde va el ajusteCómo deshacerlo
Shell (temporal)export http_proxy=…unset http_proxy https_proxy no_proxy
Shell (usuario)~/.bashrc, ~/.zshrcBorra la línea y recarga con source
Todo el sistema/etc/environmentBorra la línea y renueva la sesión
Servicio systemd/etc/systemd/system/<nombre>.service.d/*.confBorra el archivo, daemon-reload
apt/etc/apt/apt.conf.d/95proxySaca el archivo del directorio
gitgit config --global http.proxygit config --global --unset http.proxy
pip~/.config/pip/pip.conf, --proxypip config unset global.proxy
npm.npmrc (npm config set proxy)npm config delete proxy
Proceso de Docker/etc/docker/daemon.jsonBorra la clave y reinicia el servicio
Contenedores de Docker~/.docker/config.jsonBorra el bloque proxies
Escritorio GNOMEConfiguración > Red > Proxy de redPon el modo en "Desactivado"

¿Cómo compruebas que el ajuste funciona?

Cuatro pasos, en orden:

  1. Mira las variables. La salida de env | grep -i proxy debe mostrar los valores que esperas y sus gemelos en mayúsculas.
  2. Mira la dirección de salida. La dirección que devuelve curl -s https://api.ipify.org debe ser la del proxy.
  3. Sigue la conexión. En la salida de curl -v busca la línea Uses proxy env variable y una línea Trying que vaya a la dirección del proxy en lugar de al destino. Si faltan esas dos líneas, la petición no está llegando al proxy.
  4. Prueba cada herramienta por separado. Los comandos sudo apt update, git ls-remote <dirección>, pip download --no-deps six y npm view express version usan su propia configuración y hay que verificar cada uno por su cuenta.

Reunimos los pasos detallados de medición y verificación de ubicación en ¿Funciona tu proxy? Cómo probar un proxy.

Errores frecuentes

  • Olvidar export. Por sí solo, http_proxy=... deja la variable únicamente en el shell y no la pasa al programa que ejecutas.
  • Definir solo http_proxy. Las peticiones a direcciones HTTPS miran la variable https_proxy; si falta, intentan salir directamente.
  • Empezar el valor de https_proxy con https://. Si tu proxy no termina TLS, el valor empieza por http://.
  • Escribir la contraseña sin codificar. La @ que lleva dentro parte la dirección y el resultado es un 407. Enumeramos las demás causas del 407 en Códigos de estado HTTP en web scraping: 403, 407, 429, 503.
  • Escribir un puerto en la lista no_proxy. La entrada servidor:8080 no coincide con nada.
  • Esperar que un servicio siga el ajuste del shell. Un servicio de systemd no lee ~/.bashrc; hace falta un archivo de unidad o de fragmento.
  • Olvidar el archivo de configuración y culpar a la variable de entorno. git config --get http.proxy, pip config debug y npm config list sacan a la luz un valor antiguo.
  • Escribir 127.0.0.1 dentro del contenedor. El contenedor tiene su propio espacio de red.

El diagnóstico de los casos en los que la conexión ni siquiera se establece lo tratamos uno a uno en El servidor proxy no responde: qué es y cómo solucionarlo.

Guía de decisión

NecesidadAjuste recomendado
Pasar un solo comando por el proxyEscribe la variable delante del comando
Trabajar en la terminal durante la sesiónLíneas export
Permanente en el servidor, un solo usuario~/.bashrc
Permanente en el servidor, todos los usuarios/etc/environment
Que pasen las actualizaciones de paquetes/etc/apt/apt.conf.d/95proxy
Solo el tráfico de git hacia repositorios externoshttp.<dirección>.proxy
Un único paso en un flujo de CIPIP_PROXY o npm_config_proxy
Una compilación de contenedor~/.docker/config.json o --build-arg
Un servicio en segundo plano (dockerd, cron)Un archivo de fragmento de systemd
Dejar fuera la red internano_proxy y Acquire::…::DIRECT

Preguntas frecuentes

Hice los ajustes pero algunos programas siguen saliendo directos, ¿por qué?

Leer las variables de entorno no es una obligación, es una costumbre. Algunas herramientas escritas en Go solo buscan la versión en mayúsculas y algunas aplicaciones Java esperan su propio parámetro -Dhttp.proxyHost. Mira el apartado de proxy en la documentación del programa; si tiene su propio ajuste, la variable de entorno no lo sustituye.

¿Puedo usar un proxy sin escribir la contraseña en un archivo?

Sí. Si autorizas la IP de salida de tu servidor en el panel del proxy, la dirección se reduce a http://pr.proxynet.io:8000 y no queda ninguna contraseña en ningún archivo. En servidores con IP fija es la vía más limpia.

¿Puedo usar varios proxies a la vez?

Las variables de entorno guardan una sola dirección. La distinción la haces por protocolo (http_proxy y https_proxy con valores distintos) o por herramienta (http.<dirección>.proxy en git, una línea específica por host en apt). Si necesitas una dirección de salida distinta en cada petición, un paquete Proxies rotativos de dirección única hace ese trabajo por ti.

¿Mis conexiones SSH también pasan por el proxy?

No. http_proxy y sus compañeras no afectan al cliente SSH. Para pasar SSH por un proxy se define la línea ProxyCommand en el archivo ~/.ssh/config. Por eso las direcciones del tipo git clone git@... también ignoran el ajuste http.proxy.

¿Por qué mis tareas de cron no ven el proxy?

cron no lanza las tareas desde tu shell de inicio de sesión y no lee tu ~/.bashrc. Define las variables al principio del archivo crontab o ponlas con export en las primeras líneas del script.

apt se ha vuelto lento con el proxy, ¿es normal?

Los repositorios de paquetes están distribuidos geográficamente y el proxy puede llevar el tráfico a otro país. Usar el proxy solo para las fuentes externas y dejar fuera las réplicas locales con la línea Acquire::http::Proxy::<host> "DIRECT"; suele bastar.

En resumen

La configuración de proxy en Linux tiene tres capas: el entorno del shell, el archivo de configuración propio de la herramienta y la unidad de servicio. Empieza con export en la terminal, usa ~/.bashrc o /etc/environment para la permanencia y luego configura apt, git, pip, npm y Docker por separado desde sus propios archivos. Verifica en cada paso con curl -s https://api.ipify.org y no te saltes el añadir tu red interna a la lista no_proxy. Para trabajos fijos y de alto volumen que corren en un servidor, echa un vistazo a nuestras soluciones de extracción de datos o ve directamente a nuestra página de Proxies HTTPS.