서버를 관리하거나 자동화 스크립트를 작성하거나 기업 인프라에 애플리케이션을 배포하는 경우 — 언젠가는 curl 또는 wget의 트래픽을 프록시를 통해 보내야 할 필요성에 직면하게 됩니다. 이는 기업 프록시일 수도 있고, 패키지를 다운로드할 때 지리적 차단을 우회하는 것이거나, 외부 API에 대한 대량 요청 시 IP를 회전하는 것일 수 있습니다. 이 기사에서는 실용적인 내용만 다룹니다: 명령어, 설정, 코드 예제와 함께 불필요한 내용은 생략합니다.
1. curl 및 wget이 프록시와 함께 작동하는 방법: 기본 메커니즘
설정 파일을 살펴보기 전에, 내부에서 무슨 일이 일어나고 있는지 이해하는 것이 중요합니다. 두 도구 모두 HTTP/HTTPS 및 SOCKS5라는 두 가지 기본 프록시 프로토콜을 지원합니다. 이들의 메커니즘은 다르며, 이는 특정 작업에 적합한 프록시 유형을 선택하는 데 영향을 미칩니다.
HTTP 프록시는 애플리케이션 프로토콜 수준에서 중개자로 작동합니다. curl이 HTTP 프록시를 통해 요청을 보낼 때, 실제로 프록시 서버에 "내 대신 이 URL로 GET 요청을 해줘"라고 말하는 것입니다. HTTPS 트래픽의 경우 CONNECT 메서드가 사용되며 — curl은 프록시에게 대상 호스트로의 터널을 설정해달라고 요청하고, 이후 TLS 핸드셰이크는 클라이언트와 대상 서버 간에 직접 발생합니다. 이는 중요합니다: 이 경우 프록시는 HTTPS 트래픽의 내용을 볼 수 없습니다.
SOCKS5는 더 낮은 수준에서 작동하며 — 애플리케이션 프로토콜에 구애받지 않고 TCP/UDP 연결을 프록시합니다. 이는 SOCKS5를 더 유연하게 만들어 줍니다: HTTP/HTTPS뿐만 아니라 다른 프로토콜도 통과시킬 수 있습니다. 또한 SOCKS5는 프록시 서버 측에서 DNS 해석을 지원합니다 — 이는 DNS 누수를 방지하는 데 매우 중요합니다.
💡 실습을 위한 주요 차이점:
단순히 파일을 다운로드하거나 API 요청을 해야 하는 경우 — HTTP 프록시로 충분합니다. 완전한 익명성이 필요하거나 DNS 누수를 우회해야 하거나 비표준 프로토콜로 작업해야 하는 경우 — SOCKS5를 사용하세요.
curl은 매우 초기 버전부터 두 프로토콜을 기본적으로 지원합니다. wget은 역사적으로 SOCKS5 지원이 더 제한적이었으며 — 이전 버전(1.19 이전)에서는 SOCKS5를 전혀 지원하지 않고, HTTP 프록시만 지원합니다. 이는 다양한 배포판에서 작동해야 하는 스크립트를 작성할 때 고려해야 할 사항입니다.
wget 버전을 확인하려면 wget --version 명령어를 사용할 수 있습니다. curl의 경우는 curl --version를 사용하면, libcurl 라이브러리가 어떤 프로토콜로 빌드되었는지 확인할 수 있습니다.
2. 환경 변수: 가장 빠른 설정 방법
curl 및 wget을 위한 프록시를 설정하는 가장 우아한 방법은 환경 변수를 사용하는 것입니다. 이는 시스템적으로 작동하므로, 각 스크립트를 변경할 필요 없이 세션에서 한 번만 변수를 설정하거나 사용자 프로필에 추가하면 됩니다.
두 도구 모두 동일한 표준 변수를 읽습니다:
# HTTP 트래픽을 위한 export http_proxy="http://proxy.example.com:3128" # HTTPS 트래픽을 위한 export https_proxy="http://proxy.example.com:3128" # 대문자로 복사 (일부 프로그램은 대문자만 읽음) export HTTP_PROXY="http://proxy.example.com:3128" export HTTPS_PROXY="http://proxy.example.com:3128" # FTP (필요할 경우) export ftp_proxy="http://proxy.example.com:3128" # 예외 — 프록시를 통과하지 않는 주소 export no_proxy="localhost,127.0.0.1,::1,192.168.0.0/16,.internal.company.com"
중요한 점: curl은 버전에 따라 변수의 대소문자를 구분합니다. 예기치 않은 상황을 피하기 위해 — 항상 두 가지 버전을 모두 설정하세요: 소문자와 대문자. 이는 DevOps에서의 표준 관행입니다.
특정 사용자에 대해 설정이 지속적으로 적용되도록 하려면 ~/.bashrc 또는 ~/.profile에 이 줄들을 추가하세요. 시스템 스크립트 및 서비스의 경우 — /etc/environment 또는 systemd 유닛 파일의 Environment= 지시문을 통해 추가하세요.
# /etc/systemd/system/myservice.service
[Service]
Environment="http_proxy=http://proxy.example.com:3128"
Environment="https_proxy=http://proxy.example.com:3128"
Environment="no_proxy=localhost,127.0.0.1"
ExecStart=/usr/bin/myapp
프록시가 인증을 요구하는 경우, 사용자 이름과 비밀번호는 URL 변수에 직접 삽입됩니다:
export http_proxy="http://username:[email protected]:3128" export https_proxy="http://username:[email protected]:3128"
⚠️ 보안:
생산 서버의 .bashrc 또는 환경 변수에 비밀번호를 평문으로 저장하지 마세요. HashiCorp Vault, AWS Secrets Manager와 같은 vault 솔루션을 사용하거나 최소한 변수 파일의 권한을 제한하세요.
3. 프록시와 함께 사용할 curl 플래그: 전체 분석
curl은 명령줄에서 프록시를 관리하기 위한 풍부한 플래그 세트를 제공합니다. 이는 시스템 설정을 변경하지 않고도 프록시를 통해 일회성 요청을 해야 할 때 유용합니다.
기본 플래그는 -x 또는 --proxy입니다:
# 기본 HTTP 프록시 curl -x http://proxy.example.com:3128 https://api.example.com/data # 짧은 형식 curl -x proxy.example.com:3128 https://api.example.com/data # 프로토콜을 명시적으로 지정 curl --proxy http://proxy.example.com:3128 https://api.example.com/data # 프록시를 통해 외부 IP 확인 curl -x http://proxy.example.com:3128 https://api.ipify.org
환경 변수를 무시하고 직접 연결(설정된 프록시를 우회)하려면 --noproxy 플래그를 사용합니다:
# 특정 호스트에 대해 프록시 무시 curl --noproxy "internal.company.com" https://internal.company.com/api # 프록시를 완전히 무시 (환경 변수가 설정되어 있어도) curl --noproxy "*" https://api.example.com/data
프록시 연결을 디버깅하기 위한 유용한 플래그:
# 상세 모드: 모든 헤더가 표시되며, 프록시로의 CONNECT도 포함됨 curl -v -x http://proxy.example.com:3128 https://api.example.com/data # 응답 헤더만 표시 curl -I -x http://proxy.example.com:3128 https://api.example.com/data # 프록시를 통해 연결 시간 표시 curl -w "Connect: %{time_connect}s\nTotal: %{time_total}s\n" \ -x http://proxy.example.com:3128 \ -o /dev/null -s https://api.example.com/data
~/.curlrc 구성 파일을 통해 프록시를 설정하는 것은 매번 플래그를 입력하고 싶지 않을 때 편리합니다:
# ~/.curlrc
proxy = http://proxy.example.com:3128
proxy-user = username:password
noproxy = localhost,127.0.0.1,.internal.company.com
시스템 스크립트의 경우 별도의 구성 파일을 만들고 curl -K /path/to/config를 통해 명시적으로 지정할 수 있습니다 — 이는 다양한 작업에 대해 서로 다른 프록시 프로필을 가질 수 있게 해줍니다.
4. wget에서 프록시 설정: 플래그 및 구성 파일
wget은 curl보다 유연성이 떨어지지만, 일반적인 작업 — 파일 다운로드, 사이트의 재귀적 미러링 — 에 대해서는 충분한 기능을 제공합니다. wget에서 프록시는 환경 변수를 통해(위에서 설명한 대로), 명령줄 플래그를 통해, 그리고 ~/.wgetrc 구성 파일을 통해 설정할 수 있습니다.
wget의 프록시를 위한 명령줄 플래그:
# 플래그를 통한 HTTP 프록시 wget -e use_proxy=yes \ -e http_proxy=http://proxy.example.com:3128 \ https://example.com/file.tar.gz # HTTPS 프록시를 통한 wget -e use_proxy=yes \ -e https_proxy=http://proxy.example.com:3128 \ https://example.com/file.tar.gz # 인증이 필요한 경우 wget -e use_proxy=yes \ -e http_proxy=http://username:[email protected]:3128 \ https://example.com/file.tar.gz # 특정 명령에 대해 프록시 비활성화 (환경 변수가 설정되어 있는 경우) wget --no-proxy https://internal.company.com/file.tar.gz
~/.wgetrc 구성 파일은 지속적인 설정을 위한 선호하는 방법입니다:
# ~/.wgetrc use_proxy = on http_proxy = http://proxy.example.com:3128 https_proxy = http://proxy.example.com:3128 ftp_proxy = http://proxy.example.com:3128 no_proxy = localhost,127.0.0.1,.internal.company.com # 프록시가 인증을 요구하는 경우 proxy_user = username proxy_password = secretpassword
시스템 적용(모든 사용자)을 위해서는 /etc/wgetrc를 사용합니다 — 동일한 형식이지만 전역적으로 적용됩니다. 모든 다운로드 작업이 기업 프록시를 통해 이루어져야 하는 서버에 유용합니다.
실용적인 예: 깊이 및 속도 제한을 두고 프록시를 통해 사이트를 재귀적으로 다운로드하는 방법:
wget -e use_proxy=yes \
-e http_proxy=http://proxy.example.com:3128 \
--recursive \
--level=2 \
--limit-rate=500k \
--wait=1 \
--random-wait \
--user-agent="Mozilla/5.0 (compatible; Googlebot/2.1)" \
https://example.com/docs/
5. SOCKS5 프록시를 curl 및 wget에서: 설정 및 예제
SOCKS5는 익명성이 중요하거나 비표준 포트를 사용하는 작업에 더 선호되는 프로토콜입니다. 시스템 관리자 및 DevOps 엔지니어에게 SOCKS5는 SSH 터널을 통해 작업할 때, 그리고 주거용 프록시에 연결할 때 자주 사용됩니다. 이는 실제 사용자 트래픽을 모방합니다.
curl에서 SOCKS5는 프록시 URL에 특별한 접두사를 통해 지원됩니다:
# 클라이언트 측에서 DNS 해석 (DNS 누수 가능!) curl --socks5 proxy.example.com:1080 https://api.example.com/data # 프록시 측에서 DNS 해석 (추천!) curl --socks5-hostname proxy.example.com:1080 https://api.example.com/data # -x 플래그를 통해 프로토콜 명시적으로 지정 curl -x socks5h://proxy.example.com:1080 https://api.example.com/data # 인증이 필요한 경우 curl -x socks5h://username:[email protected]:1080 https://api.example.com/data # 환경 변수를 통한 SOCKS5 export all_proxy="socks5h://proxy.example.com:1080" curl https://api.example.com/data
📌 socks5 vs socks5h — 차이점은 무엇인가요?
socks5 — DNS 요청이 로컬에서 수행되며, 프록시를 통해서는 TCP 연결만 진행됩니다. DNS 누수가 발생할 수 있습니다.socks5h (h = hostname) — DNS 요청이 프록시 서버 측에서 수행됩니다. 완전한 익명성을 보장합니다. 대부분의 작업에 추천됩니다.
DevOps에서 인기 있는 시나리오는 SSH를 SOCKS5 프록시로 사용하여 트래픽을 배스천 호스트를 통해 터널링하는 것입니다:
# 로컬 포트 1080에서 SOCKS5 SSH 터널 열기 ssh -D 1080 -f -C -q -N [email protected] # 이제 curl에서 이 터널을 사용합니다 curl -x socks5h://localhost:1080 https://internal-api.private.network/data # 또는 세션의 모든 요청에 대해 환경 변수를 통해 사용 export all_proxy="socks5h://localhost:1080" wget https://internal-resource.private.network/file.tar.gz
wget은 1.19 버전부터 SOCKS5를 지원합니다. 이전 버전(CentOS 7, Ubuntu 16.04)에서는 우회 솔루션인 proxychains, tsocks를 사용하거나 SOCKS5가 필요한 작업에 대해 curl로 전환해야 합니다.
6. 프록시 인증: 사용자 이름 및 비밀번호
대부분의 상업적 프록시 및 기업 프록시 서버는 인증을 요구합니다. 자격 증명을 전달하는 방법에는 여러 가지가 있으며 — 각 방법은 보안 측면에서 장단점이 있습니다.
방법 1: URL에 자격 증명 포함 — 간단하지만 안전하지 않습니다 (비밀번호가 프로세스 목록에 표시됨):
curl -x http://user:p%[email protected]:3128 https://api.example.com/data # 비밀번호의 특수 문자는 URL 인코딩해야 합니다: @ → %40, : → %3A
방법 2: curl의 --proxy-user 플래그 — 비밀번호를 명령줄에 입력할 필요가 없으며, curl이 대화식으로 요청합니다:
# 플래그에 로그인 및 비밀번호 (여전히 ps aux에서 보임) curl -x http://proxy.example.com:3128 \ --proxy-user "username:password" \ https://api.example.com/data # 로그인만 — curl이 비밀번호를 대화식으로 요청합니다 curl -x http://proxy.example.com:3128 \ --proxy-user "username" \ https://api.example.com/data
방법 3: .netrc 파일을 통한 — 스크립트에 가장 안전한 방법입니다:
# ~/.netrc machine proxy.example.com login username password secretpassword # 파일 접근 권한 제한 chmod 600 ~/.netrc # curl에서 사용 curl -x http://proxy.example.com:3128 --proxy-netrc https://api.example.com/data
방법 4: 비밀 저장소의 환경 변수를 통한 — CI/CD 및 프로덕션 환경에 권장됩니다:
# 스크립트에서 CI/CD가 설정한 변수에서 자격 증명을 읽습니다
#!/bin/bash
PROXY_URL="http://${PROXY_USER}:${PROXY_PASS}@proxy.example.com:3128"
curl -x "${PROXY_URL}" https://api.example.com/data
인증 방법 비교 표:
| 방법 | 편리함 | 보안성 | 적합한 경우 |
|---|---|---|---|
| URL (user:pass@host) | ⭐⭐⭐ | ⭐ | 테스트 |
| --proxy-user 플래그 | ⭐⭐⭐ | ⭐⭐ | 일회성 명령 |
| .netrc 파일 | ⭐⭐ | ⭐⭐⭐ | 로컬 스크립트 |
| 환경 변수에서 vault | ⭐⭐ | ⭐⭐⭐⭐⭐ | CI/CD, 프로덕션 |
7. 예외 및 no_proxy: 로컬 주소에 대한 프록시 우회 방법
기업 및 클라우드 환경에서는 종종 세밀한 설정이 필요합니다: 외부 트래픽은 프록시를 통해, 내부 트래픽은 직접 연결됩니다. no_proxy (또는 NO_PROXY) 변수를 사용하여 예외 목록을 설정할 수 있습니다.
# 기본 예외 export no_proxy="localhost,127.0.0.1,::1" # 도메인에 대한 예외 (점이 있는 경우 — 모든 하위 도메인) export no_proxy="localhost,127.0.0.1,.internal.company.com,.corp.local" # IP 범위에 대한 예외 (CIDR 표기법은 모든 곳에서 작동하지 않음!) export no_proxy="localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" # AWS의 경우: 메타데이터 엔드포인트 및 내부 주소 제외 export no_proxy="localhost,127.0.0.1,169.254.169.254,.amazonaws.com.internal"
⚠️ no_proxy의 중요한 특징:
CIDR 표기법 (10.0.0.0/8)은 모든 도구에서 지원되지 않습니다. curl은 7.86.0 버전부터 지원하며, wget은 전혀 지원하지 않습니다. 호환성을 위해 특정 IP 또는 10.와 같은 마스크를 나열하는 것이 좋습니다 (10.으로 시작하는 모든 것).
Kubernetes 환경에서 API 서버 및 내부 서비스를 제외해야 하는 실용적인 예:
export no_proxy="localhost,127.0.0.1,10.96.0.0/12,10.244.0.0/16,.cluster.local,.svc,.default"
export NO_PROXY="${no_proxy}"
8. CI/CD에서 프록시: GitHub Actions, GitLab CI, Docker
CI/CD 파이프라인에서 프록시를 설정하는 것은 기업 네트워크에서 작업하거나 인터넷 접근이 제한된 경우 DevOps 엔지니어가 가장 자주 수행하는 작업 중 하나입니다. 인기 있는 플랫폼에 대한 특정 구성을 살펴보겠습니다.
GitHub Actions
# .github/workflows/deploy.yml
name: 배포
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
env:
http_proxy: ${{ secrets.PROXY_URL }}
https_proxy: ${{ secrets.PROXY_URL }}
no_proxy: "localhost,127.0.0.1,.github.com"
steps:
- uses: actions/checkout@v3
- name: 종속성 다운로드
run: |
curl -x "${http_proxy}" https://external-api.example.com/config.json -o config.json
wget -e use_proxy=yes -e http_proxy="${http_proxy}" https://releases.example.com/app-v1.0.tar.gz
GitLab CI
# .gitlab-ci.yml
variables:
http_proxy: "http://proxy.company.com:3128"
https_proxy: "http://proxy.company.com:3128"
no_proxy: "localhost,127.0.0.1,.gitlab.company.com"
build:
stage: build
script:
- curl -x "${http_proxy}" https://registry.npmjs.org/package -o package.json
- wget -e use_proxy=yes -e http_proxy="${http_proxy}" https://example.com/resource.tar.gz
Docker: 이미지 빌드 시 프록시 사용
# 빌드 인수로 프록시 전달 docker build \ --build-arg http_proxy=http://proxy.example.com:3128 \ --build-arg https_proxy=http://proxy.example.com:3128 \ --build-arg no_proxy=localhost,127.0.0.1 \ -t myapp:latest . # Dockerfile에서 ARG를 사용하여 변수를 가져옵니다
# Dockerfile FROM ubuntu:22.04 ARG http_proxy ARG https_proxy ARG no_proxy ENV http_proxy=${http_proxy} ENV https_proxy=${https_proxy} ENV no_proxy=${no_proxy} RUN apt-get update && apt-get install -y curl wget RUN curl -x "${http_proxy}" https://example.com/setup.sh | bash # 최종 이미지에서 프록시 변수를 지웁니다 (선택 사항) ENV http_proxy="" ENV https_proxy=""
Docker daemon에 대한 전역 프록시 설정 (프록시를 통한 docker pull):
# /etc/systemd/system/docker.service.d/proxy.conf [Service] Environment="HTTP_PROXY=http://proxy.example.com:3128" Environment="HTTPS_PROXY=http://proxy.example.com:3128" Environment="NO_PROXY=localhost,127.0.0.1,.internal.registry.com" # docker 재시작 systemctl daemon-reload systemctl restart docker
9. bash 스크립트에서 프록시 회전
외부 API나 서비스에 많은 요청을 해야 하는 경우 — 프록시 회전은 부하를 분산시키고 IP 차단을 피하는 데 도움이 됩니다. 이는 가격 모니터링, 데이터 수집 또는 다양한 지역에서 리소스의 가용성을 테스트할 때 특히 중요합니다.
이러한 작업에 적합한 것은 데이터 센터 프록시입니다 — 대량 요청 시 높은 속도와 안정성을 제공합니다.
#!/bin/bash # rotate_proxy.sh — 목록에서 프록시 회전 PROXY_LIST=( "http://user:[email protected]:3128" "http://user:[email protected]:3128" "http://user:[email protected]:3128" "http://user:[email protected]:3128" ) URLS_FILE="urls.txt" OUTPUT_DIR="./results" mkdir -p "${OUTPUT_DIR}" PROXY_COUNT=${#PROXY_LIST[@]} INDEX=0 while IFS= read -r url; do PROXY="${PROXY_LIST[$INDEX]}" FILENAME=$(echo "${url}" | md5sum | cut -d' ' -f1) echo "요청 중: ${url} 프록시 $((INDEX + 1))/${PROXY_COUNT}를 통해" curl -x "${PROXY}" \ --max-time 30 \ --retry 3 \ --retry-delay 2 \ --silent \ --output "${OUTPUT_DIR}/${FILENAME}.html" \ "${url}" if [ $? -eq 0 ]; then echo " ✓ 성공" else echo " ✗ 실패, 다음 프록시 시도 중..." INDEX=$(( (INDEX + 1) % PROXY_COUNT )) curl -x "${PROXY_LIST[$INDEX]}" \ --max-time 30 \ --silent \ --output "${OUTPUT_DIR}/${FILENAME}.html" \ "${url}" fi # 다음 프록시로 이동 INDEX=$(( (INDEX + 1) % PROXY_COUNT )) # 요청 간 짧은 일시 정지 sleep 0.5 done < "${URLS_FILE}" echo "완료! 결과는 ${OUTPUT_DIR}에 저장되었습니다."
더 발전된 방법은 사용하기 전에 프록시의 작동 여부를 확인하는 것입니다:
#!/bin/bash # check_proxy.sh — 프록시의 가용성 확인 check_proxy() { local proxy_url="$1" local test_url="https://api.ipify.org" result=$(curl -x "${proxy_url}" \ --max-time 10 \ --silent \ --write-out "%{http_code}" \ --output /dev/null \ "${test_url}") if [ "${result}" -eq 200 ]; then echo "ALIVE" else echo "DEAD" fi } # 프록시를 통해 외부 IP 가져오기 get_proxy_ip() { local proxy_url="$1" curl -x "${proxy_url}" --max-time 10 --silent https://api.ipify.org } PROXY="http://user:[email protected]:3128" STATUS=$(check_proxy "${PROXY}") echo "프록시 상태: ${STATUS}" if [ "${STATUS}" == "ALIVE" ]; then EXTERNAL_IP=$(get_proxy_ip "${PROXY}") echo "프록시를 통한 외부 IP: ${EXTERNAL_IP}" fi
10. 문제 해결 및 일반적인 오류
프록시 작업은 초기 설정 시 오류가 발생하기 마련입니다. 가장 일반적인 문제와 진단 방법을 살펴보겠습니다.
상세 모드를 통한 진단
# curl의 최대한 상세한 출력 curl -vvv -x http://proxy.example.com:3128 https://api.example.com/data 2>&1 | head -50 # 출력에서 확인할 사항: # * Connected to proxy.example.com — 프록시와의 연결이 설정됨 # CONNECT api.example.com:443 — 터널 요청 # HTTP/1.1 200 Connection established — 터널이 열림 # * SSL connection using TLS — TLS가 작동 중 # wget 디버깅 wget -d -e use_proxy=yes -e http_proxy=http://proxy.example.com:3128 https://api.example.com/data
일반적인 오류 및 해결책
| 오류 | 원인 | 해결책 |
|---|---|---|
407 Proxy Authentication Required |
자격 증명이 전달되지 않음 | URL 프록시에서 user:pass 추가 또는 --proxy-user 플래그 사용 |
Connection refused |
잘못된 포트 또는 프록시가 사용 불가능함 | 포트 확인: nc -zv proxy.host 3128 |
SSL certificate error |
SSL 검사가 있는 기업 프록시 | 기업 CA 추가: --cacert /path/to/ca.crt |
Could not resolve proxy |
DNS가 프록시 이름을 해석하지 못함 | 이름 대신 IP 사용 또는 DNS 확인 |
Timeout |
프록시가 느리거나 과부하 상태임 | 타임아웃 증가: --max-time 60 |