HAProxy 302로 프라이빗 앱에 접속: 두 번째 홉을 누가 여는가?
리다이렉트는 두 번째 연결을 브라우저에 돌려줍니다. 대신 요청을 백엔드로 보내고, 그다음 앱 자체의 절대 리다이렉트와 쿠키를 고치십시오.
누군가 공인 HAProxy 호스트를 사내 애플리케이션으로 향하게 하고 왜 브라우저가 따라가지 못하는지 묻는다. 프록시는 VPN을 통해 앱에 닿고, 리다이렉트는 Location 헤더를 돌려주며, 브라우저는 터널 안에만 존재하는 주소를 시도한다. 밖에서는 페이지가 그저 멈춘다. 어느 쪽이 두 번째 연결을 여는지가 결정 사항이며, 그것이 리다이렉트와 백엔드를 가른다.
설정에서 비슷해 보이는 두 경로
리다이렉트는 응답으로 교환을 끝낸다. 클라이언트는 Location을 읽고 자기가 있는 곳에서, 즉 당신이 한 번도 통제하지 않은 네트워크에서 새 요청을 시작한다. 프록시는 더 이상 관여하지 않는다. 이후의 모든 것은 클라이언트 자신의 DNS, 경로, 신뢰에 달려 있으므로 결과는 사용자가 브라우저를 연 위치에 따라 달라진다:
GET /app client -> proxy
302 Location proxy -> client (http://10.40.0.20:8080/)
GET /app client -> 10.40.0.20:8080 client network, no route
프록시 백엔드는 두 번째 구간을 서버 쪽에 둔다. 프록시는 이미 가진 경로로 그것을 열고, 클라이언트는 하나의 공개 이름과 계속 대화한다. 터널이 끊기면 클라이언트는 따라갈 수 없는 URL 대신 오류를 받는다. 이 차이가 장애를 진단 가능하게 만드는 것은 오류가 로그가 있는 곳에 나타나기 때문이다:
GET /app client -> proxy
GET /app proxy -> 10.40.0.20:8080 over the VPN
response proxy -> client
HAProxy의 리다이렉트 문서는 바로 이 선을 긋는다. 클라이언트에 리다이렉트로 응답하는 것은 요청을 백엔드로 전달하는 것과 다른 행동이다. 두 지시문은 설정 파일에서 몇 줄 떨어져 있고 전혀 다르게 동작하며, 하나는 요청을 끝내고 다른 하나는 프록시가 소유하는 두 번째 요청을 시작한다.
브라우저가 필요로 하지만 서버의 VPN이 주지 않는 것
브라우저 자체의 요청에는 없을 수도 있는 네 가지가 필요하다: 프라이빗 이름에 대한 DNS 응답, 그 주소로 가는 경로, 대기 중인 서비스에 닿을 권한, 그리고 그 서비스가 제시하는 인증서에 대한 신뢰다. 프록시 호스트는 터널을 통해 네 가지를 모두 갖는다. 그중 어느 것도 그 터널에 한 번도 있지 않았던 클라이언트로 넘어가지 않는다, 터널은 호스트의 속성이지 URL의 속성이 아니기 때문이다.
이 차이가 거의-작동함 증상을 설명한다. 첫 요청은 성공하고 주소 표시줄이 바뀌며, 그다음 연결이 멈추거나 실패하는 동안 프록시는 내내 건강하다. 프록시 설정에는 아무것도 깨진 것이 없고, 조정해도 10.40.0.20으로 가는 경로가 없는 클라이언트를 돕지 못한다. 클라이언트가 경로를 얻거나, 프록시가 대신 연결해야 한다.
프록시만 앱에 닿을 수 있을 때
리다이렉트 동작을 없애고 그 요청들을 프라이빗 주소를 가리키는 백엔드로 보낸다. 프런트엔드는 공개 이름을 유지하고, 백엔드가 요청이 실제로 어디로 가는지 결정한다. 클라이언트의 경로는 프록시가 목적지를 설명하는 것을 멈추고 스스로 목적지가 되기 때문에 아무것도 바뀌지 않는다. 내부 포트 8080에서 의도적으로 대기하는 앱이라면 서버 블록은 짧다:
backend private_app
mode http
option httpchk GET /health
server app1 10.40.0.20:8080 check
그 블록을, 10.40.0.20이 프록시 호스트 자체에서(여기서는 기본 경로가 아니라 VPN 인터페이스를 통해) 도달 가능해야 한다는 라우팅 테이블에 대한 진술로 읽어라. check 키워드는 그 경로가 하는 일만 보고하고, 상태 경로가 앱에 없으면 모든 프로브가 서버를 다운으로 표시한다. 통과한 검사는 클라이언트에 대해 아무것도 증명하지 않고 누가 백엔드를 쓸 수 있는지 정하는 정책을 대체하지 않는다.
이 조각은 완전한 프런트엔드가 아니다. 리스너, 인증, 허용된 호스트와 경로 선택, 그리고 앱에 누가 닿을 수 있는지 정하는 규칙을 유지하라. 백엔드 프로토콜도 맞춰라, HTTPS 백엔드는 인증서 검증과 서버 ID 설정이 필요하고 그 검사를 건너뛰면 프라이빗 연결이 검증되지 않은 연결이 되기 때문이다.
승리를 선언하기 전에 양방향 반환 경로를 확인하라. 앱은 이제 프록시에서 오는 연결을 보므로, 따로 마련하지 않는 한 그 로그와 클라이언트 주소 메타데이터는 프록시를 설명한다. 아이덴티티와 전달된 주소를 어떻게 다룰지 결정하고, 가정하지 말고 그 결정을 시험하라.
앱 자체의 리다이렉트는 여전히 안쪽을 가리킨다
첫 요청을 전달하는 것은 통합의 절반일 뿐이다. 앱은 절대 리다이렉트, 하드코딩된 자산 URL, 또는 내부에서 아는 이름으로 범위가 제한된 쿠키를 내보낼 수 있고, 그 각각이 브라우저를 닿을 수 없는 주소로 되돌린다. 내부 이름을 가리키는 Location 헤더는 원래 문제와 똑같이 보이며, 그래서 프록시 장애로 잘못 진단된다.
앱의 문서화된 외부 URL과 그 프록시 옵션을 설정하고, Host 헤더를 가상 호스트와 인증서 기대에 맞춰라. 내부 이름으로 제한된 쿠키는 공개 이름으로 돌아오지 않고, 뒤따르는 로그인 루프는 인증 버그로 읽힌다. 먼저 외부 URL을 바꾸고, 깨끗한 브라우저 프로필에서 다시 시험하고, 절반만 이전된 설정이 성공하는 로그인과 즉시 죽는 세션을 낳을 것이라 예상하라.
전달된 메타데이터는 지정된 프록시에서만 신뢰하라. 어떤 클라이언트든 설정할 수 있는 헤더는 절대 권한 값이 되어서는 안 되고, 그렇지 않으면 프록시가 호출자가 다른 사람의 신원을 주장하는 수단이 된다. 그 규칙은 리다이렉트 문제와 무관하며 이후의 어떤 재설계에서도 살아남는다.
실제로 어떤 설계인지 결정하라
모든 사용자가 이미 승인된 VPN 접근을 가지고 있다면 프라이빗 URL로의 리다이렉트는 의도된 설계일 수 있다. 그 요구 사항은 두 설계가 전혀 다른 집단에 봉사하므로 가정하지 말고 명시적으로 확인하라. 하나는 호텔 네트워크에서 전혀 작동하지 않고, 다른 하나는 공개 URL을 불러올 수 있는 누구에게나 앱을 노출한다. 그 구분은 설계 노트에 속한다.
사용자 네트워크의 기계에서 검사를 실행하고 첫 응답만이 아니라 브라우저가 하는 모든 홉을 따라가라. 주소 표시줄과 이후의 모든 요청을 지켜보라, 자산 태그의 절대 URL 하나면 첫 홉이 통과한 흐름이 깨지기에 충분하다. 로그인은 공개 이름으로 작동해야 하고, 거부되어야 할 자원은 거부된 채로 남아야 한다.
리버스 프록시를 게시하는 것은 별도의 접근 결정이며, 프록시 자체 VPN을 공개 URL을 불러올 수 있는 누구에게나 투명하게 확장하는 것이 아니다. 도표에서는 둘이 비슷해 보이고, 누구를 들여보내는지에서 다르다. 그 질문을 배포 후에 결정하는 것이 사내 애플리케이션이 우연히 공개 애플리케이션이 되는 길이다.
둘 중 무엇을 지었는지, 왜 그랬는지 적어 두라. 설정을 만지는 다음 사람은 다른 쪽을 전제할 것이다. 편의를 위해 더한 리다이렉트 하나면 원래 장애를 재현하기에 충분하고, 증상은 방금 고친 것과 똑같이 보일 것이다.