サロにぃのまったりブログ

技術全般を書くと思う。多分。

意外と簡単?自由研究がてらMikrotikのCHRでDNSフェイルオーバーする環境を作ってみた💫

今回は暇だったので(?)最近面白そうだなと思い触っていたMikrotikのCHRを使い、
DNSフェイルオーバー機能を持ったDNSキャッシュサーバーを作ってみました。

最近はコミュニティ管理で一切暇が無いのが正直なところですが、
あまりにも今週作業が気乗りしなかったので
大人の自由研究も兼ねて(?)こういったものを作ってみました。

■目次

■環境

  • Proxmox 8.4.6 (ミニPCに焼いて使用)
  • CHR 7.19.4
    • vCPU: 1 
    • メモリ: 256MB
    • ライセンスは無償ライセンス(勉強用で触っているため)

■やろうと思った背景

最近、自宅にAdGuard Homeをデプロイして使用し始めました。

広告ブロックではなく、危険サイトのブロックが目的のため、
検知頻度は正直少ないのですがLAN側にDNSサーバーを置くと
万が一DNSサーバーがダウンした際に名前解決ができず通信が止まるリスクも有るため
冗長化が必要だと感じAdGuardを2つデプロイし
KeepalivedでVIPを作り雑に冗長化させています。

我が家で利用中のAdGuard Home

ただ、さらに万が一のためにAdGuard Homeが両系ダウンしたタイミングでのみ
Public DNSへのフェイルオーバーをさせるといったことは
上位ルーターで使用しているUnifi含め難しく、
またクライアントによってはサブDNSへの試行までに時間がかかり、
柔軟な冗長化には限界があったためCHRを活用し改善する方法がないかと考えました。

■前提

  • CHRでDNSフェイルオーバーさせる場合はそこが単一障害点になることから
    VRRP含めた冗長化を行うことがベストですが今回は検証のため考慮しません。
  • 今回検証用に使うAdGuardはKeepalivedでStandbyに位置するAdGuardを使います。
    新しくデプロイしたほうが良さそうですが面倒だったのでやめました。
  • 通常の通信時は、クライアントはCHRにクエリを投げ、
    CHRが代理でAdGuard(10.10.110.10)で名前解決をしキャッシュ、
    AdGuardでの疎通不可等によって名前解決に失敗した場合に
    8.8.8.8, 8.8.4.4のPublicDNSに切り替えます。
  • 切り替え後、AdGuardが復旧した場合は再度AdGuardに自動で切り替えます。
  • 上位ルーターがダウンした場合などPublicDNSに切り替えようとしても
    名前解決ができない場合には瞬間的な切り替わりを
    繰り返す可能性もあるため一旦様子見させます。

 

■環境コンフィグ

最低限の設定を入れています。

[admin@MikroTik] > export
# 2025-08-17 12:11:19 by RouterOS 7.19.4
# system id = *******
#
/interface ethernet
set [ find default-name=ether1 ] disable-running-check=no
set [ find default-name=ether2 ] disable-running-check=no
set [ find default-name=ether3 ] disable-running-check=no
set [ find default-name=ether4 ] disable-running-check=no
/interface list
add name=WAN
add name=LAN
/ip pool
add name=dhcp_pool0 ranges=192.168.1.2-192.168.1.254
/ip dhcp-server
add address-pool=dhcp_pool0 interface=ether2 name=dhcp1
/interface list member
add interface=ether1 list=WAN
add interface=ether2 list=LAN
add interface=ether3 list=LAN
add interface=ether4 list=LAN
/ip address
add address=10.10.120.50/24 interface=ether1 network=10.10.120.0
add address=192.168.1.1/24 interface=ether2 network=192.168.1.0
/ip dhcp-client
add interface=ether1
/ip dhcp-server network
add address=192.168.1.0/24 dns-server=192.168.1.1 gateway=192.168.1.1
/ip dns
set allow-remote-requests=yes servers=10.10.110.10
/ip firewall filter
add action=accept chain=forward in-interface=ether2 out-interface=ether1
add action=accept chain=input connection-state=established,related
add action=accept chain=forward connection-state=established,related
add action=drop chain=forward log-prefix="DROP "
/ip firewall nat
add action=masquerade chain=srcnat out-interface=ether1
/ip route
add gateway=10.10.120.1 pref-src=0.0.0.0
/snmp community
add addresses=::/0
/system clock
set time-zone-name=Asia/Tokyo
/system logging
add topics=dhcp
/system ntp client
set enabled=yes
/system ntp client servers
add address=ntp.nict.jp

DNSキャッシュサーバとして使用するコンフィグは以下の部分です。

/ip dns
set allow-remote-requests=yes servers=10.10.110.10

allow-remote-requests=yesを入れることで、CHRがクライアントから受信した
DNSクエリを代理で処理・応答しキャッシュまで行います。

今はAdGuardにクエリが向く設定になっています。

インターネットに出る場合はether2→ether1→インターネットの
通信経路になるように設定していて、DNSクエリでは
クライアントは192.168.1.1宛にクエリを送ることでCHRに処理させられます。

検証用のクライアントはether2で仮想的につながっているUbuntuを使用します。

■どう実装するか

上述の通り、今はCHRをDNSキャッシュサーバとして動作する設定になっています。

ただ、残念なことにAdGuardがダウンしたらPublicDNSに
自動で切り替える設定は標準では搭載されていません。

CHRではスクリプトを使い動作を拡張させることができるため、
今回はそれを使ってDNSフェイルオーバー機能を実装することにしました。

■実装イメージ

今回の実装に際し、そもそも実現できるのか調べるため
以下スレッドを参考にさせていただきました。ありがとうございます。

DNS Failover - #8 by mt99 - General - MikroTik community forum

今回の実装では、netwatchでAdGuardに定期的(5秒ごと)にクエリを送り
ヘルスチェックを行う構成にします。

クエリを送るドメインは正直どこでもいいのですが、
今回はAdGuard側に専用のDNSレコードを登録しそのクエリを投げたいと思います。

netwatchではUp/Downそれぞれでスクリプトが定義できるため、
Up/Downでトリガーされるスクリプトをそれぞれ作成することにしました。

ただ、この方法ではスクリプトの数が多くなりメンテナンス時の
変数の修正が面倒に思えたので起動時に実行させるスクリプトを設定し、
必要な変数をグローバル変数として設定するようにしました。

■boot_init (起動時初期化用)

:log info "STARTING BOOT_INIT SCRIPT...";

:global MainDNS 10.10.110.10;
:global FailoverDNS 8.8.8.8,8.8.4.4;
:global GatewayIP 10.10.120.1;
:global MainDNSHealthCheckQuery "dns-healthcheck.local"
:global BootFlag true;

:log info "SUCCESSFUL BOOT_INIT SCRIPT!";

ちなみにGatewayIPは上位ルーターへのヘルスチェック用変数ですが、
現時点では使用していません。

dns-failover (ヘルスチェック失敗時用)

:global BootFlag;

if ($BootFlag=false) do={
:log info "BOOT_INIT script was not started... Skipping try dns fail over...";
return
}

:global MainDNS;
:global FailoverDNS;
:local currentDNS;
:set currentDNS [/ip dns get servers];

if ($currentDNS=$FailoverDNS) do={
:log info "Already using failback dns.";
:return
} else={
:log info "Switching MainDNS To Failback DNS...";
:do {
:local dnsAnswer [:resolve domain-name="google.com" server=($FailoverDNS->0)];
:log info "Failback DNS health-check successful."
} on-error={
:log error "Failback DNS health-check failed."
:return
}
/ip dns set servers $FailoverDNS;
:log info ("DNS Failover successful." . $currentDNS . "->" . $FailoverDNS)
}

dns-failover_resolve (復旧時用スクリプト)

:log info "STARTING DNS RESOLVE SCRIPT...";

:global MainDNS;
:global FailoverDNS;
:global MainDNSHealthCheckQuery;
:global DNSHealthCheckStatus;
:local currentDNS;
:set currentDNS [/ip dns get servers];

if ($currentDNS=$MainDNS) do={
:log info "Already using Main dns.";
:return
} else={
:log info "Switching Failover DNS to MainDNS...";
:do {
:local dnsAnswer [:resolve domain-name=$MainDNSHealthCheckQuery server=($MainDNS)];
:log info "MainDNS health-check successful."
} on-error={
:log error "MainDNS health-check failed."
:return
}
/ip dns set servers $MainDNS;
:log info ("DNS Failover successful." . $currentDNS . "->" . $MainDNS)
}

:log info "ENDING DNS FAILOVER RESOLVE SCRIPT..."

 

そしてここまで作って切り替え時のヘルスチェックに失敗したら
再試行が行われないことに気づいたのでヘルスチェック後に
毎回実行されるスクリプトに以下を登録しました。

dns_failover_checker (毎ヘルスチェック時に実行)

:global BootFlag;

if ($BootFlag=false) do={
:log info "BOOT_INIT script is not started... SKIPPING..."
:return
}

:global MainDNS;
:global FailoverDNS;
:global MainDNSHealthCheckQuery;
:local currentDNS;
:local FailoverDNSHealthCheckQuery "google.com";
:set currentDNS [/ip dns get servers];

if ($status="up") do={
if ($MainDNS!=$currentDNS) do={
:log info "DETECTED DNS STATUS MISMATCH..."
:log info ("DNS Healthcheck Status=" . $status . " CurrentDNS="$currentDNS );
:log info "Switching Failover DNS to MainDNS...";
:do {
:local dnsAnswer [:resolve domain-name=$MainDNSHealthCheckQuery server=($MainDNS)];
:log info "MainDNS health-check successful."
} on-error={
:log error "MainDNS health-check failed."
:return
}
/ip dns set servers $MainDNS;
:log info ("DNS Failover successful." . $currentDNS . "->" . $MainDNS)
}
} else={
if ($FailoverDNS!=$currentDNS) do={
:log info ("DETECTED DNS STATUS MISMATCH...")
:log info ("DNS Healthcheck Status=" . $status . " CurrentDNS="$currentDNS );
:log info "Switching Failover DNS to MainDNS...";
:do {
:local dnsAnswer [:resolve domain-name=$FailoverDNSHealthCheckQuery server=($FailoverDNS->0)];
:log info "Failover health-check successful."
} on-error={
:log error "Failover health-check failed."
:return
}
/ip dns set servers $FailoverDNS;
:log info ("DNS Failover successful." . $currentDNS . "->" . $FailoverDNS)
}
}

 

■完成形のConfig

[admin@MikroTik] > /export
# 2025-08-17 14:51:53 by RouterOS 7.19.4
# system id = 8BE7NQY0RBG
#
/interface ethernet
set [ find default-name=ether1 ] disable-running-check=no
set [ find default-name=ether2 ] disable-running-check=no
set [ find default-name=ether3 ] disable-running-check=no
set [ find default-name=ether4 ] disable-running-check=no
/interface list
add name=WAN
add name=LAN
/ip pool
add name=dhcp_pool0 ranges=192.168.1.2-192.168.1.254
/ip dhcp-server
add address-pool=dhcp_pool0 interface=ether2 name=dhcp1
/interface list member
add interface=ether1 list=WAN
add interface=ether2 list=LAN
add interface=ether3 list=LAN
add interface=ether4 list=LAN
/ip address
add address=10.10.120.50/24 interface=ether1 network=10.10.120.0
add address=192.168.1.1/24 interface=ether2 network=192.168.1.0
/ip dhcp-client
add interface=ether1
/ip dhcp-server network
add address=192.168.1.0/24 dns-server=192.168.1.1 gateway=192.168.1.1
/ip dns
set allow-remote-requests=yes servers=10.10.110.10
/ip firewall filter
add action=accept chain=forward in-interface=ether2 out-interface=ether1
add action=accept chain=input connection-state=established,related
add action=accept chain=forward connection-state=established,related
add action=drop chain=forward log=yes log-prefix="DROP "
/ip firewall nat
add action=masquerade chain=srcnat out-interface=ether1
/ip route
add gateway=10.10.120.1 pref-src=0.0.0.0
/snmp community
add addresses=::/0
/system clock
set time-zone-name=Asia/Tokyo
/system logging
add topics=dhcp
/system ntp client
set enabled=yes
/system ntp client servers
add address=ntp.nict.jp
/system scheduler
add name=boot_init on-event=boot_init policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-time=startup
/system script
add dont-require-permissions=yes name=dns-failover owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":global BootFlag;\
\n\
\nif (\$BootFlag=false) do={\
\n :log info \"BOOT_INIT script was not started... Skipping try dns fail over...\";\
\n return\
\n}\
\n\
\n:global MainDNS;\
\n:global FailoverDNS;\
\n:local currentDNS;\
\n:set currentDNS [/ip dns get servers];\
\n\
\nif (\$currentDNS=\$FailoverDNS) do={\
\n :log info \"Already using failback dns.\";\
\n :return \
\n} else={\
\n :log info \"Switching MainDNS To Failback DNS...\";\
\n :do {\
\n :local dnsAnswer [:resolve domain-name=\"google.com\" server=(\$FailoverDNS->0)];\
\n :log info \"Failback DNS health-check successful.\"\
\n } on-error={\
\n :log error \"Failback DNS health-check failed.\"\
\n :return\
\n } \
\n /ip dns set servers \$FailoverDNS;\
\n :log info (\"DNS Failover successful.\" . \$currentDNS . \"->\" . \$FailoverDNS)\
\n} "
add dont-require-permissions=yes name=dns-failover_resolve owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":log info \"STARTING DNS RESOLVE \
SCRIPT...\";\
\n\
\n:global MainDNS;\
\n:global FailoverDNS;\
\n:global MainDNSHealthCheckQuery;\
\n:global DNSHealthCheckStatus;\
\n:local currentDNS;\
\n:set currentDNS [/ip dns get servers];\
\n\
\nif (\$currentDNS=\$MainDNS) do={\
\n :log info \"Already using Main dns.\";\
\n :return \
\n} else={\
\n :log info \"Switching Failover DNS to MainDNS...\";\
\n :do {\
\n :local dnsAnswer [:resolve domain-name=\$MainDNSHealthCheckQuery server=(\$MainDNS)];\
\n :log info \"MainDNS health-check successful.\"\
\n } on-error={\
\n :log error \"MainDNS health-check failed.\"\
\n :return\
\n } \
\n /ip dns set servers \$MainDNS;\
\n :log info (\"DNS Failover successful.\" . \$currentDNS . \"->\" . \$MainDNS)\
\n} \
\n\
\n:log info \"ENDING DNS FAILOVER RESOLVE SCRIPT...\""
add dont-require-permissions=yes name=boot_init owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":log info \"STARTING BOOT_INIT SCRIPT...\";\
\n\
\n:global MainDNS 10.10.110.10;\
\n:global FailoverDNS 8.8.8.8,8.8.4.4;\
\n:global GatewayIP 10.10.120.1;\
\n:global MainDNSHealthCheckQuery \"dns-healthcheck.local\"\
\n:global BootFlag true;\
\n\
\n:log info \"SUCCESSFUL BOOT_INIT SCRIPT!\";"
add dont-require-permissions=yes name=dns_failover_checker owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":global BootFlag;\
\n\
\nif (\$BootFlag=false) do={\
\n :log info \"BOOT_INIT script is not started... SKIPPING...\"\
\n :return\
\n}\
\n\
\n:global MainDNS;\
\n:global FailoverDNS;\
\n:global MainDNSHealthCheckQuery;\
\n:local currentDNS;\
\n:local FailoverDNSHealthCheckQuery \"google.com\";\
\n:set currentDNS [/ip dns get servers];\
\n\
\nif (\$status=\"up\") do={\
\n if (\$MainDNS!=\$currentDNS) do={\
\n :log info \"DETECTED DNS STATUS MISMATCH...\"\
\n :log info (\"DNS Healthcheck Status=\" . \$status . \" CurrentDNS=\"\$currentDNS );\
\n :log info \"Switching Failover DNS to MainDNS...\";\
\n :do {\
\n :local dnsAnswer [:resolve domain-name=\$MainDNSHealthCheckQuery server=(\$MainDNS)];\
\n :log info \"MainDNS health-check successful.\"\
\n } on-error={\
\n :log error \"MainDNS health-check failed.\"\
\n :return\
\n } \
\n /ip dns set servers \$MainDNS;\
\n :log info (\"DNS Failover successful.\" . \$currentDNS . \"->\" . \$MainDNS) \
\n }\
\n} else={\
\n if (\$FailoverDNS!=\$currentDNS) do={\
\n :log info (\"DETECTED DNS STATUS MISMATCH...\")\
\n :log info (\"DNS Healthcheck Status=\" . \$status . \" CurrentDNS=\"\$currentDNS );\
\n :log info \"Switching Failover DNS to MainDNS...\";\
\n :do {\
\n :local dnsAnswer [:resolve domain-name=\$FailoverDNSHealthCheckQuery server=(\$FailoverDNS->0)];\
\n :log info \"Failover health-check successful.\"\
\n } on-error={\
\n :log error \"Failover health-check failed.\"\
\n :return\
\n } \
\n /ip dns set servers \$FailoverDNS;\
\n :log info (\"DNS Failover successful.\" . \$currentDNS . \"->\" . \$FailoverDNS)\
\n }\
\n}"
/tool netwatch
add disabled=no dns-server=10.10.110.10 down-script=dns-failover host=dns-healthcheck.local interval=5s test-script=dns_failover_checker type=dns up-script=dns-failover_resolve

スクリプト以外の変更点としては以下の2点。

■boot_initを起動時にトリガーするように設定する

/system scheduler
add name=boot_init on-event=boot_init policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-time=startup

■netwatchでDNSクエリのヘルスチェックとスクリプトの実行を行う

/tool netwatch
add disabled=no dns-server=10.10.110.10 down-script=dns-failover host=dns-healthcheck.local interval=5s test-script=dns_failover_checker type=dns up-script=dns-failover_resolve

■動作チェック

■CHRを再起動しBOOT_INITが動作するか確認する

2025-08-17 13:41:41 system,info router rebooted by ssh:admin@192.168.10.108
2025-08-17 13:41:41 script,info STARTING BOOT_INIT SCRIPT...
2025-08-17 13:41:41 script,info SUCCESSFUL BOOT_INIT SCRIPT!

開始・成功ログが出てるので大丈夫そう。

GUIからもグローバル変数が格納されていることを確認できた。

■AdGuardをシャットダウンしフェイルオーバーするか確認する

(1)ヘルスチェック用レコードをCHR宛にdigし応答が返ってくることを確認

/ip dns
set allow-remote-requests=yes servers=10.10.110.10

(2)AdGuard(Standby)をShutdown

2025-08-17 14:10:57 netwatch,info event down [ type: dns, host: dns-healthcheck.local ]
2025-08-17 14:10:57 script,info Switching MainDNS To Failback DNS...
2025-08-17 14:10:57 script,info DETECTED DNS STATUS MISMATCH...
2025-08-17 14:10:57 script,info 10.10.110.10
2025-08-17 14:10:57 script,info Switching Failover DNS to MainDNS...
2025-08-17 14:10:57 script,info Failover health-check successful.
2025-08-17 14:10:57 script,info DNS Failover successful.10.10.110.10->8.8.8.8;DNS Failover successful.10.10.110.10->8.8.4.4
2025-08-17 14:10:57 script,info Failback DNS health-check successful.
2025-08-17 14:10:57 system,info dns changed by netwatch:type: dns, host: dns-healthcheck.local/script:dns_failover_checker (/ip dns set servers=8.8.8.8,8.8.4.4)
2025-08-17 14:10:57 script,info DNS Failover successful.10.10.110.10->8.8.8.8;DNS Failover successful.10.10.110.10->8.8.4.4
2025-08-17 14:10:57 system,info dns changed by netwatch:type: dns, host: dns-healthcheck.local/script:dns-failover (/ip dns set servers=8.8.8.8,8.8.4.4)

二重に切り替えようとしていますが正直原因がわからず。。。

今回はこれで動いたので良しとしてます。

 

確認としてPublic DNS切り替え後クライアントからAdGuardの
ヘルスチェック用レコードでDigしましたが名前解決はできなかったことから、
ちゃんと切り替わっていることが確認できます。

(3)AdGuard(Standby)を起動

2025-08-17 14:11:16 netwatch,info event up [ type: dns, host: dns-healthcheck.local ]
2025-08-17 14:11:16 script,info STARTING DNS RESOLVE SCRIPT...
2025-08-17 14:11:16 script,info Switching Failover DNS to MainDNS...
2025-08-17 14:11:16 script,info DETECTED DNS STATUS MISMATCH...
2025-08-17 14:11:16 script,info 8.8.8.8;8.8.4.4
2025-08-17 14:11:16 script,info Switching Failover DNS to MainDNS...
2025-08-17 14:11:16 script,info MainDNS health-check successful.
2025-08-17 14:11:16 script,info DNS Failover successful.8.8.8.8->10.10.110.10;DNS Failover successful.8.8.4.4->10.10.110.10
2025-08-17 14:11:16 script,info ENDING DNS FAILOVER RESOLVE SCRIPT...
2025-08-17 14:11:16 system,info dns changed by netwatch:type: dns, host: dns-healthcheck.local/script:dns-failover_resolve (/ip dns set servers=10.10.110.10)
2025-08-17 14:11:16 script,info MainDNS health-check successful.
2025-08-17 14:11:16 script,info DNS Failover successful.8.8.8.8->10.10.110.10;DNS Failover successful.8.8.4.4->10.10.110.10
2025-08-17 14:11:16 system,info dns changed by netwatch:type: dns, host: dns-healthcheck.local/script:dns_failover_checker (/ip dns set servers=10.10.110.10)

こちらも二重に切り替わっていますが一旦動作しているのでヨシ!(現場猫)

クライアント側でも再度digでヘルスチェック用レコードが返ることを確認できました。

ただ、一点気になったポイントとして名前解決ができなかった
ヘルスチェック用ドメインは下記の通り、NEGATIVEフラグが付与されていました。

[admin@MikroTik] /ip/dns/cache/all> print
Flags: N - NEGATIVE
Columns: NAME, TYPE, DATA, TTL
# NAME TYPE DATA TTL
0 www.expedia.co.jp.edgekey.net CNAME e6238.x.akamaiedge.net. 5h59m3s
1 x.akamaiedge.net SOA n0x.akamaiedge.net. 13m52s
2 ntp.nict.jp A 133.243.238.163 19h16m16s
3 ntp.nict.jp A 133.243.238.164 19h16m16s
4 ntp.nict.jp A 133.243.238.243 19h16m16s
5 ntp.nict.jp A 133.243.238.244 19h16m16s
6 ntp.nict.jp A 61.205.120.130 19h16m16s
7 cloudflare.net SOA ns1.cloudflare.net. 7m47s
8 tracking.prf.hn A 5.150.170.6 4h2s
9 tracking.prf.hn A 5.150.170.4 4h2s
10 tracking.prf.hn A 5.150.170.5 4h2s
11 api.x.com CNAME api.x.com.cdn.cloudflare.net. 8m40s
12 www.google.com (unknown) 2h12m39s
13 N dns-healthcheck.local 23h53m33s
14 ads.mozilla.org CNAME mc.prod.ads.prod.webservices.mozgcp.net. 1m10s
15 mc.prod.ads.prod.webservices.mozgcp.net A 34.36.137.203 1m10s
16 telemetry-incoming.r53-2.services.mozilla.com A 34.120.208.123 6m16s
17 expedia.prf.hn CNAME tracking.prf.hn. 44m37s
18 prf.hn SOA dns1.p01.nsone.net. 14m50s
19 accounts.google.com A 108.177.125.84 0s
20 api.x.com.cdn.cloudflare.net A 172.66.0.227 1m39s
21 api.x.com.cdn.cloudflare.net A 162.159.140.229 1m39s
22 www.expedia.co.jp CNAME www.expedia.co.jp.edgekey.net. 3s
23 google SOA ns-tld1.charlestonroadregistry.com. 14m17s
24 google.com A 142.250.206.238 1m52s

NEGATIVEフラグの詳細はよくわかっていませんが、
どうやらこのフラグが付いたドメインTTLまで再問い合わせを行わないようで、
digを繰り返し実行してもキャッシュが残る限りは値が帰ってきませんでした。

うーん、この仕様正直微妙な気も・・・。

環境によっては、意図しないNEGATIVEフラグによる名前解決の失敗を避けるように
スクリプトを修正しても良さそうです。

■最後に

Mikrotikはほぼ初見でスクリプトも初めて作ってみたのですが、
作成からバグ修正まで約2時間程度でできました。

スクリプト自体も難易度はそこまで高くはなく、比較的簡単に作ることができました。

気分転換に作ったものにはなりますが思ったより出来栄えも良く、
本番運用に耐えられそうなものになったのでCHRのライセンス購入や
hEXのような物理筐体の購入も少し考えたいなと思いました。

あくまで初心者が作ったものにはなりますが、
同じような仕組みを構築したいと思っている方の参考になれば嬉しいです!

この記事が参考になったと思った方は良ければスター等よろしくお願いします!

■おまけ

仕事では普段ADC等を触っていて、L7通信の複雑さに頭を抱えていたので
L4レベルでなにか遊べないか考えて作ってました。

が・・・記事を書いていてこれMikrotikで終端していることに気づいて(=L7)、
私は何をやってたんだろうなと感じる週末でした。