태그: DNS

KCSC, Warning.or.kr에 적용된 필터링 방법과 사설

 

본래 방송통신심의위원회는 한국에서 ‘불법’으로 규정하는 사이트에 대한 접근을 차단하기 위하여 DPI 기반의 필터링을 도입합니다.
이 장비는 HTTP Request의 헤더를 분석하여 Host 필드의 값이 특정 도메인이라면 302 Redirect를 전송, warning.or.kr로 보내버립니다.

이 시스템은 2006년부터 동작하기 시작했는데, 이 시점에서 이미 기본권 침해 논란이 불거지기 시작하였습니다.
비록 Host 필드만을 확인하는 것이지만, 마음만 먹으면 특정 IP에서 어떠한 사이트에 들어갔는지 리스트를 작성할 수 있기 때문입니다.

사용자들은 이에 대응하여 DPI 장비가 필터링하지 못하도록 Host 필드의 뒤에 \n을 추가하거나, Host 대신 HoSt를 사용하는 등, HTTP 표준에는 부합하지만 DPI 장비의 필터에는 걸리지 않는 Request를 생성하여 우회하였습니다.

물론 KCSC가 DPI 장비를 개량하면서 다시 막혔습니다.

 

그러다 많은 사이트들이 HTTPS를 기본 지원하게 되면서, 기존의 HTTP 헤더 검사로는 차단이 불가능하게 되었습니다.
HTTPS 헤더를 까 봐야 암호화된 문자열들만이 나타나기 때문입니다.

그러자 KCSC는 DNS Spoofing을 시도합니다. 국내 ISP들의 DNS 서버에 특정 도메인 주소에 대한 응답을 warning.or.kr 서버로 돌리도록 한 것이죠.
물론 사용자들은 8.8.8.8과 1.1.1.1 등의 DNS 서버로 비교적 쉽게 우회할 수 있었고, 이에 한 때 1.1.1.1에 대한 접속을 차단하는 등의 강수를 두다가 철회하였습니다.

이 시점에서 KCSC가 취할 수 있는 대응 수단은 몇 가지가 있습니다.

 

1. DNS Request를 감시, 특정 도메인에 대한 요청을 찾으면 Request의 Source IP와 Response의 IP를 통해 TCP 커넥션을 식별, 차단
2. 특정 도메인에 대한 DNS 쿼리를 통해 IP Pool 확보, 해당 IP에 대한 접속을 차단
3. TLS의 Client Hello 메시지에서 암호화되지 않는 부분을 이용, Host 식별 후 차단
4. TLS의 Server Hello 메시지에서 Certificate를 확인, Server Name 필드를 식별 후 차단

 

1번은 기술적으로 구현이 힘들고 (컴퓨팅 파워 부족), DNS를 암호화하면 무력화됩니다
2번은 CloudFlare같은 접속 서비스를 사용하는 사이트에 대해서는 효과를 보기 힘든 데다(CloudFlare CDN 진입점을 차단할 수는 없으니), Virtual Host등을 이용하여 같은 서버를 사용할 경우 무고한 피해자가 발생할 수 있는 등의 문제가 있으며
3번은 현재 적용된 SNI 필터링입니다. 단, 이 문제점을 이미 인식하고 있기에 TLS 1.3에서 도입된 ESNI가 적용될 경우 무력화됩니다
4번은 ESNI가 보급된 이후에 적용될 것으로 예상되는 방법으로, 아직 대응방안이 마련되지 않았습니다

 

이 모든 것들을 한번에 해결할 수 있는 방법으로 VPN을 사용하는 것이 있으나, VPN 트래픽을 식별해낼 수 있으며, Packet을 Drop하는 방법으로 차단 또한 가능합니다.
이미 중국에서는 황금방패에 적용된 기법이고, VPN 트래픽 식별을 위해 머신러닝을 적용하고 있다는 이야기도 들려오고 있습니다.

결과적으로 창과 방패의 싸움이지만, 사법권력을 쥐고 있는 방패가 결국은 승리할 것으로 보입니다.

Windows 클라이언트에서 DNS Whitelist 적용하기

DNSCrypt를 이용하는 것으로 간단하게 구성할 수 있다.

https://github.com/jedisct1/dnscrypt-proxy/releases/tag/2.0.19

1. 위 주소에서 DNSCrypt for Windows를 다운로드 받은 뒤 적당한 경로에 압축을 푼다.

2. blacklist.txt에 “*.*”을 추가한다.

3. whitelist.txt에 원하는 도메인들을 추가한다.

4. dnscrypt-proxy.toml에서 whitelist_file, blacklist_file의 주석을 해제한다.

5. service-install.bat를 실행한다.

6. DNS 서버를 127.0.0.1로 설정한다.

 

이 구성이 실효성을 가지기 위해서는 제한을 받는 사용자가 서비스, DNS 설정과 dnscrypt 설정을 변경하지 못하도록 권한 제어가 이루어져야 할 것이다.

Microsoft PowerShell을 이용한 DNS 서버 레코드 갱신

 

네트워크 관련 작업을 하다 보면 (주로 비용상의 문제로) 유동 IP를 사용해야 하는 경우가 생기곤 한다.
유동 IP 환경에서 서비스를 제공하기 위하여 주로 DDNS를 사용하게 되는데, 웬만한 도메인 서비스들은 DDNS 서비스를 자체적으로 제공하지만, 만약 자체 도메인 서버를 운용하는 경우라면 어떨까?

리눅스라면 BIND와 쉘 스크립트라는 걸출한 물건이 존재한다. 그리고 윈도우에서는 PowerShell을 이용하여 DNS 서버를 관리할 수 있다.

 

PowerShell로 서버를 관리하기 위해서는 몇 가지 전제 조건이 필요하다.

1. WinRM 서비스가 활성화되어 있어야 한다.
2. PSRemoting이 활성화되어 있어야 한다.
3. WinRM 서비스에서 클라이언트/서버가 각각 신뢰하는 호스트로 등록되어 있어야 한다.
4. WinRM 서비스 포트인 TCP 5985/5986이 열려있어야 한다.
5. 클라이언트에서 파워 쉘 스크립트 실행 권한(Set-ExecutionPolicy)이 주어져 있어야 한다.

 

먼저 서버에서 PSRemoting을 활성화해야 한다. 파워 쉘을 관리자 권한으로 열고, 다음 명령을 입력한다.

기본적으로 PSRemoting은 같은 홈/도메인 네트워크 안, 동일한 서브넷 내에서만 허용되어 있다.
이 제약을 풀기 위하여 다음과 같은 명령어를 입력한다.

이제 서버와 클라이언트에서 서로를 신뢰하는 호스트로 지정해주어야 한다.

호스트 이름은 IP 혹은 FQDN이 될 수 있으며, ‘,'(콤마)로 구분된다. 만약 모든 호스트에 대하여 허용하고 싶다면 *(와일드카드 문자열)을 사용하자.

 

이 시점에서 WinRM 서비스가 시작되어 있지 않다면 등록 과정에서 에러가 발생한다. 경고 메시지에서는 자동으로 WinRM 서비스를 시작한다고 하지만, 실제로는 실행되지 않는다.
그러므로 수동으로 시작해준다.

WinRm 서비스가 시작된 후 정상적으로 신뢰하는 호스트 등록이 가능할 것이다.

 

이렇게 서버-클라이언트의 설정이 마무리되면, 접속을 테스트 해 본다.

접속이 정상적으로 이루어진다면 이러한 메시지가 나올 것이다. 만약 접속되지 않는다면 방화벽과 신뢰하는 호스트의 설정을 다시 살펴보자.

접속에 성공하였다면 원격 세션을 통하여 PowerShell을 실행할 준비가 되었다는 뜻이다.

 

이제 실제로 원격 세션을 열어보자. PowerShell에서 원격 세션을 얻을 수 있는 명령어는 크게 두 가지가 있다.

둘 다 원격 세션을 생성하지만, Enter-PSSession은 프롬프트에 직접 로그인하고, New-PSSession은 세션 객체를 만든다는 차이가 있다.

단순히 명령어를 실행하는 것은 Enter-PSSession이 더 간단하지만, 명령을 수행한 결과를 Return한다거나, 로컬 변수를 이용하여 명령을 수행해야 하는 경우에는 New-PSSession이 더 적합하다.

 

New-PSSession을 이용하여 원격 세션을 만드는 것은 아주 간단하다.

여기서 Credential 옵션은 인증 정보를 제공한다. 공란으로 두거나 String을 입력하였을 경우, 윈도 로그인 창이 팝업되고 인증 정보를 입력할 수 있다.
하지만 우리의 목적은 스크립트를 이용한 자동화이므로 수동으로 자격 증명을 입력하는 것은 바람직하지 않다. 그렇다면 어떻게 인증 정보 입력을 자동화 할 수 있을까?

 

정답은 Credential 옵션에 PSCredential 오브젝트를 넘겨주는 것이다. 단, 이 때 주의할 것은 PowerShell Credential에서 패스워드는 암호화 된 스트링으로 지정된다는 점이다.
Plain String을 넣을 경우 인증이 이루어지지 않는다. 그러므로 먼저 Plain-Text를 SecureString으로 변환해주어야 한다.

새로운 secureString 객체를 만들고 password를 SecureString으로 변환하였다.

 

그리고 PSCredential 오브젝트를 만들 때 아이디와 SecureString을 인자로 넘겨주고, PSCredential로 인증을 수행한다.
이것으로 새로운 PSSession 객체를 얻었다.

 

원격 접속이 성립하였으니, 원격으로 명령어를 실행해야 할 것이다. 원격 명령어 실행을 위해 사용하는 것이 바로 Invoke-Command 명령어다.

Invoke-Command 명령어의 문법은 다음과 같다.

여기서 지역 변수를 전달하고 싶다면 params()와 -ArgumentList 옵션을 사용한다.

 

이 명령어를 이용해 가장 먼저 할 일은 DNSServer 모듈이 존재하는지 확인하는 것이다.

DNSServer Cmdlet이 존재한다면 모듈 목록에 DnsServer가 나올 것이다.

 

DNSServer Cmdlet이 있다면 실제로 DNS 서버의 레코드를 읽고, 변경할 수 있다.
지금은 DDNS를 적용할 것이므로, 레코드의 값을 변경하는 명령어인 Set-DnsServerResourceRecord를 이용해야 한다.

Set-DnsServerResourceRecord cmdlet은 목표 레코드를 리소스 레코드 오브젝트를 통하여 검색한다. 즉, 그 오브젝트와 일치하는 대상을 수정할 수 있다.
그러므로 우리는 먼저 Get-DnsServerResourceRecord를 통하여 목표 리소스 레코드 오브젝트를 얻고, 그것을 수정하여 새로운 변수에 담고, 원본 리소스 레코드를 트리거로 삼아 목표 값을 갱신할 수 있다.

이 때, IPv4 Address의 경우 String을 직접 입력하지 못하므로, System.Net.IPAddress 객체로 변환해주어야 한다는 점에 유의하자.

 

백문이 불여일견, 샘플 소스를 하나 올려두겠다.

 

마지막 줄의 Get-PSSession | Remove-PSSession을 통해 사용이 끝난 세션은 반드시 종료하자.