컬(curl) 없는 컨테이너에서 HTTP 요청, 배시 '/dev/tcp'로 해결...추가 설치 없이 셸만으로 가능
컬 없는 컨테이너에서 배시만으로 HTTP 요청을 보내는 방법이 공유됐다.
[한국정보기술신문] 개발자 마레크 슈파(Marek Šuppa)가 6월 16일 자신의 블로그에 컬(curl) 같은 별도 도구 없이 배시(bash) 셸만으로 HTTP 요청을 보내는 방법을 공개했다. 그는 한 컨테이너가 내부 도커(Docker) 네트워크를 통해 다른 서비스에 닿는지 확인하려 했으나, 사용 중이던 이미지가 최소 구성으로 깎여 있어 컬도 wget도 없는 상황에 부딪혔다고 밝혔다. 소켓을 열 만한 다른 도구도 없었지만, 이미 들어 있는 배시 자체로 문제를 해결했다는 것이 글의 요지다.
여기서 컨테이너는 프로그램과 실행 환경을 한데 묶어 어디서나 동일하게 돌아가도록 만든 격리된 실행 단위를 말한다. 컬과 wget은 명령어 한 줄로 웹 주소에 접속해 응답을 받아오는 대표적인 도구다. 운영에 꼭 필요한 것만 담아 용량을 줄인 이른바 최소 구성 이미지에서는 이런 도구가 빠져 있는 경우가 많다.

셸 자체가 HTTP를 처리한다
슈파가 제시한 방법의 핵심은 배시의 '/dev/tcp' 기능이다. 그는 우선 'exec 3<>/dev/tcp/service/8642' 명령으로 대상 호스트와 포트에 연결을 열고, printf 명령으로 'GET /health' 요청문을 직접 써 보낸 뒤, cat 명령으로 응답을 읽어 들이는 세 줄짜리 예시를 소개했다. 여기서 'service'는 접속하려는 상대의 호스트 이름으로, 도커 네트워크에 설정된 컨테이너나 서비스 이름처럼 이름이 실제로 풀려 닿을 수 있는 주소여야 한다고 설명했다.
이 방식으로는 응답의 상태 줄과 헤더, 빈 줄, 본문이 모두 그대로 출력된다. 슈파는 인증 토큰처럼 헤더를 덧붙이고 싶을 때는 요청을 끝내는 빈 줄 앞에 'Authorization: Bearer' 줄을 하나 더 넣으면 된다며, 'GET /v1/models' 요청에 토큰을 함께 보내는 예시도 함께 제시했다.
"'/dev/tcp'는 실제 파일이 아니다"
슈파는 처음 이 기능을 접했을 때 가장 헷갈렸던 점으로 '/dev/tcp'가 실제로 디스크에 존재하는 장치 파일이 아니라는 사실을 꼽았다. 해당 경로를 목록으로 조회해도 아무것도 나오지 않고, 다른 셸에서 그 경로를 읽으려 하면 오류만 난다는 것이다. 이는 배시가 내부적으로 처리하는 일종의 방향 전환(redirection) 표기일 뿐이라고 그는 설명했다.
배시 설명서에 따르면 호스트가 유효한 이름이나 인터넷 주소이고 포트가 정수형 포트 번호 또는 서비스 이름이면, 배시는 해당하는 TCP 소켓을 열려고 시도한다. 슈파는 '/dev/tcp'와 '/dev/udp'라는 이름이 실제 유닉스 계열 시스템에는 존재하지 않아 기존 경로와 충돌할 일이 없어 선택된 것이라고 전했다. 이 과정에서 배시가 이름을 주소로 바꾸는 작업과 연결 수립을 대신 처리해 주고, 'exec 3<>' 구문이 그 소켓을 다른 파일처럼 읽고 쓸 수 있는 통로로 넘겨준다는 설명이다.
연결 종료 헤더 필수...TLS는 지원 안 해
슈파는 이 방법을 쓸 때 알아둬야 할 점도 함께 정리했다. 우선 요청에 'Connection: close' 헤더를 반드시 넣어야 한다고 강조했다. 이 헤더가 없으면 서버가 응답 후에도 연결을 계속 열어 두는 HTTP/1.1의 기본 동작 탓에, 응답을 읽는 명령이 오지 않는 데이터를 무한정 기다리게 된다는 것이다. 그는 서버에 연결을 닫도록 요청하면 이 문제가 해결되며, 명령 전체를 시간제한(timeout) 안에서 실행하면 어느 경우든 안전하다고 덧붙였다.
다음으로 이 방법은 암호화 통신(TLS)을 지원하지 않는다는 점을 짚었다. '/dev/tcp'가 여는 것은 암호화되지 않은 일반 소켓이어서 평문 HTTP에서만 작동하며, 보안이 적용된 https 주소에 접속하려면 별도 도구가 필요하고 그쯤이면 차라리 제대로 된 도구를 갖추는 편이 낫다는 설명이다.
또한 이 기능은 배시 고유의 것으로, 모든 셸이 공통으로 지키는 표준 규격(POSIX)에 속하지 않는다. 데비안 계열의 기본 셸인 대시(dash)나 지셰(zsh)에는 이 기능이 없어, 해당 셸을 쓰는 스크립트에서는 쓸 수 없고 배시를 직접 불러 써야 한다고 그는 밝혔다. 마지막으로 이 기능은 배시를 만들 때 특정 옵션을 켜야만 들어가는 컴파일 시점 선택 사항이라는 점도 덧붙였다. 대부분의 일반 배포판은 이 옵션을 켜 두지만, 데비안이 오랜 기간 꺼진 채로 배포했던 만큼 오래되거나 극도로 단출한 시스템에서는 사용 가능 여부를 먼저 확인하는 것이 좋다고 권했다.
"평소엔 컬이 정답, 설치 못 하는 컨테이너에서만"
슈파는 평소 작업에서는 여전히 컬이 적절한 도구라고 선을 그었다. 다만 아무것도 추가로 설치할 수 없는, 의도적으로 작게 만든 컨테이너 안에서라면 이 방법으로 패키지 하나 늘리지 않고도 빠른 점검을 끝낼 수 있다고 글을 맺었다.
한국정보기술신문 정보기술분과 강민규 기자 news@kitpa.org











