서버 보안을 강화하는 방법 중 하나는 방화벽 포트를 닫아두는 것입니다. 포트 노킹은 이러한 보안 전략을 구현하는 한 가지 방법으로, 특정 ‘비밀 노크’를 제공해야만 포트가 열리도록 구성합니다.
포트 노킹: 비밀스러운 접근법
과거 금주법 시대, 스피크이지(speakeasy)에 들어가려면 특정 암호를 알고 문을 두드려야 했습니다. 포트 노킹은 이와 유사한 개념을 컴퓨터 보안에 적용한 것입니다. 인터넷을 통해 특정 서비스에 접근해야 하지만, 방화벽을 무작정 개방하고 싶지 않을 때 포트 노킹을 활용할 수 있습니다.
포트 노킹은 미리 정의된 특정 순서로 연결 시도를 발생시켜, 방화벽 포트를 닫아두었다가, 정확한 패턴의 연결 시도가 감지되면 자동으로 열리도록 설정합니다. 이 연결 시도 순서가 ‘비밀 노크’ 역할을 하는 것입니다. 반대로, 다른 ‘비밀 노크’를 통해 포트를 다시 닫을 수도 있습니다.
포트 노킹은 흥미로운 방법이지만, ‘모호함을 통한 보안’의 한 예이며, 본질적으로 취약점을 내포하고 있다는 점을 인지해야 합니다. 시스템 접근 방식에 대한 정보가 특정 그룹에만 알려져 있어 보안이 유지되지만, 비밀이 노출되면 (발견, 관찰, 추측, 해킹 등) 보안은 즉시 무력화됩니다. SSH 서버에 키 기반 로그인을 적용하는 것과 같이 더 강력한 보안 방법을 사용하는 것이 좋습니다.
사이버 보안에서 가장 효과적인 접근 방식은 다층적 방어입니다. 포트 노킹은 이러한 방어 계층 중 하나일 수 있지만, 그 자체로는 강력한 보안을 보장하기에는 부족하다고 할 수 있습니다. 포트 노킹이 적절히 강화된 시스템에 큰 이점을 주지 못할 수도 있습니다.
사이버 보안은 광범위하고 복잡한 주제이며, 포트 노킹은 결코 유일한 방어 수단으로 사용되어서는 안 됩니다.
포트 노킹 설정 방법
포트 노킹의 예시를 위해 SSH 포트인 22번 포트를 제어하는 데 활용해 보겠습니다. 이를 위해 knockd라는 도구를 사용할 것입니다. Ubuntu 또는 Debian 기반 배포판을 사용한다면 `apt-get`을 통해 설치할 수 있습니다. 다른 Linux 배포판에서는 해당 배포판의 패키지 관리자를 사용하십시오.
터미널에 다음 명령을 입력합니다:
sudo apt-get install knockd
시스템에 iptables 방화벽이 이미 설치되어 있을 가능성이 높습니다. 하지만 저장된 iptables 규칙을 자동으로 로드하기 위해 `iptables-persistent` 패키지를 추가로 설치해야 할 수도 있습니다.
다음 명령을 입력하여 설치합니다:
sudo apt-get install iptables-persistent
IPv4 설정 화면이 나타나면 스페이스바를 눌러 “예” 옵션을 선택합니다.
IPv6 설정 화면에서도 스페이스바를 눌러 “예” 옵션을 선택하고 계속 진행합니다.
다음 명령은 iptables에 기존 연결을 유지하도록 지시합니다. 이제 SSH 포트를 닫는 명령을 실행합니다.
SSH로 연결된 사용자의 연결이 끊기지 않도록 하기 위한 조치입니다.
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
이 명령은 방화벽에 다음과 같은 규칙을 추가합니다:
- `-A`: 방화벽 규칙 테이블의 맨 아래에 규칙을 추가합니다.
- `INPUT`: 들어오는 연결에 적용되는 규칙입니다.
- `-m conntrack`: `iptables`가 추가적인 패킷 매칭 모듈을 사용하도록 지정합니다. 여기서는 `conntrack` 모듈이 커널의 연결 추적 기능을 활용합니다.
- `–ctstate ESTABLISHED,RELATED`: 규칙이 적용될 연결 유형을 지정합니다. `ESTABLISHED`는 이미 활성화된 연결, `RELATED`는 기존 연결에 의해 파생된 연결을 의미합니다.
- `-j ACCEPT`: 규칙과 일치하는 트래픽을 `ACCEPT` 대상으로 보내, 해당 트래픽을 허용하고 방화벽을 통과시킵니다.
이제 포트를 닫는 명령을 실행할 수 있습니다.
sudo iptables -A INPUT -p tcp --dport 22 -j REJECT
이 명령은 방화벽에 다음과 같은 규칙을 추가합니다:
- `-A`: 방화벽 규칙 테이블의 맨 아래에 규칙을 추가합니다.
- `INPUT`: 들어오는 연결에 적용되는 규칙입니다.
- `-p tcp`: TCP 프로토콜을 사용하는 트래픽에 적용되는 규칙입니다.
- `–dport 22`: 22번 포트(SSH 포트)로 향하는 TCP 트래픽에 적용되는 규칙입니다.
- `-j REJECT`: 규칙과 일치하는 트래픽을 `REJECT` 대상으로 보내, 해당 트래픽을 거부하고 방화벽을 통과시키지 않습니다.
`netfilter-persistent` 데몬을 시작해야 합니다. 다음 명령으로 시작할 수 있습니다.
sudo systemctl start netfilter-persistent
`netfilter-persistent` 데몬이 저장 및 재로드 루틴을 통해 iptables 규칙을 관리하도록 해야 합니다.
다음 명령을 입력합니다:
sudo netfilter-persistent save
sudo netfilter-persistent reload
이제 필요한 유틸리티가 설치되었고, SSH 포트는 (연결을 끊지 않고) 닫혀 있습니다. 이제 ‘비밀 노크’를 구성할 차례입니다.
포트 노킹 설정
포트 노킹을 구성하기 위해 두 개의 파일을 수정해야 합니다. 첫 번째는 `knockd` 구성 파일입니다:
sudo gedit /etc/knockd.conf
gedit 편집기가 열리고 knockd 구성 파일이 로드됩니다.
필요에 맞게 이 파일을 편집합니다. “openSSH” 및 “closeSSH” 섹션이 중요하며, 각 섹션에는 다음과 같은 네 가지 항목이 있습니다.
- `sequence`: 포트 22를 열거나 닫기 위해 접속해야 하는 포트 순서입니다. 기본 포트는 7000, 8000, 9000이고, 열려면 9000, 8000, 7000입니다. 변경하거나 더 많은 포트를 추가할 수 있지만, 여기서는 기본값을 유지하겠습니다.
- `seq_timeout`: 포트를 열거나 닫도록 트리거하기 위해 포트에 접속해야 하는 시간입니다.
- `command`: 열기 또는 닫기 작업이 트리거될 때 iptables 방화벽으로 전송되는 명령입니다. 이 명령들은 방화벽에 규칙을 추가하거나 제거합니다.
- `tcpflags`: 비밀 순서에서 각 포트가 수신해야 하는 패킷 유형입니다. SYN(동기화) 패킷은 TCP 연결 요청인 3방향 핸드셰이크의 일부입니다.
“openSSH” 섹션은 “5초 이내에 7000, 8000, 9000번 포트에 TCP 연결 요청을 보내면, 포트 22를 여는 명령을 방화벽으로 보낸다”는 의미입니다.
“closeSSH” 섹션은 “5초 이내에 9000, 8000, 7000번 포트에 TCP 연결 요청을 보내면, 포트 22를 닫는 명령을 방화벽으로 보낸다”는 의미입니다.
방화벽 규칙
`openSSH` 및 `closeSSH` 섹션의 ‘command’ 항목은 한 가지 매개변수를 제외하고 동일합니다. 다음과 같이 구성되어 있습니다.
- `-A`: 방화벽 규칙 목록의 맨 아래에 규칙을 추가합니다(openSSH 명령어의 경우).
- `-D`: 방화벽 규칙 목록에서 규칙을 삭제합니다(closeSSH 명령어의 경우).
- `INPUT`: 들어오는 네트워크 트래픽과 관련된 규칙입니다.
- `-s %IP%`: 연결을 요청하는 장치의 IP 주소입니다.
- `-p`: 네트워크 프로토콜입니다. 여기서는 TCP입니다.
- `-dport`: 목적지 포트입니다. 예시에서는 22번 포트입니다.
- `-j ACCEPT`: 방화벽 내의 수락 대상으로 보내, 패킷이 조치를 취하지 않고 나머지 규칙을 통과하게 합니다.
knockd 구성 파일 수정
파일의 수정된 부분은 아래에서 빨간색으로 강조 표시됩니다.
`seq_timeout`을 15초로 늘립니다. 이는 넉넉한 시간이지만, 수동으로 연결 요청을 실행하는 경우 이 정도 시간이 필요할 수 있습니다.
`openSSH` 섹션에서 `command`의 `-A` 옵션을 `-I`(삽입)로 변경합니다. 이 명령은 방화벽 규칙 목록의 맨 위에 새 방화벽 규칙을 삽입합니다. `-A` 옵션을 그대로 두면 방화벽 규칙 목록의 맨 아래에 규칙이 추가됩니다.
들어오는 트래픽은 목록의 각 방화벽 규칙에 대해 위에서 아래로 테스트됩니다. 이미 22번 포트를 닫는 규칙이 존재하므로, 들어오는 트래픽이 허용 규칙을 만나기 전에 닫기 규칙에 먼저 걸리면 연결이 거부됩니다. 새 규칙이 먼저 보이면 연결이 허용됩니다.
닫기 명령은 방화벽 규칙에서 `openSSH`에 의해 추가된 규칙을 삭제합니다. 이제 SSH 트래픽은 기존의 “22번 포트 닫힘” 규칙에 따라 다시 처리됩니다.
편집을 마친 후 구성 파일을 저장합니다.
knockd 제어 파일 수정
knockd 제어 파일은 상대적으로 간단합니다. 편집하기 전에 네트워크 연결의 내부 이름을 알아야 합니다. 다음 명령으로 찾을 수 있습니다.
ip addr
이 문서 작성에 사용된 연결은 `enp0s3`입니다. 이 연결 이름을 기록해 둡니다.
다음 명령은 knockd 제어 파일을 편집합니다:
sudo gedit /etc/default/knockd
gedit에 열린 knockd 파일은 다음과 같습니다.
수정해야 할 부분은 빨간색으로 강조 표시되어 있습니다.
`START_KNOCKD=` 항목을 0에서 1로 변경합니다.
`KNOCKD_OPTS=` 항목의 시작 부분에서 해시 기호 #를 제거하고 `eth1`을 네트워크 연결 이름인 `enp0s3`으로 바꿉니다. 물론 네트워크 연결이 `eth1`이면 변경할 필요가 없습니다.
실행 확인
이제 모든 설정이 완료되었으므로 실행되는지 확인할 차례입니다. 다음 명령으로 knockd 데몬을 시작합니다.
sudo systemctl start knockd
이제 다른 컴퓨터로 이동하여 연결을 시도합니다. 포트 노킹을 구성하기 위해서가 아니라, knock 패키지가 `knock`라는 도구를 제공하기 때문에 해당 컴퓨터에도 `knock`를 설치했습니다. 이 컴퓨터를 사용하여 지정된 순서대로 연결을 시도하여 ‘노크’를 실행합니다.
다음 명령을 사용하여 IP 주소가 192.168.4.24인 호스트 컴퓨터의 포트에 접속하기 위한 ‘비밀 순서’를 전송합니다.
knock 192.168.4.24 7000 8000 9000 -d 500
이 명령은 192.168.4.24의 컴퓨터에 접속을 시도하고, 7000, 8000, 9000번 포트 순서대로 연결 요청을 실행합니다. 각 포트 사이에는 500밀리초의 지연시간(-d)이 있습니다.
그런 다음 `dave`라는 사용자가 192.168.4.24에 SSH 요청을 합니다.
ssh [email protected]
연결이 수락되고 비밀번호를 입력하면 원격 세션이 시작됩니다. 프롬프트는 `[email protected]`와 같습니다. 원격 컴퓨터에서 로그아웃하려면 다음 명령을 입력합니다.
exit
프롬프트가 로컬 컴퓨터로 돌아옵니다. 다시 한 번 `knock`를 사용하되 이번에는 포트를 역순으로 지정하여 원격 컴퓨터의 SSH 포트를 닫습니다.
knock 192.168.4.24 9000 8000 7000 -d 500
특별히 유용한 원격 세션은 아니었지만 포트 노킹을 통해 포트를 열고 닫는 과정을 보여주며 하나의 스크린샷에 모두 담을 수 있었습니다.
반대편에서는 어떻게 보일까요? 포트 노킹 호스트의 시스템 관리자는 다음 명령으로 시스템 로그에 기록된 내용을 확인할 수 있습니다.
tail -f /var/log/syslog
세 개의 `openSSH` 항목이 표시됩니다. 이는 원격 `knock` 유틸리티가 각 포트에 접속을 시도했기 때문입니다.
트리거 시퀀스의 세 단계가 모두 충족되면 “열려라 참깨” 메시지가 기록됩니다.
IP 주소가 정확한 비밀 노크를 제공한 컴퓨터(192.168.4.23)인 경우, 포트 22에 대한 SSH 액세스를 허용하는 규칙이 `iptables` 규칙 목록에 삽입됩니다.
사용자 `dave`가 잠시 연결한 후 연결을 끊습니다.
세 개의 `closeSSH` 항목이 표시됩니다. 이는 원격 `knock` 유틸리티가 각 포트에 접속을 시도했기 때문입니다. 이는 포트 노킹 호스트에 22번 포트를 닫도록 지시합니다.
세 단계가 모두 트리거되면 다시 “OPEN SESAME” 메시지가 표시됩니다. 방화벽에서 규칙을 제거하는 명령이 전송됩니다. (포트를 닫을 때 “CLOSE SESAME” 메시지가 아닌 이유가 무엇일까요? 누가 알겠습니까?)
이제 22번 포트에 대한 `iptables` 규칙 목록에는 해당 포트를 닫기 위해 처음에 입력한 규칙만 남게 됩니다. 따라서 22번 포트는 다시 닫힙니다.
결론
포트 노킹의 기본적인 작동 원리입니다. 재미 삼아 해볼 만한 방법이지만, 실제 환경에서는 이러한 방식에만 의존하지 않는 것이 좋습니다. 필요하다면 보안을 위한 유일한 방법으로 사용하지 마십시오.