포트 노킹
fwknop과 호환되는 단일 패킷 인증(SPA, Single Packet Authorization)입니다. 보호 대상 포트는 평소에 외부에서 연결할 수 없고, 클라이언트가 암호화되고 HMAC으로 인증되며 재전송이 방지되는 노크 패킷을 먼저 보냅니다. Gately가 인증하면 그 출발지 주소에만 정해진 시간 동안 접근을 허용합니다. SSH 직접 연결을 보호하는 데 자주 쓰며, 웹 터미널로는 할 수 없는 작업(scp, IDE 원격 개발, 포트 포워딩)도 외부에서 안전하게 연결할 수 있고 스캐너와 무차별 대입 공격에는 포트가 보이지 않습니다.
기능
- fwknop 클라이언트 호환: 공식 fwknop 클라이언트(Linux, macOS, WSL)를 그대로 사용합니다. 키 형식은
fwknop -k로 생성한KEY_BASE64,HMAC_KEY_BASE64와 같으며, 규칙 페이지에서 클라이언트 명령과~/.fwknoprc구성 블록을 한 번에 만들 수 있습니다. - 보안: 패킷은 AES-256-CBC로 암호화되고, HMAC(SHA-256 / 384 / 512)을 먼저 검증한 뒤 복호화하므로 키가 맞지 않는 패킷은 복호화 단계에 들어가지 않습니다. 패킷은 한 번만 사용할 수 있고(재전송 방지), 타임스탬프가 유효 기간(기본 120초)을 벗어나면 거부합니다. 사용자 이름을 제한할 수 있고, 패킷에 허용할 주소를 명시하도록 요구할 수도 있습니다.
- 방화벽 차단: 보호 대상 포트를 시스템 방화벽(nftables, 커널에 nf_tables가 없으면 iptables + ipset)에 기록하고 노크에 성공한 주소만 허용합니다. 이 호스트의 sshd처럼 Gately를 거치지 않는 서비스도 보호합니다.
- Gately 진입점의 노크 허용: 포트 포워딩, 프로토콜 라우팅, STUN 내장 전달의 규칙과 프로토콜 라우팅의 개별 라우트에서 「노크 필요」를 선택할 수 있습니다. Gately가 직접 판단하므로 방화벽 권한이 필요 없으며 Windows, macOS,
NET_ADMIN이 없는 컨테이너에서도 사용할 수 있습니다. - 포트 하나만 개방: 프로토콜 라우팅이 TCP로 온 노크 패킷(
fwknop -P tcp)을 식별할 수 있어, 같은 포트에서 노크를 받고 노크 결과에 따라 SSH 등의 라우트를 허용합니다. - 침입 방지 연동: 거부된 노크는 「포트 노킹 거부」 이벤트를 발행하며, 침입 방지의 내장 「포트 노킹 실패」 템플릿으로 횟수에 따라 출처를 차단할 수 있습니다. 이미 차단된 주소가 보낸 노크는 바로 버립니다.
- 현재 권한 목록(남은 시간, 취소 가능)을 제공하고, 노크 성공과 거부는 모두 실행 로그에 기록되며 알림 센터에서 구독할 수 있습니다.
빠른 시작: 이 호스트의 SSH 보호
- 「포트 노킹」을 열고 규칙을 추가합니다. 포트에
tcp/22를 입력하고 「방화벽 차단」을 켭니다. 키를 비워 두면 자동으로 생성합니다. - 규칙 카드에서 「클라이언트」를 누르고 서버의 도메인이나 공인 IP를 입력한 뒤 명령줄 또는
~/.fwknoprc구성 블록을 복사합니다. - 클라이언트에 fwknop을 설치합니다. macOS는
brew install fwknop, Debian / Ubuntu는apt install fwknop-client, Windows는 WSL에 설치합니다. - 노크한 뒤 30초 안에 SSH로 연결합니다.
fwknop -n gately-ssh && ssh user@example.com노크 패킷은 기본적으로 UDP 62201 포트로 보냅니다. Gately가 라우터 뒤에 있으면 UDP 62201과 보호 대상 포트를 Gately가 있는 호스트로 함께 포워딩해야 합니다. 「방화벽 자동 허용」을 켜면 로컬 방화벽(OpenWrt fw4, firewalld, ufw 등)이 노크 포트를 자동으로 허용하고, 차단이 적용된 뒤에는 보호 대상 포트도 허용하므로 연결 가능 여부는 노크로만 결정됩니다.
자신이 접속하지 못하는 상황 방지
- 「내부망 노크 면제」가 기본으로 켜져 있습니다. 사설 주소, 링크 로컬 주소, 이 호스트의 네트워크 인터페이스가 속한 공인 IPv6 대역(인터페이스 프리픽스가 /64 이하이면 그 프리픽스, 클라우드 서버에서 흔한 /124, /128이면 그 프리픽스 자체만)은 노크가 필요 없으며, 설정에서 노크 면제 허용 목록을 추가할 수 있습니다. 통신사급 NAT 주소 100.64.0.0/10은 포함되지 않습니다. 같은 통신사의 다른 사용자도 이 대역을 쓰기 때문입니다. Tailscale 사용자는 100.64.0.0/10을 허용 목록에 추가하세요.
- 방화벽 차단을 켜도 이미 맺어진 연결에는 영향이 없고, 권한이 만료된 뒤에도 이미 맺어진 연결은 그대로 유지됩니다.
- Gately가 정상 종료되면 차단 규칙을 삭제합니다. 강제 종료(
kill -9,docker rm -f)된 경우 규칙은 Gately를 다시 시작할 때까지 남습니다. 내부망이나 다른 경로로 이 기기에 로그인할 수 있는지 먼저 확인하세요.
규칙
| 필드 | 설명 |
|---|---|
| 포트 | 클라이언트가 허용을 요청할 수 있는 포트입니다(예: tcp/22, udp/51820, 최대 16개). 클라이언트 -A 인자의 포트는 모두 이 목록에 있어야 하며, 그렇지 않으면 노크 전체가 거부됩니다 |
| 방화벽 차단 | 켜면 시스템 방화벽에서 이 포트를 차단하고 노크에 성공한 주소만 허용합니다. 끄면 방화벽을 변경하지 않으며 규칙은 「노크 필요」 진입점이 참조하는 용도로만 쓰입니다 |
| 암호화 키 / HMAC 키 | base64 인코딩이며 비우면 자동 생성합니다(32 / 64바이트, fwknop -k와 같음). 기존 fwknop 서버의 키를 입력할 수도 있습니다. 규칙을 복사하면 새 키가 생성되고, 규칙을 수정, 비활성화 또는 삭제하면 그 규칙이 발급한 모든 권한이 취소됩니다 |
| HMAC 알고리즘 | SHA-256(fwknop 기본값), SHA-384, SHA-512이며 클라이언트의 --hmac-digest-type과 일치해야 합니다 |
| 접근 허용 시간 | 노크에 성공한 뒤 새 연결을 맺을 수 있는 시간입니다. 기본값은 30초입니다 |
| 클라이언트 최대 시간 | 클라이언트가 -f로 더 긴 시간을 요청할 때의 상한입니다(기본 300초). 0이면 클라이언트가 요청한 시간을 무시합니다. 요청이 접근 허용 시간보다 짧으면 항상 요청값을 사용합니다 |
| 사용자 이름 제한 | 패킷의 사용자 이름이 이 값인 노크만 받습니다(fwknop은 기본적으로 로컬 로그인 이름을 보내며 -U로 지정할 수 있음) |
| 주소 명시 요구 | 주소가 없는 패킷(fwknop -s)을 거부하며, 클라이언트는 -R 또는 -a로 허용할 주소를 명시해야 합니다 |
포트 포워딩, 프로토콜 라우팅 또는 STUN이 참조하는 규칙은 삭제할 수 없으며 카드에 참조하는 쪽이 표시됩니다.
어느 주소를 허용하는가
-R(권장): 클라이언트가 먼저 자신의 공인 IPv4를 조회해 패킷에 넣습니다. 패킷이 전송 중에 가로채여도 다른 주소에는 쓸 수 없습니다.-s: 패킷에 주소가 없으며 패킷의 출발지 주소를 허용합니다. 패킷을 가로챈 쪽이 자신의 주소에서 먼저 재전송할 수 있으므로, 규칙에서 「주소 명시 요구」를 켜 이런 패킷을 거부할 수 있습니다.-a 주소: 지정한 주소를 허용합니다. 예를 들어 다른 기기를 대신해 노크할 때 씁니다.
fwknop 2.6 클라이언트는 IPv4로만 노크 패킷을 보내고 IPv4 주소만 허용할 수 있으므로 서버에 IPv4로 접근할 수 있어야 합니다. Gately 자체는 IPv6로 도착한 노크 패킷도 받아 그 출발지 주소를 허용합니다.
전역 설정
- UDP 노크 포트: 기본값은 62201입니다(fwknop 클라이언트의 기본 포트). 0을 입력하면 UDP를 수신하지 않고 프로토콜 라우팅을 거쳐 온 노크만 받습니다. 활성화된 규칙이 없으면 수신하지 않습니다.
- 방화벽 자동 허용: 위 내용을 참고하세요.
- 내부망 노크 면제 / 노크 면제 허용 목록: 방화벽 차단에만 영향을 줍니다. 「노크 필요」 진입점은 각자의 IP 목록으로 제어합니다.
- 패킷 유효 기간: 패킷 타임스탬프와 이 호스트 시간의 최대 차이이며 기본값은 120초입니다. 클라이언트와 서버의 시계가 모두 정확해야 합니다.
방화벽 차단
| 환경 | 방화벽 차단 | Gately 진입점의 노크 허용 |
|---|---|---|
Linux(root 또는 CAP_NET_ADMIN, nftables 있음) | 사용 가능 | 사용 가능 |
Docker(host 네트워크 + cap_add: NET_ADMIN) | 사용 가능, 규칙이 호스트에 기록됨 | 사용 가능 |
| Synology DSM 등 nf_tables가 없는 Linux | iptables(legacy) + ipset 사용, 공식 Docker 이미지에 포함됨 | 사용 가능 |
| Windows, macOS, FreeBSD, 권한이 없는 Linux | 사용 불가, 페이지 상단에 이유 표시 | 사용 가능 |
Gately는 nftables에 독립된 inet gately_knock 테이블을 만들고 시스템에 이미 있는 규칙은 변경하지 않습니다. prerouting 체인(우선순위 -160, 연결 추적 이후 목적지 주소 변환 이전)이 보호 대상 포트로 가는 패킷을 gate 체인에 넘기고, 순서대로 목적지가 이 호스트가 아닌 트래픽(이 호스트를 거쳐 다른 기기로 전달되는 연결은 영향 없음), 루프백 인터페이스, 이미 맺어진 연결, TCP에서 연결을 시작하는 패킷이 아닌 것, 노크 면제 서브넷과 노크 허용 집합에 있는 주소 + 프로토콜 + 포트를 허용하고 나머지는 버립니다. prerouting에서 판단하므로 Docker 컨테이너로 매핑된 포트는 매핑 전 포트 기준으로 차단합니다. 허용은 타임아웃이 있는 집합 요소로 기록되며 만료되면 커널이 자동으로 삭제합니다.
nf_tables가 없으면 iptables의 mangle 테이블을 사용합니다. PREROUTING에서 GATELY_KNOCK으로 점프하고, 보호 대상 포트마다 ipset 집합(gkn4_tcp_22 등, 타임아웃 포함) 하나를 둡니다. 방화벽 리로드나 nft flush ruleset 등으로 테이블이나 점프가 지워지면 10초 안에 자동으로 다시 쓰고 아직 유효한 허용도 함께 복원합니다. 페이지의 「방화벽 규칙 보기」에서 현재 기록된 규칙을 확인할 수 있습니다.
포트 포워딩으로 들어오는 SSH
Gately 포트 포워딩으로 이 호스트의 127.0.0.1:22에 전달되는 연결은 루프백 인터페이스에서 오므로 방화벽 차단의 영향을 받지 않습니다. 이런 경우 포트 포워딩 규칙에서 「노크 필요」를 선택하거나, 방화벽 차단으로 외부에 열린 포워딩 포트를 보호하세요.
주의
- Docker 포트 매핑이 출처 주소를 바꿈: Gately를 브리지 네트워크로 실행하고 노크 포트를
-p 62201:62201/udp로 매핑했을 때 Docker의 userland-proxy가 그 매핑을 처리하면(IPv6나 루프백 주소로 접근하거나 iptables 연동을 끈 경우 등) Gately에는 패킷 출처가 Docker 게이트웨이 주소로 보입니다.-s패킷은 게이트웨이 주소를 허용하게 되어 실제 클라이언트는 허용되지 않고, 같은 게이트웨이를 거쳐 전달되는 모든 연결을 허용할 수도 있습니다. Gately를 host 네트워크로 실행하거나(방화벽 차단에도 필요) 클라이언트에서-R/-a로 허용할 주소를 명시하고 「주소 명시 요구」를 켜세요. - SYN만 차단하는 설계의 절충: 활성화 전에 맺어졌지만 아직 연결 추적에 기록되지 않은 연결을 끊지 않도록, TCP는 연결을 시작하는 SYN 패킷만 버리고 SYN이 없는 패킷은 그대로 통과시킵니다. 이론적으로 SYN cookie(
net.ipv4.tcp_syncookies)가 켜진 시스템은 SYN 큐가 넘칠 때 유효한 cookie를 맞힌 단독 ACK를 받아들여 연결을 맺습니다. cookie는 커널 비밀 키와 시간으로 계산한 32비트 초기 시퀀스 번호라 맞히는 것은 사실상 불가능합니다. 보안 요구가 매우 높다면 SYN cookie를 끄세요(sysctl net.ipv4.tcp_syncookies=0).
Gately 진입점의 노크 허용
포트 포워딩, 프로토콜 라우팅, STUN(내장 전달)의 「IP 접근 제어」에서 노크 규칙 하나를 「노크 필요」로 선택할 수 있습니다. 출발지 주소가 그 규칙의 만료되지 않은 권한을 가지고 있어야 새 연결을 맺을 수 있고(UDP는 새 세션을 만들 때 판단합니다. 권한이 만료된 뒤에도 WireGuard 같은 기존 세션은 계속 전달되며, 방화벽 차단에서 이미 맺은 연결이 유지되는 것과 같습니다. 허용 목록 / 차단 목록은 여전히 패킷마다 판단합니다) 나머지 연결은 바로 끊습니다. 허용 목록 / 차단 목록과 함께 설정하면 둘 다 만족해야 합니다. 노크 규칙은 「방화벽 차단」을 끄고 이런 진입점에서만 쓰게 할 수 있습니다.
프로토콜 라우팅에서는 개별 라우트에서도 「노크 필요」를 선택할 수 있습니다. 예를 들어 443 포트에서 HTTPS 라우트는 모두에게 열고 SSH 라우트는 노크에 성공한 주소에만 엽니다.
포트 하나만 개방
프로토콜 라우팅 규칙에 「포트 노킹」 라우트(프로토콜 spa)를 추가하면 클라이언트가 TCP로 같은 포트에 노크 패킷을 보냅니다.
fwknop -A tcp/443 -P tcp -p 443 -R -D example.com --use-hmac \
--key-base64-rijndael <encryption key> --key-base64-hmac <HMAC key>
ssh -p 443 user@example.com- 노크 라우트는 패킷을 포트 노킹 모듈에 넘기며 전달하거나 응답하지 않습니다.
-A의 포트는 노크 규칙의 포트 목록에 있어야 합니다(예:tcp/443). - 규칙 전체가 노크를 요구하면 노크하지 않은 출처는 노크 라우트만 쓸 수 있고, 노크한 뒤에야 다른 라우트와 기본 대상을 쓸 수 있습니다.
- 규칙에 노크 라우트가 있을 때만 노크 패킷을 식별하므로 다른 규칙의 프로토콜 식별에는 영향이 없습니다.
- 노크 규칙의 UDP 포트를 0으로 설정하면 프로토콜 라우팅 포트만 외부에 열 수 있습니다.
- 노크 라우트가 있는 프로토콜 라우팅 포트와 UDP 노크 포트는 방화벽 차단을 켠 규칙에 넣을 수 없습니다(저장·활성화 시 확인하며, 차단하면 노크 패킷 자체가 도달하지 못하기 때문입니다). 방화벽 규칙을 만들 때도 항상 제외됩니다.
침입 방지 연동
거부된 노크는 「포트 노킹 거부」 이벤트(via 필드는 udp 또는 tcp)를 발행하며, 같은 출처에 대해 5초에 한 번까지만 발행합니다. UDP 패킷의 출처 주소는 위조할 수 있으므로, 공격자가 침입 방지를 이용해 임의의 IP를 차단하게 만들지 못하도록 UDP로 받은 패킷 중 HMAC 인증에 실패한 것은 집계만 하고 이벤트를 발행하지 않습니다. HMAC 인증을 통과했지만(키를 가진 클라이언트가 만든 패킷) 규칙에 의해 거부된 경우(복호화 실패, 재전송, 만료, 허용되지 않은 포트 요청, 사용자 이름 불일치 등)에만 발행합니다. 프로토콜 라우팅의 TCP 포트로 받은 패킷은 핸드셰이크를 거쳐 출처를 위조할 수 없으므로 HMAC 불일치도 발행합니다. 침입 방지에서 「포트 노킹 실패」 템플릿으로 규칙을 만들면(기본 10분 안에 5회면 1일 차단) 위조 패킷을 계속 보내는 출처가 차단되고, 차단된 주소의 노크는 바로 버립니다. 노크 형식이 아닌 데이터(포트 스캔, 다른 프로토콜)는 집계만 하고 이벤트는 발행하지 않습니다.
인증 전에 출처 대역별로(IPv4는 /24, IPv6는 /64마다 초당 최대 10개) 속도를 제한한 뒤 전체 초당 200개 한도를 적용하고, 초과분은 바로 버립니다. 위조 패킷 홍수로 CPU가 가득 차지 않고, 한 출처의 홍수가 다른 출처의 정상 노크를 밀어내지도 않습니다. HMAC 인증에 실패한 패킷은 하나씩 로그에 남기지 않고 1분마다 「N개 출처에서 M개의 잘못된 노크 패킷」 한 줄로 요약합니다.
제한 사항
- fwknop의 Rijndael(AES) CBC 모드와 HMAC만 지원하며 GnuPG, 구형 IV 모드, HMAC이 없는 패킷은 지원하지 않습니다.
- 명령 메시지(
fwknop -C)는 실행하지 않고 NAT 접근 요청(-N)도 지원하지 않습니다. - 노크 권한은 메모리에 저장되므로 Gately를 다시 시작하면 다시 노크해야 합니다(이미 맺어진 연결에는 영향 없음). 재전송 방지 기록도 메모리에만 있으므로 재시작 후에는 타임스탬프가 시작 시각보다 이른 패킷을 모두 거부합니다. 클라이언트 시계가 서버보다 느리면 재시작 직후 몇 초 동안의 노크가 거부될 수 있으며, 다시 노크하면 됩니다.