Як заблокувати рекламу в застосунках на Apple-девайсах: DNS-over-HTTPS сервер для Pi-hole

💡 Усі статті, обговорення, новини про Mobile — в одному місці. Приєднуйтесь до Mobile спільноти!

Привіт! Мене звати Олександр Ющенко, я військовослужбовець НГУ і Software Engineer. Ця стаття частково базується на туторіалі від Богдана Свердлюка по налаштуванню Pi-Hole. Це прекрасний матеріал, і я раджу прочитати його перед тим, як виконувати дії в цій статті.

Нижчеописане рішення дозволяє блокувати банеру рекламу та відеорекламу і в браузері, і в застосунках (при правильному налаштуванні блок-лістів в Pi-hole). Наприкінці бонус для тих, хто хоче спробувати, як це працює, проте не хоче робити купу налаштувань для цього.

Ця стаття також буде корисна тим, хто використовує сторонні блокувальники реклами на iOS, які можуть не давати той рівень функціональності, кастомізації чи приватності, який може дати власний сервер з Pi-hole. Особливо це корисно тим, хто хоче замінити мобільний Adguard і не знає альтернативи.

Проте проблема Pi-hole «з коробки» в тому, що він підтримує лише звичайний незашифрований DNS. Для більшості побутових задач цього достатньо, але суттєвий недолік цього рішення — він не блокує рекламу всередині застосунків на Apple-девайсах. Починаючи з iOS 14 Apple надала можливості використання DoH (DNS over HTTPS) для шифрування DNS-запитів, ускладнення DNS спуфінгу і, очевидно, щоб рекламу було не так легко заблокувати. Отже, реклама всередині застосунків резолвиться за допомогою DoH через DNS-сервери, визначені рекламним SDK, оминаючи DNS, вказаний для мережі. Ця поведінка не повʼязана з тим, чи увімкнено Private Relay.

Але це вирішується за допомогою додавання системної політики для пристрою, яка б змушувала використовувати потрібний DNS-сервер через HTTPS.

Початкові умови

Обладнання та софт, що я використовував:

  • HP Elitedesk 800 G4 mini
  • Proxmox VE 8
    • Nginx Proxy Manager (LXC)
    • Pi-hole (LXC)
      • Unbound (recursive proxy)
      • doh-server
  • TP-Link Omada VPN Router
    • Omada Cloud Controller
  • Cloudflare
    • Куплений домен
    • Таблиця DNS

Моя конфігурація трохи відрізняється від статті Богдана, оскільки я використовую Unbound для того, щоб резолвити DNS незалежно від провайдерів, але в даному матеріалі ми розбиратимемо шлях до Pi-Hole.

План

Відповідно, план полягатиме в тому, щоб запустити деякий сервіс, який перетворював би DoH в звичайний DNS запит і відправляв би його на Pi-hole.

Для цього можна використовувати doh-server від DNSCrypt. Зараз це open-source рішення доволі нове і має версію 0.9. Альтернативно можна використовувати dnscrypt-proxy та налаштувати його таким чином, щоб перенаправляти запити на Pi-Hole, але, на мою думку, це ускладнює рішення.

В моїй конфігурації doh-server працюватиме через http-протокол, хоча програма має вбудовані можливості використовувати TLS, вказавши в аргументах шлях до сертифікату.

Цього б було достатньо для домашнього використання, але я також хочу, щоб це рішення працювало за межами локальної мережі. Для цього треба повʼязати IP DoH-сервера та домен в Cloudflare DNS-таблиці, покинути порт завдяки port forwarding на роутері та налаштувати проксі з https в nginx proxy manager.

Ось спрощена схема:

Schema

Встановлюємо doh-server

  1. Завантажуємо та встановлюємо відповідний пакет для вашої системи
    • Для x86:
      wget https://github.com/DNSCrypt/doh-server/releases/download/0.9.15/doh-proxy_0.9.15-1_amd64.deb
      sudo apt install ./doh-proxy_0.9.15-1_amd64.deb
    • Для ARM:
      wget https://github.com/DNSCrypt/doh-server/releases/download/0.9.15/doh-proxy_0.9.15-1_arm64.deb
      sudo apt install ./doh-proxy_0.9.15-1_arm64.deb
  2. Додаємо запуск сервісу:
    1. Створюємо файл:
      sudo nano /etc/systemd/system/doh-proxy.service
    2. Додаємо вміст:
      [Unit]
      Description=DNS-over-HTTPS Proxy
      After=network.target
      
      [Service]
      ExecStart=/usr/bin/doh-proxy \
      -H ’doh.yourdomain.com’ \ ## Домен за яким буде доступний сервер
      -u 127.0.0.1:53 \ ## IP-адреса і порт де запущено Pi-hole
      -l 0.0.0.0:3000 ## запускаємо сервіс на порту 3000 для доступу з усіх IP
      User=nobody
      Group=nogroup
      
      [Install]
      WantedBy=multi-user.target
      Якщо використовуєте doh-server та nginx на одному хості — можна видалити -l 0.0.0.0:3000. Тоді сервіс буде запускатись і доступний через порт 3000 для використання на localhost (127.0.0.1). Але тоді ви не зможете перевірити роботу з іншого пристрою напряму (пункт 3).
    3. Підвантажуємо файл сервісу та запускаємо.
      sudo systemctl daemon-reload
      sudo systemctl enable —now doh-proxy
  3. Перевіряємо роботу з іншого пристрою в локальній мережі.
    curl -v -H 'accept: application/dns-json' 'http://<ip-адреса>:3000/dns-query?name=example.com&type=A'
    в результаті маємо отримати приблизно таку відповідь:
    {"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"example.com","type":1}],"Answer":[{"name":"example.com","type":1,"TTL":0,"data":"23.192.228.80"},{"name":"example.com","type":1,"TTL":0,"data":"23.215.0.138"},{"name":"example.com","type":1,"TTL":0,"data":"23.192.228.84"},{"name":"example.com","type":1,"TTL":0,"data":"23.220.75.245"},{"name":"example.com","type":1,"TTL":0,"data":"23.215.0.136"},{"name":"example.com","type":1,"TTL":0,"data":"23.220.75.232"}]}

Дізнатись ip в локальній мережі можна, виконавши команду ip a.

Налаштування домену

Cloudflare (якщо є свій домен)

  1. Дізнаємось свою публічну IP-адресу.
    curl ifconfig.me
  2. Логінимось в акаунт та переходимо до таблиці DNS-записів.

    Cloudflare DNS

  3. Додаємо запис з вашою публічною IP-адресою та тимчасово вимикаємо Proxy status.
  4. Переходимо в профіль -> API Tokens та створюємо токен для DNS Challenge, щоб згенерувати SSL-сертифікат.
    Надаємо права на Zone.Zone Read, Zone.DNS Edit.

Duck DNS (якщо немає свого домену)

  1. Логінимось або реєструємо акаунт.
  2. Додаємо домен (одразу заповниться ваш IP).

    Duck DNS

    Примітка: багато провайдерів ховають клієнтів за NAT, тому навіть динамічна публічна адреса може не спрацювати. Краще уточнити цей нюанс і за необхідності підключити статичну IP-адресу.

Налаштовуємо Nginx Proxy Manager

  1. Додаємо Proxy Host.
    Вказуємо наш домен, протокол http, IP-адресу doh-server та порт 3000.
    Вказуємо, що цей Proxy публічно доступний.

    nginx create proxy

  2. Переходимо на вкладку SSL та генеруємо сертифікат.
    Вибираємо DNS Challenge та DNS провайдера (Cloudflare/DuckDNS).
    Вставляємо API токен Cloudflare або токен DuckDNS.

    nginx certbot

  3. Після цього можете включити проксі в Cloudflare.

Налаштування роутера

Залежно від моделі роутера слід шукати налаштування Port Forwarding та додати запис з вхідним портом 443, вихідним портом 443, TCP протокол та IP-адресу nginx.

Port Forwarding

За необхідності також зарезервуйте IP-адреси в локальній мережі для nginx та doh-server, використовуючи DHCP Reservation. Або самостійно пропишіть IP адреси за межами розподілення DHCP на пристрої(ях) з цими сервісами.

DHCP Reservation

Генеруємо файл для Apple-девайсу

  1. Створюємо файл doh.mobileconfig.
  2. Додаємо вміст:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
        <dict>
            <key>PayloadContent</key>
            <array>
                <dict>
                    <key>DNSSettings</key>
                    <dict>
                        <key>DNSProtocol</key>
                        <string>HTTPS</string>
                        <key>ServerURL</key>
                        <string>https://doh.yourdomain.com/dns-query</string> <!-- Вкажіть ваш домен -->
                    </dict>
                    <key>OnDemandRules</key>
                    <array>
                        <dict>
                            <key>Action</key>
                            <string>EvaluateConnection</string>
                            <key>ActionParameters</key>
                            <array>
                                <dict>
                                    <key>DomainAction</key>
                                    <string> NeverConnect</string>
                                    <key>Domains</key>
                                    <array>                                    
                                        <!-- Додайте домени для ігнорування -->
                                        <string>captive.apple.com</string>
                                    </array>
                                </dict>
                            </array>
                        </dict>
                        <dict>
                            <key>Action</key>
                            <string>Connect</string>
                        </dict>
                    </array>
                    <key>PayloadDescription</key>
                    <string>Configures device to use Pi-Hole DNS</string>
                    <key>PayloadDisplayName</key>
                    <string>Pi-Hole DoH</string>
                    <key>PayloadIdentifier</key>
                    <string>com.apple.dnsSettings.managed.$(uuidgen)</string> <!-- uuid -->
                    <key>PayloadType</key>
                    <string>com.apple.dnsSettings.managed</string>
                    <key>PayloadUUID</key>
                    <string>$(uuidgen)</string> <!-- uuid -->
                    <key>PayloadVersion</key>
                    <integer>1</integer>
                </dict>
            </array>
            <key>PayloadDescription</key>
            <string>Adds DoH to macOS Big Sur and iOS 14 or newer systems</string>
            <key>PayloadDisplayName</key>
            <string>Pi-Hole DoH</string>
            <key>PayloadIdentifier</key>
            <string>$(uuidgen)</string> <!-- uuid -->
            <key>PayloadRemovalDisallowed</key>
            <false/>
            <key>PayloadType</key>
            <string>Configuration</string>
            <key>PayloadUUID</key>
            <string>$(uuidgen)</string> <!-- uuid -->
            <key>PayloadVersion</key>
            <integer>1</integer>
        </dict>
    </plist>
  3. Вкажіть свій домен.
  4. Згенеруйте та вставте рандомні uuid у вказані поля (замість «$(uuidgen)»).
  5. Також ви можете вказати домени, які ігноруватимуться цим правилом.
    Це корисно, якщо використовуєте корпоративний VPN і DNS сервер компанії знаходиться в локальній мережі.

Налаштування macOS

  1. Відкрийте файл конфігураціїна своєму пристрої та перейдіть в Налаштування -> General -> Device Management, активуйте профіль.

    Pasted Graphic 9.png

  2. Перейдіть в Network -> VPN & Filter. Тут ви можете вмикати та вимикати DoH за необхідності.

    Pasted Graphic 10.png

Налаштування iOS/iPadOS

  1. Відкрийте файл конфігурації на вашому пристрої. Перейдіть в налаштування -> General -> VPN & Device Management. Активуйте профіль.

    Pasted Graphic 11.jpg

  2. У меню DNS виберіть ваш профіль замість Automatic. В цьому меню ви можете вмикати і вимикати DoH за необхідності.

    Pasted Graphic 12.jpg

Таким чином тепер блокування реклами працюватиме в застосунках і не в локальній мережі. Але слід звернути увагу, що під час відключень світла ваш роутер та/або сервер можуть не працювати і, відповідно, не працюватиме DNS-сервер. Ви можете втратити доступ до інтернету через те, що девайс не може визначити IP-адресу за DNS. Тому слід або вимикати DoH, або забезпечити безперебійну роботу обладнання.

Бонус

Ви можете спробувати, як працює DoH, не налаштовуючи власний сервер. Проте не матимете можливості ніяк впливати на фільтри блокування реклами. Існує ініціатива DNS4EU з безкоштовними публічними DNS-серверами. Ви можете завантажити і встановити файл конфігурації з блокуванням реклами від цього сервісу. Інструкція за цим посиланням. Але фільтри там невеликі і більшість реклами залишається.

Також за цим посиланням можна подивитись список різних публічних DNS-серверів. Ви можете спробувати створити власний файл конфігурації і вказати URL.

Використання сторонніх DNS серверів може мати різні наслідки. Враховуйте ці ризики.

Сподобалась стаття автора? Підписуйтесь на його акаунт вгорі сторінки, щоб отримувати сповіщення про нові публікації на пошту.

👍ПодобаєтьсяСподобалось6
До обраногоВ обраному5
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Unbound (recursive proxy)

Анбаунд — це, все таки, днс ресолвер, а не проксі.

З власного досвіду (робив подібну штуку багато-багато років тому), то такий блокувальник реклами буде працювати дуже обмежено. Більшість хитрожопих сервісів зараз або фетчать рекламу з тих же хостів, що й сам сервіс (google.com чи instagram.com, наприклад) чи працюють напряму з айпі адресою в обхід всяких днс (більшість реклами в іграх та додатках, наприклад). Для жодного з цих варіантів немає універсального способу блокування реклами. Тобто можна взагалі заблокувати специфічні хости на рівні роутера (я заблокував тікток, інтсграм, твіттер багато років тому, бо це ще гірше ніж реклама, це цифровий рак), але все ж будуть виключення й такі радикальні міри не кожному будуть по смаку.

Щодо реклами в інстаграм, тіток (і скорше всього ютуб також) — так, це не допоможе, бо вона глибоко вшита в застосунок і майже дорівнює звичайному контенту. Про це, до речі, теж писав Богдан у своїй статті.
І, логічно, нативна реклама в застосунку теж не зникне (наприклад як просування товарів в розетці або спам пушами в уклоні).
Але реклама в застосунках, що показується за допомогою стороннього АПІ — блокується, я це перевіряв в багатьох застосунках.

Можна використовувати браузерний блокувальник + Ukrainian Filters (github.com/ukrainianfilters/lists) + Pi-Hole, щоб все ж блокувати рекламу з того ж хоста. На компʼютері списки підтримують усі популярні блокувальники github.com/...​rainianfilters/lists/wiki, а на телефоні трохи складніше, лише Ghostery підтримує списки і то скопійовані вручну

Використовую AdGuard + uBlock Origin, але а) це не працює на корпоративному залізі, де встановлені свої політики, які це забороняють; б) гугл/еппл видавлюють uBlock всіма правдами та неправдами; в) AdGuard — москальська програма і не можеш бути впевненим, що там десь не заритий собака; г) це все так і не працює на айфоні/айпеді.

Я дуже рідко відкриваю щось через телефонний браузер. Користуюсь переважно тільки десктопним з усіма можливими засобами блокування реклами і тд. Буває раз в тисячу років відкрию щось на телефоні і таке враження, що кількості реклами на сайтах стає все більше і більше, а корисного контенту менше і менше.

Олександре, перш за все дякую вам за службу. Також дякую за цю чудову статтю. Це саме те чого мені не вистачає в моїй. Радий що вона вас надихнула.

Поясніть будь ласка вашу схему для мене як для зовсім початківців. Що робить кожен елемент схеми?

Я також вперше дізнався про Unbound. Я так розумію що замість того щоб звертатись до якогось Google ви маєте DNS вдома? А така можна? Я це хочу!

Для чого порт форвардинг на роутері? Я завжди думав що cloudlare тунель заміняє переадресацію портів.

Для чого nginx? Ніколи з ним не працював.

От Apple: ніби і дбають про безпеку користувача запроваджуючи DoH, але одночасно за допомогою нього захищають рекламу в застосунках.

P.S. у своїй статті вже пізніше додав про встановлення та налаштування DNSCrypt.

На схемі показано, що коли пристрій хоче розрезолвити якийсь домен, він спочатку шукає мій домен і знаходить його у Cloudflare, що відповідає за таблицю DNS для мого домену, після чого по отриманому IP відправляє DNS запит. Cloudflare як проксі перенаправляє цей запит приховуючи IP моєї мережі. Далі цей запит отримує мій роутер і одразу перенаправляє на Nginx. Nginx — це вже проксі всередині моєї мережі, що також забезпечує шифрування вмісту. Далі цей запит йде на doh-server. Він в свою чергу перетворює DoH запит на звичайний DNS запит і звертається до pi-hole. Pi-Hole використовує Unbound в якості резолвера. Unbound звертається до авторизованих DNS серверів, що відповідають за TLD, потім за домен, сабдомен і тд.
Unbound може працювати і в режимі кешування запитів від резолверів, і як DNS резолвер самостійно. Проте він усе одно звертатиметься назовні, щоб отримати інформацію про те, який сервер зберігає DNS записи. Насправді я його поставив тільки через те, що це одна галочка в скрипті для PVE)
Тунелі від cloudflare дійсно мають сенс саме в цьому випадку, але так історично склалось, що я їх не використовую, І ще це трохи зменшує кількість елементів, які можуть вийти з ладу. Проте коли буде час, то можливо і з цим попрацюю)
Nginx в цій схемі зручний саме для мене, проте ніхто не забороняє використовувати вбудоване рішення для шифрування в doh-server. Nginx (а саме Nginx Proxy Manager) дозволяє мені заховати хости за допомогою проксі, забезпечити шифрування за допомогою згенерованого ssl сертифікату і адмініструвати це все в зручному інтерфейсі.

Сложно как-то. Я бы просто подняла туннель к домашнему гейтвею, где сконфигурила бы ад блокинг.

Але DoH у будь якому разі потрібний, без цього реклама в застосунках залишиться

DoH — бесплатная опция на моем домашнем гейтвее, как и ад блокинг, включаются за 2 минуты.

мені лишається вам позаздрити, в мене такої опції немає)

Ubiquiti гейтвеи от 200 долларов вроде
Информация прозвучала на правах рекламы xD

Підписатись на коментарі