Cloudflare Zero TrustのWARP Clientを使った状態で広告をブロックする

h3poteto.hatenablog.com

Zero Trustを使い始めたんだけど,こいつをモバイル端末で使う場合,WARP Clientを使うことになる.前回説明した通り,ブラウザ利用であれば特にWARPは不要なんだが,アプリ内でAPIを叩くような場合はこれが必須となる. で,WARPというのはVPNも使っているので,自動的にVPNが設定される.この状態でAdblockするにはどうしたらいいだろうか.

通常モバイル端末でAdblockしようとすると,

  1. VPNをいれる
  2. 広告をブロックしてくれるブラウザを使う

の2択になると思う.ブラウザしか使わないのであれば,2は良い選択肢である.アプリ内ブラウザ等まで対応しようと思うと,1しか選択肢がなくなる. しかし,このVPNWARPVPNと競合するので,どちらか一つしかいれることができない.

というわけで,理想的にはWARPのVNPを有効化している状態でもAdblockできたら良い.

大前提: Split TunnelsとGatewayは別物

developers.cloudflare.com

Split Tunnels only impacts the flow of IP traffic. DNS requests are still resolved by Gateway and subject to DNS policies unless you add the domains to your Local Domain Fallback configuration.

Split Tunnelsはトラフィックを特定のTunnel経由にするというもの.対してGatewayはCloudflareのDNSであって,全く別のレイヤーの話をしている. WARP Clientを使うときは,常にDNSとしてCloudflareのDNSが使われている.ここの制御をするのがGatewayで,GatewayのpoliciesはDNSの挙動に関わる制御をしている.

対して,Split Tunnelsはドメインの解決後,トラフィックをどのルートで送るのかという話しかしていない.なので,ここにDNSは関係ない.

ということは,例えばSplit Tunnels側ではincludeルールで,example.comトラフィックのみをSplit Tunnelsに流しているとする.この場合でもWARP Clientを使えばGatewayを通るので,DNSFirewall Policyは普通に使える.

このGatewayを回避できるのは,Local Domain Fallbackだけだ.ここに設定したドメインだけはCloudflareのDNSを通さずにドメイン解決することができる.だから,GatewayのPolicyを回避させたいようなドメインがある場合はここに設定すると良い.localhost とかね.

Gatewayで特定のドメインをブロックする

qiita.com

基本的にはこの記事と同じことをしている.

Listを作るのは面倒くさかったので全部Terraformにしている.

locals {
  list_size   = 1000
  total_lists = ceil(length(var.ad_domains) / local.list_size)
}

resource "cloudflare_zero_trust_list" "ads" {
  count       = local.total_lists
  account_id  = var.account_id
  name        = "advertisements_${count.index + 1}"
  description = "List of advertisement domains ${count.index + 1}"
  type        = "DOMAIN"

  items = [
    for domain in slice(var.ad_domains, count.index * local.list_size, min((count.index + 1) * local.list_size, length(var.ad_domains))) : {
      value = domain
    }
  ]
}

Listsは上限が1000件なので,ドメインのリストを1000件ずつに分離してListを作っている.こうしておけば,ドメインリストが更新されたらterraform apply するだけで良い.

resource "cloudflare_zero_trust_gateway_policy" "block_ad" {
  account_id     = var.account_id
  name           = "Block ad"
  description    = ""
  device_posture = ""
  enabled        = true
  action         = "block"
  filters        = ["dns"]
  identity       = ""
  precedence     = 2047
  traffic = join(
    " or ",
    [for list in cloudflare_zero_trust_list.ads : format("any(dns.domains[*] in $%s)", list.id)]
  )
  rule_settings = {
    block_page_enabled                 = false
    insecure_disable_dnssec_validation = false
    ip_categories                      = false
    ip_indicator_feeds                 = false
    ignore_cname_category_matches      = false
    resolve_dns_through_cloudflare     = false
  }
}

あとはGateway Policyを作るだけ.

Split Tunnelsには特に触れない

すべてのトラフィックをcloudflared経由にする必要はないと思っていたので,Include IPs and domains で運用している.家のサービスだけをここに追加しているので,他のトラフィックはZero Trustを流れなくなる.

これでWARP ClientのVPN接続した場合でもある程度の広告がブロックできるようになった.

ちなみにDNSを迂回するのは現実的じゃない

世の中にはpi-hole みたいなものもあって,「OS側の設定でDNSを全部pi-holeに回しちゃえばいいじゃん」と思ったときもありました. ここには大きな問題があって,そもそもWARP ClientはDNSも保護されていると謳っている.それはつまり,DNS自体もCloudflareが用意するDNS over HTTPSが強制的に使われていることを意味している.一応確認したけど,OSでのDNS設定をWARP Clientは上書きしてくるので,この方法は無力だった.

もちろんWARP Clientを使わない前提であれば良い選択肢だと思う.