단일 장치를 위한 리버스 SSH: 루프백 리스너인가, 라우팅된 VPN인가?
바깥으로만 연결할 수 있는 장치를 위한 관리 포트 하나. 리버스 터널로 충분한지, 실제 라우팅 경로가 필요한지를 세 가지 조건이 결정합니다.
장치는 통신사 NAT 뒤에 있고, 바깥으로 연결을 열 수 있으며, 아무도 닿을 수 없는 SSH 서버를 실행합니다. 들어오는 요청은 보통 "VPN을 붙여 달라"로 표현되는데, 그것은 여러 답 중 하나입니다. 받아들이기 전에 관리용 TCP 포트 하나가 필요한지, 아니면 서브넷 전체로 가는 라우팅 경로가 필요한지 정하십시오. 둘은 서로 다른 노출을 가진 다른 프로젝트입니다.
리버스 터널은 좁은 쪽 답을 잘 합니다. 장치가 허브로 접속해 허브에 루프백 포트에서 듣도록 요청하고, 그 포트를 자신의 SSH 서비스로 돌려보냅니다. 그다음 관리자는 공용 인터넷이 아니라 허브를 통해 리스너에 닿습니다.
선택을 결정하는 세 가지 조건
어떤 설정보다 먼저 이것을 적어 두십시오. 이는 제한된 관리 진입과 네트워크 확장 사이의 경계이며, 이것을 뒤섞는 것이 다섯 줄짜리 터널을 주인 없는 평평한 네트워크로 만드는 길입니다.
| Condition | Reverse tunnel fits | Something else is needed |
|---|---|---|
| How many services must be reachable? | One, or a small fixed set on loopback | Anything that needs many ports or dynamic discovery |
| Who connects, and how are they authenticated? | Named administrators with their own hub accounts | Third parties, or anything needing per-flow audit you do not control |
| Does the device's own LAN need to be reachable? | No | Yes, so a routed VPN or a managed platform is the honest answer |
세 번째 행이 사람들이 다투는 항목입니다. 장치의 SSH 포트로 가는 터널은 그 이웃을 당신의 네트워크에 올리지 않으며, 추가 전달의 연쇄로 그렇게 보이게 해서도 안 됩니다. 요구가 정말 LAN이라면 그렇게 말하고 대신 그것을 설계하십시오.
허브 쪽: 범위가 제한된 릴레이 계정
각 장치에 공개 키 인증을 쓰고 대화형 세션이 없는 자체 릴레이 계정을 주십시오. Match 블록이 그 계정으로 제한되어 있으므로 권한은 작게 유지되고, 개인 키는 있어야 할 장치에 남습니다.
Match User relay-dev-7
AuthenticationMethods publickey
PubkeyAuthentication yes
AllowTcpForwarding remote
PermitListen 127.0.0.1:21022
GatewayPorts no
MaxSessions 0
PermitTTY no
X11Forwarding no
AllowAgentForwarding no
MaxSessions 0 가 흥미로운 줄입니다. 셸과 하위 시스템 세션은 거부하면서 장치에 필요한 전달은 허용하므로, 도난당한 키를 대화형 로그인으로 바꿀 수 없습니다. PermitListen 는 허브 쪽 리스너 주소와 포트를 고정하고, GatewayPorts no 는 허브가 있는 네트워크에 여는 대신 루프백에 유지합니다.
다시 읽기 전에 실제 설정을 시험하고, 그동안 두 번째 세션을 열어 두십시오. Match 블록이 실수로 모든 사람에게 적용되거나 옵션 이름에 오타가 있는 것은, 강화 중인 상자에서 자신을 잠그는 고전적인 방법입니다. 실행 중인 설정을 덤프하고 방금 쓴 파일을 믿는 대신 옵션을 다시 읽으십시오. 옵션 지원은 빌드마다 다르므로, 새 서버에서 받아들여진 줄이 오래된 서버에서는 그냥 무시될 수 있습니다.
장치 쪽: 크게 실패하는 전달
장치가 연결을 열고 리스너를 요청합니다. 인수의 두 주소는 -R 서로 다른 뜻이고, 왼쪽에서 오른쪽으로 읽는 것이 어느 쪽이 어느 쪽인지 기억하는 가장 빠른 방법입니다.
ssh -NT \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-R 127.0.0.1:21022:127.0.0.1:22 \
[email protected]
왼쪽 쌍은 허브가 들어야 할 곳입니다. 오른쪽 쌍은 요청이 왔을 때 장치가 연결하는 대상이며, 장치 자신의 쪽에서 해석됩니다. 둘을 바꾸면 깔끔하게 올라오지만 엉뚱한 곳으로 전달하는 터널이 되어, 실패보다 나쁩니다.
ExitOnForwardFailure=yes 요청한 리스너를 세울 수 없을 때 명령이 종료되게 하므로, 재시작 루프가 반쯤 동작하는 세션을 남기는 대신 진실을 알려 줍니다. 로컬 SSH 서비스가 나중에 건강한지에 대해서는 아무 말도 하지 않으므로, 프로세스 관리자와 가끔의 실제 연결 시험과 짝지으십시오.
추가 홉을 피하려고 루프백 수신 주소를 와일드카드로 바꾸지 마십시오. 그 한 번의 변경이 관리 경로와 공개적으로 도달 가능한 포트의 차이이며, 보통 디버깅 중에 일어나 그대로 남습니다. 동료가 허브를 통해 포트에 닿지 못한다면 바인드를 넓히는 대신 점프 설정을 고치십시오. 이 설계의 보안 논거는 전부 루프백입니다.
허브를 통한 관리자 접근
허브에 일반 계정이 있는 관리자는 그것을 통해 전달된 포트로 점프할 수 있습니다. 릴레이 계정은 이 용도로 쓰지 않으며, 둘을 분리해 두는 요점이 바로 그것입니다.
ssh -J [email protected] -o HostKeyAlias=device-7 \
-p 21022 [email protected]
선택한 별칭 아래에서 장치의 호스트 키를 확인하십시오. 그 키가 사슬 끝의 장치를 식별하며 터널은 전송일 뿐입니다. 종단 간 로그인은 여전히 양쪽 끝을 인증합니다. 공유 허브에서 끝나는 터널이 그것만으로 신뢰할 수 있는 경로가 되지는 않습니다.
수명 주기를 소유하십시오
장치 명령을 정확히 하나의 활성 인스턴스와 의도적으로 고른 재시작 정책을 가진 프로세스 관리자 아래에서 실행하십시오. keepalive 옵션은 죽은 상대를 감지하지만, 종료된 프로세스를 되살리지는 않습니다.
그다음 폐기 절차를 쓰십시오. 각 장치는 자체 리스너와 자체 키를 가지므로, 폐기란 그 계정을 폐기하고 그 키를 제거하는 것이지 함대 전체가 공유하는 비밀을 돌리는 것이 아닙니다.
마지막으로 루프백이 무엇을 주는지 기억하십시오. 네트워크 노출을 제한하지만 공유 허브의 다른 로컬 사용자에 대해서는 아무것도 하지 않으므로, 허브 자체의 접근 정책은 여전히 올바라야 합니다. sshd_config 참조 위에서 사용한 각 옵션을 문서화하며, 그중 하나를 넓히기 전에 전달 섹션을 끝까지 읽을 가치가 있습니다.