仕組みで排除するYoutube狂い

仕組みで排除するYoutube狂い

はじめに

前回、Raspberry Pi に Ubuntu をインストールする記事を書きました。

今回この端末を使用して、特定の時間帯は Youtube へのアクセスを禁止するということを実現したいと思います。

なぜそうするのか

Youtube はその膨大なコンテンツから自分の好きなジャンルを好きなだけ視聴できます。私自身、昼のワイドショーを見るぐらいなら自分の好きなジャンルの動画を見ています。

ただ、それ故についつい長時間見すぎてしまう人も多いのではないでしょうか?

子供達も例外ではありません。ある程度制限を掛けないと、外出を拒否するほど夢中になってしまうこともしばしば…。

制限を掛けるために、今までに以下のことを試しました。

  • ルータの MAC アドレスフィルタリング機能で、該当デバイスの通信を遮断する

もちろんこのやり方でも目的は達成できるのですが、

  • 制限の実行、解除を手動で実施しなければならない
  • 全ての通信を遮断するのは不便なケースも

妻とも話し合った結果、何とか自動で制御できないか方法を探してみました。

そこで、今回は「物理的にネットワークを分離して特定のサイトに特定の時間帯にアクセスできないようにする」という記事を参考に、Raspberry Pi を使用して自動で行えるようにしました。

ネットワーク構成

現在、自宅のネットワーク構成はざっくり以下のようになっています。

home_network.png

NURO 光契約時に渡されたSGP200Wという ONU(ルーター機能付き)を挟んで、TP-Link のメッシュ Wi-Fi ユニットにであるDeco M5をアクセスポイントとして自宅の Wi-Fi の電波を拡張しています。

そのメッシュ Wi-Fi 配下に、Youtube に接続する端末である Fire TV Stick と LG のLG UJ630Aがあります(このテレビは有線接続)

この ONU と Deco M5 の間にルーターと DNS サーバ、DHCP サーバの機能を追加した Raspberry Pi を挟み、ネットワークのセグメントを物理的に分離します。

DNS とはドメイン名を IP アドレスに変換するシステム、DHCP はネットワーク上のデバイスに IP アドレスを自動的に割り当てるシステムです。

after_home_network.png

Raspberry Pi(以降 rasb4 と呼ぶ)の USB3.0 ポートに USB Lan ポートを追加し、ONU 側をメッシュ Wi-Fi 側のネットワークを分離します。

USB Lan ポートはuniというメーカーのものを使用しています。

dnsmasq を使用して USB Lan ポート側の DNS サーバを構築する

dnsmasq とは、ネットワークでの DNS と DHCP の管理を行うためのソフトウェアです。

ここではオンボード LAN 側を上位ネットワーク(192.168.10.0/24)、USB Lan ポート側を下位ネットワーク(192.168.20.0/24)としてセグメントを分割します。

設定が完了するまで下位ネットワークの端末はインターネットに接続できなくなるので注意してください。

dnsmasq のインストール

まずは dnsmasq をインストールします。

$ sudo apt -y install dnsmasq

# ステータスを確認
$ sudo systemctl status dnsmasq

× dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Sat 2023-12-23 20:52:15 JST; 13h ago
    Process: 709 ExecStartPre=/etc/init.d/dnsmasq checkconfig (code=exited, status=0/SUCCESS)
    Process: 767 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, status=2)
        CPU: 113ms

Dec 23 20:52:14 myhost systemd[1]: Starting dnsmasq - A lightweight DHCP and caching DNS server...
Dec 23 20:52:15 myhost dnsmasq[767]: dnsmasq: failed to create listening socket for port 53: Address already in use
Dec 23 20:52:15 myhost dnsmasq[767]: failed to create listening socket for port 53: Address already in use
Dec 23 20:52:15 myhost systemd[1]: dnsmasq.service: Control process exited, code=exited, status=2/INVALIDARGUMENT
Dec 23 20:52:15 myhost dnsmasq[767]: FAILED to start up
Dec 23 20:52:15 myhost systemd[1]: dnsmasq.service: Failed with result 'exit-code'.
Dec 23 20:52:15 myhost systemd[1]: Failed to start dnsmasq - A lightweight DHCP and caching DNS server.

インストール直後の dnsmasq のステータスを確認するとサービスの起動に失敗しているのが分かります。

メッセージを見ると、どうやら 53 番ポートが既に使用されているようです。

53 番ポートが何のプロセスで使用されているのか確認します。

# 53番ポートを使用中のプロセスを確認
$ sudo lsof -i :53
COMMAND   PID            USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
systemd-r 647 systemd-resolve   13u  IPv4  19702      0t0  UDP localhost:domain
systemd-r 647 systemd-resolve   14u  IPv4  19703      0t0  TCP localhost:domain (LISTEN)

PID が 647 のプロセスが既に利用中のようです。systemd-resolvedは Linux に組み込まれているネットワーク名前解決を管理するためのデーモン(バックグラウンドで動作するプログラム)です。

一旦この問題は放置して dnsmasq の設定ファイルを作成します。

設定ファイルの編集

/etc/dnsmasq.confの末尾に以下を追記します。必要であればバックアップを作成しておきます。

"/etc/dnsmasq.conf"
$ sudo vim /etc/dnsmasq.conf

# dnsmasqがリッスンするIPアドレスを指定
listen-address=127.0.0.1,192.168.20.1
# listen-addressで指定されたインターフェースにのみバインドすることを強制
bind-interfaces
# プライベートIPアドレスの逆引き要求を上位サーバーに問い合わせない
bogus-priv
# /etc/hostsファイルの内容を無視する
no-hosts
# 割り当てるIPアドレスの範囲を指定する(リース時間は12時間)
dhcp-range=192.168.20.2,192.168.20.254,12h
# 割り当てるサブネットマスクを指定
dhcp-option=option:netmask,255.255.255.0
# 割り当てるデフォルトゲートウェイを指定
dhcp-option=option:router,192.168.20.1
# 割り当てるDNSサーバのIPアドレスを指定
dhcp-option=option:dns-server,192.168.20.1
# カスタマイズや拡張設定のディレクトリを指定
conf-dir=/etc/dnsmasq.d
# DNSレコードの最大生存時間(TTL)を最大300秒に指定
max-ttl=300

設定の詳細を見ていきます。

# dnsmasqがリッスンするIPアドレスを指定
listen-address=127.0.0.1,192.168.20.1

listen-addressでは dnsmasq が DNS クエリを処理を受け入れる設定をしています。

DNS クエリとは名前解決の問い合わせであり、IP アドレスに関連するドメイン名などを DNS サーバに問い合わせることです。

127.0.0.1ループバックアドレスという特殊なアドレスで、システム自身を表す IP アドレスです。

このアドレスをリッスンアドレスに含めることで、同じマシン上で実行されているプロセス(例えば、ローカルで動作するウェブブラウザーやパッケージの更新コマンドの名前解決)が dnsmasq を介して DNS クエリを行えるようになります。

この設定に192.168.20.1を加えることで、このネットワークに接続されている端末の DNS クエリを処理できるようになります。

上位ネットワーク192.168.10.1はルーターの DHCP 機能を使用するので指定しません。

# 割り当てるサブネットマスクを指定
dhcp-option=option:netmask,255.255.255.0
# 割り当てるデフォルトゲートウェイを指定
dhcp-option=option:router,192.168.20.1
# 割り当てるDNSサーバのIPアドレスを指定
dhcp-option=option:dns-server,192.168.20.1

この設定では、USB LAN ポートに指定した IP アドレスを指定します。

# カスタマイズや拡張設定のディレクトリを指定
conf-dir=/etc/dnsmasq.d

追加の設定ファイルを配置するディレクトリを指定します。今回はこのディレクトリに名前解決をブロックするファイルを配置します。

# DNSレコードの最大生存時間(TTL)を最大300秒に指定
max-ttl=300

キャッシュされた古い情報が長時間使用されるのを防ぎます。

IP アドレスの指定

前回の記事で IP アドレスを固定化するために/etc/netplan/99_config.yamlを編集しましたが、USB Lan ポートに IP アドレスを設定するための記述を追加します。

"/etc/netplan/99_config.yaml"
# NICの情報を確認
$ ip a | grep -E "^[0-9]*:"
# ループバックアドレス
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
# オンボードのNIC
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
# USB Lanポート(これに対する設定を追加)
3: enx00e04c68054f: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
# Wi-Fi
4: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000

$ sudo vim /etc/netplan/99_config.yaml

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: no
      addresses:
        - 192.168.10.9/24
      routes:
        - to: default
          via: 192.168.10.1
      nameservers:
        addresses: [192.168.0.1, 8.8.8.8]
    # 追加
    enx00e04c68054f:
      dhcp4: no
      addresses:
        # USB Lanポート側のアドレスを指定
        - 192.168.20.1/24

設定を反映させます。

$ sudo netplan apply

$ ip a

# 省略

# IPアドレスが割り当てられていることを確認
3: enx00e04c68054f: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:e0:4c:68:05:4f brd ff:ff:ff:ff:ff:ff
    inet 192.168.20.1/24 brd 192.168.20.255 scope global enx00e04c68054f
       valid_lft forever preferred_lft forever
    inet6 fe80::2e0:4cff:fe68:54f/64 scope link
       valid_lft forever preferred_lft forever
4: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000

systemd-resolved の停止と dnsmasq の実行

53 番ポートが競合していた問題を解決します。

ここでsystemd-resolvedを停止すると、rasb4 の名前解決が出来なくなり rasb4 自身のインターネット通信が一旦出来なくなります。

# systemd-resolved を停止
$ sudo systemctl stop systemd-resolved
# サービスの自動起動も停止
$ sudo systemctl disable systemd-resolved

# ステータスの確認
$ sudo systemctl status systemd-resolved
○ systemd-resolved.service - Network Name Resolution # サービスが停止されている
    Loaded: loaded (/lib/systemd/system/systemd-resolved.service; disabled; vendor preset: enabled)
    Active: inactive (dead)
        Docs: man:systemd-resolved.service(8)
            man:org.freedesktop.resolve1(5)
            https://www.freedesktop.org/wiki/Software/systemd/writing-network-configuration-managers
            https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients

# dnsmasq を起動
$ sudo systemctl start dnsmasq

# ステム起動時に自動的に起動するように設定
$ sudo systemctl enable dnsmasq

# dnsmasq が正常に起動したことを確認
$ sudo systemctl status dnsmasq
● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
     Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2023-12-28 06:00:01 JST; 1h 7min ago
    Process: 1458 ExecStartPre=/etc/init.d/dnsmasq checkconfig (code=exited, status=0/SUCCESS)
    Process: 1467 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, status=0/SUCCESS)
    Process: 1477 ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf (code=exited, status=0/SUCCESS)
   Main PID: 1476 (dnsmasq)
      Tasks: 1 (limit: 4423)
     Memory: 608.0K
        CPU: 333ms
     CGroup: /system.slice/dnsmasq.service
             └─1476 /usr/sbin/dnsmasq -x /run/dnsmasq/dnsmasq.pid -u dnsmasq -7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new --local-service --trust-anchor=.,20326,8,2,e06d44b80b8f1d39a95c0b0d7c65d08458e8804>

Dec 28 06:00:01 myhost dnsmasq-dhcp[1476]: DHCP, IP range 192.168.20.2 -- 192.168.20.254, lease time 12h
Dec 28 06:00:01 myhost dnsmasq[1476]: using nameserver 192.168.10.1#53
Dec 28 06:00:01 myhost dnsmasq[1476]: reading /etc/resolv.conf
Dec 28 06:00:01 myhost dnsmasq[1476]: using nameserver 192.168.10.1#53
Dec 28 06:00:01 myhost dnsmasq[1476]: using nameserver 192.168.10.1#53
Dec 28 06:00:01 myhost dnsmasq[1476]: using nameserver 8.8.8.8#53
Dec 28 06:00:01 myhost dnsmasq[1476]: cleared cache
Dec 28 06:00:01 myhost systemd[1]: Started dnsmasq - A lightweight DHCP and caching DNS server.
Dec 28 06:52:56 myhost dnsmasq-dhcp[1476]: DHCPREQUEST(enx00e04c68054f) 192.168.20.48 d8:07:b6:29:c8:c8
Dec 28 06:52:56 myhost dnsmasq-dhcp[1476]: DHCPACK(enx00e04c68054f) 192.168.20.48 d8:07:b6:29:c8:c8 deco-M5

ここまで設定した段階で、まだインターネットに繋がりませんが下位ネットワークの端末に DHCP 機能で IP アドレスが割り振られていると思います。

ip-status.png

次に、rasb4 の名前解決が出来るように設定します。

resolv.conf の編集

systemd-resolved を停止したので既存のresolv.confを削除し、DNS サーバのアドレスを直接指定します。

/etc/resolv.conf/run/systemd/resolve/stub-resolv.confのシンボリックリンクとなっています。

$ ls -lh /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Aug  8 00:23 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf

# 既存のファイルを削除
$ sudo rm /etc/resolv.conf

# 新規でファイルを作成
$ sudo vim /etc/resolv.conf

# プライマリDNSサーバを指定(ルーター)
nameserver 192.168.10.1
# セカンダリDNSサーバを指定
nameserver 8.8.8.8

sudo apt updateなどを実行してインターネットと通信できるか確認してください。

2 つの NIC の間でパケットを転送する

/etc/sysctl.confを編集して IPv4 のパケット転送(IP フォワーディング)を有効にします。

$ sudo vim /etc/sysctl.conf

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1 # コメントアウトを外す

# 設定の変更を反映させる
$ sudo sysctl -p
net.ipv4.ip_forward = 1

この設定をすることで、システムは IPv4 パケットを他のネットワークインターフェースへ転送することができるようになります。

ここまでの設定で下位ネットワークがインターネットに繋がることを確認します。

ドメインのブロック

インターネットへの接続が確認できたら、名前解決をブロックするドメインを記述したファイルを作成します。

今回そのファイルはホームディレクトリ配下に作成し、そのシンボリックリンクを/etc/dnsmasq.confで指定したディレクトリに配置します(今回は/etc/dnsmasq.d)。

$ mkdir deny_web_site

$ echo "address=/youtube.com/" >> deny_web_site/youtube.conf

# 内容を確認
$ cat deny_web_site/youtube.conf
address=/youtube.com/

# シンボリックリンクを作成
$ sudo ln -s deny_web_site/youtube.conf /etc/dnsmasq.d/youtube.conf

# サービスを再起動
$ sudo systemctl restart dnsmasq

下位のネットワーク側の端末で、Youtube へのアクセスのみがブロックされていることを確認します。

block-youtube.png

設定ファイルのリンクを解除する場合は以下のコマンドを実行します。

# シンボリックリンクの解除
$ sudo unlink /etc/dnsmasq.d/youtube.conf

# サービスを再起動
$ sudo systemctl restart dnsmasq

cron の設定

ここまでで Youtube へのアクセスをブロックすることができました。

最後にブロックするドメインを記述した設定ファイルを、指定した時間で作成、削除する cron を設定します。

$ sudo crontab -e

# -f オプションを追加し、既にファイルが存在していた場合でも強制的に上書きする
0 0,20 * * * ln -sf /home/[your user]/deny_web_site/youtube.conf && sudo systemctl restart dnsmasq
0 6,21 * * * unlink /etc/dnsmasq.d/youtube.conf && sudo systemctl restart dnsmasq

cron が指定した時間に実行されているか確認するには以下のコマンドを実行します。

$ sudo cat /var/log/syslog | grep CRON | grep youtube.conf
Dec 26 00:00:01 myhost CRON[1346]: (root) CMD (ln -sf /home/[your user]/deny_web_site/youtube.conf /etc/dnsmasq.d/youtube.conf && systemctl restart dnsmasq)
Dec 26 06:00:01 myhost CRON[1668]: (root) CMD (unlink /etc/dnsmasq.d/youtube.conf && systemctl restart dnsmasq)
Dec 26 20:00:01 myhost CRON[2911]: (root) CMD (ln -sf /home/[your user]/deny_web_site/youtube.conf /etc/dnsmasq.d/youtube.conf && systemctl restart dnsmasq)
Dec 26 21:00:01 myhost CRON[3036]: (root) CMD (unlink /etc/dnsmasq.d/youtube.conf && systemctl restart dnsmasq)
Dec 27 00:00:01 myhost CRON[3324]: (root) CMD (ln -sf /home/[your user]/deny_web_site/youtube.conf /etc/dnsmasq.d/youtube.conf && systemctl restart dnsmasq)
Dec 27 06:00:01 myhost CRON[3572]: (root) CMD (unlink /etc/dnsmasq.d/youtube.conf && systemctl restart dnsmasq)

現在の問題点

Youtube をブロックすることが出来ました。しかし、これは新しいセッションをブロックする場合で、既に視聴中の動画はブロックすることは出来ません。

iptablesで MAC アドレスを利用して通信を遮断する方法も試してみましたが、既に確立された接続(既存のセッション)にはブロックされませんでした。

また、再起動時に dnsmasq の自動起動が失敗しているという問題点も発覚しました。調べたところ、起動時に IP アドレスやネットワークの設定が行われる前に dnsmasq が起動していることが問題と考えられます。

これらの問題は設定で解決できるかもしれないので、改めて調査して検証しようと思います。

番外編

今回 DNS サーバ及びルーターとして設置した rasb4 の負荷をモニターしたい時に、nmon というツールを利用すると便利だったので紹介します。

nmon は、CPU、メモリー、ディスク I/O、ネットワーク等、システム・リソースの使用状況をリアルタイムに収集してくれるツールです。

$ sudo apt -y install nmon

# nmonを起動
$ nmon

nmon.png

起動したら続けてキーボードよりc m dと入力して、CPU、メモリ、ディスクの利用状況を表示させます。

nmon2.png

これで端末に現在どれだけの負荷が掛かっているのかリアルタイムにモニターできます。

Raspberry Pi は消費電力も低く最近ではスペックも向上している為、手軽で個人で利用する分には十分ですが用途によって負荷が気になる場合は別途マシンを用意したほうが良いかもしれません。

その後

現在の問題点の項でも述べましたが、dnsmasq の自動起動が失敗している件についてはサービスの起動が成功しているときもあれば、失敗している時もあるということが分かりました。

この原因については分かりませんでしたが、サービスの起動に失敗した場合に再度実行する処理を設定すれば全て解決しそうです。

その方法として単純に以下のスクリプトを実行することを考えました。

  • サービスのステータスを判定する
  • 起動していなければ起動処理
  • cron の実行は起動時に一度だけにする(若しくは定期的に監視する)

まぁでも、正直ダルいな…と思ったのでもっと良い方法が無いか調べたら良い方法がありました。

サービスファイルを修正する

サービスファイルの[Service]という項目には、サービス失敗時の再起動や遅延時間を指定することができます。

/lib/systemd/system/dnsmasq.serviceというファイルに以下の内容を追記します。

"/lib/systemd/system/dnsmasq.service"
# バックアップファイルを作成(念の為)
$ sudo cp /lib/systemd/system/dnsmasq.service /lib/systemd/system/dnsmasq.service.bk

# サービスファイルを編集する
$ sudo vim /lib/systemd/system/dnsmasq.service
#--------------------------------------------------

# [Service] ディレクティブ内の末尾に下記を追記する
Restart=on-failure
RestartSec=10s
#--------------------------------------------------

# systemd に変更を認識させる
$ sudo systemctl daemon-reload

# サービスの変更を反映させる
$ sudo systemctl restart dnsmasq

スクリプトを作成する

cron で実行するコマンドをシェルスクリプトに変更します。

$ mkdir cron_scripts

$ vim cron_scripts/allow_youtube.sh
#--------------------------------------------------
#!/bin/bash

# YouTubeへのアクセスを許可する設定を無効にする
unlink /etc/dnsmasq.d/youtube.conf

# dnsmasqを再起動する
systemctl restart dnsmasq
#--------------------------------------------------

$ vim cron_scripts/block_youtube.sh
#--------------------------------------------------
#!/bin/bash

# YouTubeへのアクセスを遮断する設定を有効にする
ln -sf /home/user01/deny_web_site/youtube.conf /etc/dnsmasq.d/youtube.conf
# マシンを再起動する
reboot
#--------------------------------------------------

$ sudo crontab -e
# m h  dom mon dow   command
0 0,17 * * * /home/user01/cron_scripts/block_youtube.sh >> /var/log/block_youtube.log 2>&1
0 6,21 * * * /home/user01/cron_scripts/allow_youtube.sh >> /var/log/allow_youtube.log 2>&1

最後に

単純にドメインの名前解決をブロックすれば YouTube の視聴も止めれると考えていましたが、一度接続されたセッションをブロック(動画視聴中のブロック)する方法としてルーターを再起動してしまうという方法しか採用できませんでした。

あまりネットワークのプロトコルには詳しくないのでもっと良い方法があるかもしれませんが、とりあえず時間を限定した Youtube ライフとなっています。