오늘은 Cloudflare Workers 를 사용하여 TOR 네트워크 IP(릴레이 및 브리지)를 무료로 차단하는 방법을 배웁니다.

본격적으로 시작하기 전에, Cloudflare가 최근 Managed Lists 를 도입하여 클릭 한 번으로 TOR을 차단할 수 있게 되었음을 알려드립니다. 유일한 단점은 Enterprise 고객에게만 제공된다는 것입니다.

여유가 되신다면 좋습니다. 이 글을 무시하셔도 됩니다. 하지만 Enterprise 플랜을 이용할 수 없다면 계속 읽어주세요.

TOR 네트워크 차단

처음에는 cron job으로만 사용되는 worker를 만들어 TOR IP를 가져오고, 파싱하여 Cloudflare Lists에 저장했습니다. 이 목록은 Firewall Rules에서 모든 웹사이트에 사용될 것이었습니다.

Lists 기능의 주요 문제는 모든 목록에 걸쳐 10,000개 항목으로 제한된다는 것이었습니다. Business 또는 Enterprise 플랜이든 상관없이 여기 에 문서화된 대로 현재 제한입니다.

다른 문제는 여기 에 명시된 대로 현재 개별 IPv6 주소를 저장할 수 없다는 것이었습니다.

이 두 가지 주요 문제로 인해 목록 사용은 실행 가능한 옵션이 아니었습니다. 그래서 대신 Workers KV로 전환했습니다. 많은 테스트와 Cloudflare Discord 채널에서 여러 사람들과의 논의 후, 이러한 TOR IP를 저장하고 쿼리하는 최선의 방법을 찾았습니다.

“스마트한” 접근 방식을 사용하여 모든 TOR IP에 대해 하나의 항목(key:value)만 저장합니다. 이로써 비용을 상당히 절약할 수 있습니다.

이것이 작동하는 이유는 Workers KV 값 제한이 25 Mb인 반면, 저장하는 많은 IP는 몇 Kb만 차지하기 때문입니다.

Workers KV 무료 플랜의 제한은 이러한 IP를 지속적으로 읽고 쓰기에 충분합니다. 부족할 경우 worker 내에서 캐싱을 적용할 수 있으며(READ에 10분 캐싱을 이미 적용했습니다), 플랜은 월 5$로 업그레이드할 수 있습니다.

물론 사용을 스마트하게 관리하여 보호하려는 특정 영역(예: 로그인 영역 등)에서만 사용자 IP를 확인할 수도 있습니다.

서비스 워커

Github 에 바로 사용할 수 있는 worker를 준비했습니다.

클론하고, 요구 사항을 구성하고, 게시하기만 하면 됩니다.

구성

이것이 작동하려면 몇 가지가 필요합니다:

  • Wrangler
  • 계정 ID
  • 하나의 KV Namespace

계정 ID를 얻으려면 Cloudflare에서 웹사이트 중 하나를 열고 “Overview” 페이지의 오른쪽 하단을 확인하세요. 계정 ID를 얻으면 wrangler.toml에 저장하세요:

1account_id = "..."

Worker KV Namespace를 만들려면 다음 명령을 실행하세요:

sudo wrangler kv:namespace create storage

결과는 다음과 같아야 합니다:

1🌀 Creating namespace with title "cloudflare-block-tor-worker-storage"
2✨ Success!
3Add the following to your configuration file:
4kv_namespaces = [
5{ binding = "storage", id = "cc309bd612d9476f8abdc43ec4f93fd6" }
6]

위의 ID를 사용하여 wrangler.toml을 편집하고 ID를 저장하세요:

1[[kv_namespaces]]
2binding = "TOR_COMBINED_LIST"
3id = "cc309bd612d9476f8abdc43ec4f93fd6"

KV Namespace를 직접 만들지 마세요. 위의 명령을 사용하세요. KV Binding은 필수이며 무시하지 마세요.

위의 단계가 완료되면 worker를 게시하세요:

sudo wrangler publish

worker는 60분(1시간)마다 실행되도록 구성되어 있습니다. 변경하려면 wrangler.toml을 편집하세요. API가 시간당 한 번만 업데이트되므로 더 짧은 간격은 도움이 되지 않습니다.

목록 사용

이제 TOR 네트워크 IP 목록이 있으며 사용할 차례입니다. workers에 이미 익숙하다면 매우 간단하고, 익숙하지 않아도 쉽습니다.

아이디어는 들어오는 사용자 IP를 목록과 대조하고 일치하면 요청을 차단하는 것입니다. 아래는 새 worker에 간단히 복사하여 붙여넣을 수 있는 간단한 스타터 스크립트입니다:

 1const clientIP = request.headers.get('CF-Connecting-IP');
 2const ipset = await TOR_COMBINED_LIST.get("ipset", { type: "json", cacheTtl: 600 });
 3
 4if (ipset[clientIP])
 5{
 6return new Response("TOR is Forbidden", {
 7headers: {
 8'content-type': 'text/html;charset=UTF-8',
 9},
10status: 403
11});
12}

IP 목록이 하나의 객체로 저장되어 있기 때문에 조회가 매우 빠르며, 파싱이나 검색이 필요하지 않습니다.

캐싱도 적용되어 있어(원하면 비활성화할 수 있음) 속도가 더욱 빨라집니다. Workers KV 캐시는 Cache API 를 사용하여 작동합니다.

값을 가져온 후 IP가 존재하는지 확인하고, 존재하면 원하는 것을 반환합니다.

위 스니펫을 request 객체가 사용 가능한 한 새로운 또는 기존 worker(심지어 Cloudflare Pages)에서 어디든 사용하세요.

목록 테스트

앞서 언급했듯이, Workers를 사용한 적이 없어도 이 목록을 사용하는 것은 매우 쉽습니다.

어떤 웹사이트에서든 간단히 복사하여 붙여넣고 사용할 수 있는 작은 예제를 만들었습니다:

 1addEventListener("fetch", event => {
 2event.respondWith(handleRequest(event.request))
 3})
 4
 5const html = `<!doctype html>
 6
 7<html lang="en">
 8  <head>
 9    <title>Access Denied</title>
10    <meta charset="utf-8">
11    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
12    <meta name="robots" content="noindex, nofollow" />
13    <meta name="viewport" content="width=device-width, initial-scale=1">
14    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> 
15  </head>
16  <body>
17    <div class="container">
18      <h1 class="mt-5 text-center">Oops!</h1>
19      <h2 class="mt-2 text-center text-danger">TOR Network is not allowed.</h2>
20    </div>
21  </body>
22</html>`;
23
24async function handleRequest(request)
25{
26const clientIP = request.headers.get('CF-Connecting-IP');
27const ipset = await TOR_COMBINED_LIST.get("ipset", { type: "json", cacheTtl: 600 });
28
29if (ipset[clientIP])
30{
31return new Response(html, {
32headers: {
33'content-type': 'text/html;charset=UTF-8',
34},
35status: 403
36});
37}
38
39const response = await fetch(request);
40
41return response;
42}

이 worker에 KV Binding을 추가하는 것을 잊지 마세요.

위의 worker가 배포된 상태에서 TOR 브라우저 를 실행하고 테스트합니다:

Block TOR Result

보시다시피 예상대로 정확히 작동합니다. TOR 네트워크 IP가 차단되었으며, 릴레이와 브리지 모두 해당됩니다.

제 자체 웹사이트에서는 이를 제거했으므로 지금 TOR을 통해 접속하면 작동합니다(TOR을 차단할 이유가 없습니다).

비용 계산

이 글의 시작 부분에서 무료 Worker 제한이 TOR을 차단하기에 충분하다고 말씀드렸으며, 실제로 그렇습니다. 또한 이 프로젝트를 만드는 최선의 방법을 찾기 위해 많은 테스트를 했다고 언급했습니다.

테스트 중 많은 READ/WRITE/LIST/DELETE 요청을 사용하여 결국 월 약 60-100$의 비용이 발생했습니다. 이상적이지 않죠? 😳

그래서 시간당 하나의 WRITE만 수행하는 방법을 찾았습니다. 이는 하루 24 WRITE, 월(31일) 744 WRITE를 의미합니다.

이는 Workers KV 무료 티어 내에 유지되며 TOR을 차단할 수 있습니다.

유용한 링크

이상입니다. 이 글과 도구가 도움이 되었기를 바랍니다.

의견을 알려주세요!