이름 기반 가상 호스트 SSH 리버스 프록시가 있습니까?


36

개발 환경에서 HTTP 리버스 프록시를 좋아했으며 DNS 기반 가상 호스트 리버스 프록시가 매우 유용하다는 것을 알았습니다. 방화벽에서 하나의 포트 (및 표준 포트) 만 열면 관리가 훨씬 쉬워집니다.

SSH 연결과 비슷한 것을 찾고 싶지만 많은 운이 없었습니다. SSH 터널링을 사용하지 않는 것이 좋습니다. 표준 이외의 포트 범위를 열어야하기 때문입니다. 이것을 할 수있는 것이 있습니까?

HAProxy가이를 수행 할 수 있습니까?


의도 한 응용 프로그램 파일 전송 또는 호스트에 대한 실제 SSH 액세스입니까?
Kyle Hodgson

호스트에 대한 직접 SSH 액세스가 목표입니다. 이러한 요구는 Mercurial 서버를 사내에서 운영하려는 욕구에서 비롯되었지만 우리 서버는 방화벽 뒤에 있습니다. 지금은 단순히 HTTP 버전을 설정하려고 노력하고 있지만 HTTP 대신 SSH를 사용하여 커밋을 원했습니다. 가능한 경우 다른 서버에 대한 직접 SSH 액세스는 보너스입니다.
ahanson

이것은 성가신 문제입니다.
편향

내 이해는 HAProxy가 이제 SNI를 통해이를 지원한다는 것입니다. 나는 아직 이것을 얻을 수 없었지만.
Carel

답변:


24

이름 기반 SSH가 프로토콜의 작동 방식에 따라 가능하다고 생각하지 않습니다.

몇 가지 대안이 있습니다.

  • 포트 22가 게이트웨이 역할을하도록 응답하는 호스트를 설정하면됩니다. 그런 다음 키를 기반으로 요청을 내부로 전달하도록 ssh 서버를 구성 할 수 있습니다. 키가있는 SSH 게이트웨이

  • 해당 호스트를 프록시로 사용하도록 클라이언트를 조정할 수 있습니다. 즉, 게이트웨이 호스트에 ssh 한 다음 해당 호스트를 사용하여 내부 호스트에 연결합니다. 클라이언트 구성의 SSH 프록시 .

  • 가장자리에 간단한 http 프록시를 설정할 수도 있습니다. 그런 다음이를 사용하여 들어오는 연결을 허용하십시오. HTTP 프록시 를 통한 SSH .

분명히 위의 모든 사항과 함께 게이트웨이를 올바르게 구성하고 잠그는 것이 매우 중요합니다.


17

지난 16 개월 동안이 문제에 대한 해결책을 찾고있었습니다. 그러나 내가 볼 때마다 관련 RFC에 지정되고 주요 구현으로 구현 된 SSH 프로토콜 로이 작업을 수행하는 것이 불가능한 것 같습니다.

그러나 약간 수정 된 SSH 클라이언트를 사용하고 의도 한대로 의도하지 않은 방식으로 프로토콜을 사용하려는 경우 달성 할 수 있습니다. 이에 대한 자세한 내용은 아래를 참조하십시오.

불가능한 이유

클라이언트는 SSH 프로토콜의 일부로 호스트 이름을 보내지 않습니다.

호스트는 DNS 조회의 일부로 호스트 이름을 보낼 수 있지만 캐시 될 수 있으며 클라이언트에서 리졸버를 통해 권한있는 서버로의 경로가 프록시를 통과하지 못할 수 있으며 특정 DNS 조회를 특정 SSH 클라이언트.

SSH 프로토콜 자체로 할 수있는 일은 없습니다. 클라이언트에서 SSH 버전 배너를 보지 않아도 서버를 선택해야합니다. 프록시로 무언가를 보내기 전에 클라이언트에게 배너를 보내야합니다. 서버의 배너는 다를 수 있으며 어떤 배너를 사용해야할지 추측 할 기회가 없습니다.

이 배너는 암호화되지 않은 상태로 보내지더라도 수정할 수 없습니다. 연결 설정 중에 해당 배너의 모든 비트가 확인되므로 연결이 약간 끊어 질 수 있습니다.

나에게 결론은 분명하다.이 연결을 작동시키기 위해서는 클라이언트 측에서 무언가를 변경해야한다.

대부분의 해결 방법은 SSH 트래픽을 다른 프로토콜로 캡슐화합니다. 클라이언트가 보낸 버전 배너가 호스트 이름을 포함하는 SSH 프로토콜 자체에 추가 된 것을 상상할 수도 있습니다. 배너의 일부가 현재 자유 형식 식별 필드로 지정되어 있기 때문에 기존 서버와의 호환성을 유지할 수 있으며 클라이언트는 일반적으로 서버에서 버전 배너를 기다리기 전에 자체적으로 배너를 보내지 만 프로토콜은 클라이언트가 배너를 보낼 수 있도록합니다. 먼저. ssh 클라이언트의 일부 최신 버전 (예 : Ubuntu 14.04의 버전)은 서버 배너를 기다리지 않고 배너를 보냅니다.

이 배너에 서버의 호스트 이름을 포함시키기위한 조치를 취한 클라이언트를 모르겠습니다. 그러한 기능을 추가 하기 위해 OpenSSH 메일 링리스트패치 를 보냈 습니다 . 그러나 SSH 트래픽을 스누핑하는 사람에게 호스트 이름을 공개하지 않으려는 의도로 거부되었습니다. 비밀 호스트 이름은 기본적으로 이름 기반 프록시의 작동과 호환되지 않으므로 언제든지 SSH 프로토콜에 대한 공식 SNI 확장을 기대하지 마십시오.

실제 솔루션

나에게 가장 효과적인 솔루션은 실제로 IPv6을 사용하는 것이 었습니다.

IPv6을 사용하면 각 서버에 별도의 IP 주소를 할당 할 수 있으므로 게이트웨이는 대상 IP 주소를 사용하여 패킷을 보낼 서버를 찾을 수 있습니다. SSH 클라이언트는 때때로 Teredo를 사용하여 IPv6 주소를 얻는 유일한 방법 인 네트워크에서 실행될 수 있습니다. Teredo는 신뢰할 수없는 것으로 알려져 있지만 연결의 기본 IPv6 끝이 공용 Teredo 릴레이를 사용하는 경우에만 가능합니다. 게이트웨이에 Teredo 릴레이를 배치하면 프록시를 실행할 수 있습니다. Miredo는 5 분 이내에 릴레이로 설치 및 구성 할 수 있습니다.

해결 방법

점프 호스트 / bastion 호스트를 사용할 수 있습니다. 이 방법은 개별 서버의 SSH 포트를 공용 인터넷에 직접 노출시키지 않으려는 경우에 사용됩니다. SSH에 필요한 외부 용 IP 주소 수를 줄이는 데 추가 이점이 있으므로이 시나리오에서 사용할 수 있습니다. 보안상의 이유로 다른 보호 계층을 추가하기위한 솔루션이라는 사실은 추가 보안이 필요하지 않을 때이를 사용하지 못하게하지 않습니다.

ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target

실제 솔루션 (IPv6)이 접근 할 수없는 경우 해킹하기가 더럽습니다.

내가 설명하려는 핵은 절대적으로 최후의 수단으로 만 사용해야합니다. 이 핵 사용에 대해 생각하기 전에 SSH를 통해 외부에 도달하려는 각 서버에 대해 IPv6 주소를 얻는 것이 좋습니다. SSH 서버에 액세스하기위한 기본 방법으로 IPv6을 사용하고 IPv6 배포에 영향을 미치지 않는 IPv4 전용 네트워크에서 SSH 클라이언트를 실행해야하는 경우에만이 핵을 사용하십시오.

아이디어는 클라이언트와 서버 간의 트래픽이 완벽하게 유효한 SSH 트래픽이어야한다는 것입니다. 그러나 프록시는 호스트 이름을 식별하기 위해 패킷 스트림에 대해 충분히 이해하면됩니다. SSH는 호스트 이름을 보내는 방법을 정의하지 않기 때문에 이러한 가능성을 제공하는 다른 프로토콜을 고려할 수 있습니다.

HTTP와 HTTPS는 모두 서버가 데이터를 보내기 전에 클라이언트가 호스트 이름을 보낼 수있게합니다. 이제 문제는 SSH 트래픽과 HTTP 및 HTTPS로 동시에 유효한 바이트 스트림을 구성 할 수 있는지 여부입니다. HTTPs 그것은 거의 초보자가 아니지만 HTTP가 가능합니다 (HTTP의 충분히 자유로운 정의를 위해).

SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$: 
Host: example.com

이것은 SSH 또는 HTTP처럼 보입니까? SSH이며 완벽하게 RFC를 준수합니다 (이진 문자 중 일부는 SF 렌더링으로 약간 엉망이 된 것 제외).

SSH 버전 문자열에는 주석 필드가 포함되며, 위의 값은 / HTTP/1.1입니다. 개행 후 SSH에는 일부 바이너리 패킷 데이터가 있습니다. 첫 번째 패킷은 MSG_SSH_IGNORE클라이언트가 보낸 메시지이며 서버는 무시합니다. 무시할 페이로드는 다음과 같습니다.

: 
Host: example.com

HTTP 프록시가 받아들이는 것에 대해 충분히 자유롭다면, 동일한 바이트 시퀀스가 ​​HTTP 메소드라고 부르고 SSH-2.0-OpenSSH_6.6.1무시 메시지 시작시의 이진 데이터는 HTTP 헤더 이름으로 해석됩니다.

프록시는 HTTP 메소드 나 첫 번째 헤더를 모두 이해하지 못합니다. 그러나 Host백엔드를 찾기 위해 필요한 모든 헤더를 이해할 수 있습니다.

이것이 작동하려면 프록시는 백엔드를 찾기에 충분한 HTTP 만 이해하면된다는 원칙에 따라 설계되어야하고 일단 백엔드가 발견되면 프록시는 단순히 원시 바이트 스트림을 전달하고 실제 종료 상태를 유지합니다. 백엔드가 수행 할 HTTP 연결

HTTP 프록시에 대해 너무 많은 가정을하기에는 약간의 스트레칭처럼 들릴 수 있습니다. 그러나 SSH를 지원할 목적으로 개발 된 새로운 소프트웨어를 기꺼이 설치하려는 경우 HTTP 프록시에 대한 요구 사항이 나쁘게 들리지 않습니다.

내 경우에는이 방법이 코드, 구성 등을 변경하지 않고 이미 설치된 프록시에서 작동하는 것으로 나타났습니다. 그리고 이것은 SSH 만 염두에두고 HTTP만으로 작성된 프록시를위한 것입니다.

개념 증명 클라이언트프록시 . (프록시는 저에 의해 운영되는 서비스라는 면책 사항입니다. 다른 프록시가이 사용법을 지원한다고 확인되면 링크를 자유롭게 교체하십시오.)

이 핵의주의 사항

  • 사용하지 마십시오. IPv6 인 실제 솔루션을 사용하는 것이 좋습니다.
  • 프록시가 HTTP 트래픽을 이해하려고 시도하면 반드시 중단됩니다.
  • 수정 된 SSH 클라이언트에 의존하는 것은 좋지 않습니다.

the gateway can use the destination IP address to find out which server to send the packet toIPv6 솔루션에서의 의미를 자세히 설명 할 수 있습니까 ?
Jacob Ford

@JacobFord 문장을 이해하는 열쇠는 프록시가 아닌 게이트웨이 라는 단어를 사용한다는 것 입니다. IPv6을 사용하면 모든 호스트에 하나씩 IP 주소를 할당 할 수있는 충분한 IP 주소를 확보 할 수 있습니다. 프록시 뒤에 놓인 모든 컴퓨터에는 자체 IP 주소가 있으며 이름을 확인할 수 있습니다. 따라서 더 이상 프록시가 필요하지 않으며 클라이언트는 트래픽을 원하는 호스트의 IP 주소로 보내고 트래픽을 직접 라우팅 할 수 있습니다.
kasperd

해결 방법 부분에는 ssh -J에 대한 비교적 새로운 명령 스위치가 있으므로 다음을 사용할 수 있습니다. ssh -J user1 @ front user2 @ target
PMN

3

나는 이것이 틀렸다는 것을 증명 하기는하지만 적어도 당신이 묘사 한 방식으로 가능할 것이라고 믿지 않습니다. 클라이언트가 연결하고자하는 호스트 이름을 (최소한 명확하게) 보내는 것으로 보이지 않습니다. SSH 연결의 첫 단계는 암호화를 설정하는 것 같습니다.

또한 호스트 키 확인에 문제가 있습니다. SSH 클라이언트는 호스트 이름뿐만 아니라 IP 주소를 기반으로 키를 확인합니다. 다른 키를 가진 여러 호스트 이름이 있지만 연결하는 동일한 IP가 있습니다.

가능한 해결책은 'bastion'호스트를 사용하는 것인데, 여기서 클라이언트는 해당 시스템에 ssh in하고 정상적인 (또는 원하는 경우 제한됨) 쉘을 가져온 다음 내부 호스트로 ssh 할 수 있습니다.


요새 개념은 현재 설정되어 있지만 버전 관리에 문제가 있습니다 (추가 노력없이).
ahanson

ssh가 fqdn을 보내지 않고 클라이언트 측에서 DNS를 처리한다는 것은 매우 성가신 일입니다. 대부분의 TCP / IP 응용 프로그램 수준 프로토콜을 만들 때 사람들이 공개 IP 팽창 및 NAT에 대해 걱정하지 않았다고 생각합니다. 심각하기 때문에, 당신은 해야 iptables에 (즉, 커널 필터)와 FQDN을 기반 NAT를 할 수 있습니다.
편견

호스트 키는 리버스 프록시의 키가 될 수 있습니다. 내부 네트워크와 호스트를 제어 할 수 있기 때문에 백엔드의 보안을 신뢰할 경우 이점이 있습니다.
rox0r

@bias 상위 수준의 프로토콜이 호스트 이름을 보내더라도 호스트 이름을 기반으로 NAT를 수행 할 수 없습니다. 수행 할 수없는 이유는 SYN 패킷을 수신 할 때 백엔드 선택을 수행해야하지만 호스트 이름은 SYN이 처리되고 SYN-ACK가 리턴 된 후에 만 ​​전송되기 때문입니다. 그러나 http 및 https와 같은 호스트 이름을 전송하는 프로토콜에는 매우 얇은 애플리케이션 계층 프록시를 사용할 수 있습니다.
kasperd

3

블록에 새로운 아이가 있습니다. SSH 파이퍼는 내부 호스트에 맵핑되어야하는 사전 정의 된 사용자 이름을 기반으로 연결을 라우팅합니다. 리버스 프록시와 관련하여 최상의 솔루션입니다.

github의 SSh 파이퍼

첫 번째 테스트에서 SSH와 SFTP는 모두 작동합니다.


그것은 사실상 MITM 공격입니다. MITM 공격으로부터 보호되는 모든 설정에서 중단 될 것으로 예상됩니다.
kasperd

1
실제로 호스팅 당사자는 호스트와 중간에있는 사람이므로 둘 만 관련됩니다.
Király István

@ KirályIstván 이것은 멋진 프로젝트이며 다른 답변이 100 % 정확하지 않다는 증거입니다. github.com/gliderlabs/sshfront 및 일부 bash / python 을 기반으로 유사한 도구를 만들었습니다 (소스를 열 수는 없지만 흥미 롭지 는 않습니다 -bash 는 ssh 클라이언트를 실행하고 python은 인증 처리기로 사용됩니다). 그러나 현재 솔루션에 한 가지 문제가 있습니다. sftp (scp는 작동)를 지원하지 않습니다. 서브 시스템을 실행하려고하면 정지 debug1: Sending subsystem: sftp됩니다. shirt-front는 하위 시스템을 지원하지 않습니다. SSh 파이퍼에서 밑단을지지합니까?
Maciek Sawicki

1
@MaciekSawicki 저는 저자가 아닙니다. 난 그냥 내 프로젝트에서 사용합니다. .)
Király István

2

프록시 모드가있는 Honeytrap (낮은 상호 작용 허니팟)이 그것을 달성하기 위해 수정할 수 있는지 궁금합니다.

이 허니팟은 모든 프로토콜을 다른 컴퓨터로 전달할 수 있습니다. 이름 기반 vhost 시스템을 추가하면 (Apache로 구현 됨) 모든 프로토콜에 대해 완벽한 역방향 프록시가 될 수 있습니다.

나는 그것을 달성 할 수있는 기술이 없지만 어쩌면 훌륭한 프로젝트 일 수 있습니다.


훌륭한 아이디어!
편견

1
Apache의 vhost 기능은 서버에서 데이터를 보내기 전에 호스트 이름을 보내는 클라이언트에 의존합니다. SSH 프로토콜에는 그러한 호스트 이름이 포함되어 있지 않으므로 이러한 기능을 구현하기 시작하는 방법을 알지 못합니다. 그러나 아이디어를 구체화 할 수 있다면 개념 증명을 구현하거나 왜 작동하지 않는지 알려 드리겠습니다.
kasperd

1

방화벽 뒤에서 액세스하려는 포트 / 호스트 수가 증가함에 따라 VPN의 편리 성이 증가합니다.

그래도 VPN이 마음에 들지 않습니다.


1

ssh의 작동 방식으로 인해 불가능하다고 생각합니다. https와 마찬가지로 ssh의 모든 항목이 암호화되어 있기 때문에 게이트웨이는 어디에 연결 하려는지 알지 못하므로 호스트마다 다른 (외부) IP를 가져야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.