VyattaでWAN Proofを行います。サービス対象のDNSゾーンのNSをWAN Proof Vyattaに向け、DNSレコードをWAN側の状況に応じて更新することで、常にActiveなネットワークのIPアドレスが引けるようになります。通常時は両側のIPアドレスをラウンドロビンで返しますが、どちらかのWANがfailするとそれを検知して生きている側のIPアドレスだけを返すようになります。ゾーンのNSには両側のWANのIPアドレスを指定しておく必要があります。実際のコンテンツを持っているサーバはVyatta下にDNATされていて、どちらのWANからのリクエストかは意識する必要がありません。
この機能の実現にはVyatta標準のWAN Load Balancing機能と、そのフックスクリプト機能、それからUnboundのunbound-controlでDDNSを利用します。DDNSはBINDのnsupdateでも大丈夫でしょう。
まずは外に出ていけるようにしてリポジトリの追加
set interfaces ethernet eth0 address '192.168.20.252/24' set interfaces ethernet eth0 address '192.168.20.20/24' set interfaces ethernet eth1 address '192.168.30.252/24' set interfaces ethernet eth1 address '192.168.30.20/24' set interfaces ethernet eth2 address '10.168.40.1/24' set protocols static route 0.0.0.0/0 next-hop '192.168.20.2' set protocols static route 0.0.0.0/0 next-hop '192.168.30.2' set protocols static route 192.168.10.251/32 next-hop '192.168.20.2' set protocols static route 192.168.10.253/32 next-hop '192.168.30.2' set nat source rule 4000 outbound-interface 'any' set nat source rule 4000 source address '10.168.40.0/24' set nat source rule 4000 translation address 'masquerade' set system name-server '192.168.11.254' set system package repository squeeze components 'main contrib non-free' set system package repository squeeze distribution 'squeeze' set system package repository squeeze url 'http://ftp.jaist.ac.jp/pub/Linux/debian' set system package repository squeeze-backports components 'main contrib non-free' set system package repository squeeze-backports distribution 'squeeze-backports' set system package repository squeeze-backports url 'http://ftp.jaist.ac.jp/pub/Linux/debian-backports' commit
operation modeでUnboundとPythonのYAMLライブラリをインストールしてUnboundの設定
sudo aptitude update sudo aptitude safe-upgrade sudo aptitude hold lighttpd live-initramfs sudo aptitude install unbound=1.4.17-2~bpo60+1 python-yaml sudo unbound-control-setup sudo vi /etc/unbound/unbound.conf # server: セクションに以下を設定 # interface: 0.0.0.0 # interface-automatic: yes # rrset-roundrobin:yes # access-control: 0.0.0.0/0 allow # remote-control: セクションに以下を設定 # control-enable: yes
WAN Load Balancingの監視に基いてDNSレコードを更新するフックスクリプト
/config/scripts/update-lc.py
#!/usr/bin/env python import os import os.path import glob import shlex from subprocess import call import yaml from optparse import OptionParser op = OptionParser() op.add_option("-d", "--debug", dest="debug", help="debug mode", action="store_true", default=False) op.add_option("-i", "--interface", dest="if_name", help="interface", metavar="INTERFACE") op.add_option("-s", "--state", dest="state", help="state", metavar="STATE") op.add_option("-c", "--conf", dest="conf_file", help="conf file", metavar="CONFFILE", default="/config/scripts/update-lc.yaml") (ops, args) = op.parse_args() work_dir = "/tmp/lc" log_file = "%(work_dir)s/lc.log" % locals() log = open(log_file,"w") if ops.debug: if_name = ops.if_name state = ops.state else: if_name = os.environ["WLB_INTERFACE_NAME"] state = os.environ["WLB_INTERFACE_STATE"] log.write(if_name+"n") log.write(state+"n") lc_conf = yaml.load(open(ops.conf_file).read()) if not os.path.exists(work_dir): os.mkdir(work_dir) for k,v in lc_conf.iteritems(): domain_dir = "%(work_dir)s/%(k)s" % locals() domain_if_dir = "%(domain_dir)s/%(if_name)s" % locals() ip_addr = v[if_name] entry_file_name = "%(domain_if_dir)s/%(ip_addr)s" % locals() changed = False if not os.path.exists(domain_dir): os.mkdir(domain_dir) if not os.path.exists(domain_if_dir): os.mkdir(domain_if_dir) if state == "ACTIVE" and not os.path.exists(entry_file_name): open(entry_file_name,"wb").close() changed = True if state == "FAILED" and os.path.exists(entry_file_name): os.remove(entry_file_name) changed = True if changed: remove_cmd = "/usr/sbin/unbound-control local_data_remove %(k)s" % locals() args = shlex.split(remove_cmd) call(args) nics = os.listdir(domain_dir) for nic in nics: addrs = os.listdir("%(domain_dir)s/%(nic)s" % locals()) for addr in addrs: add_cmd = "/usr/sbin/unbound-control local_data %(k)s 5 IN A %(addr)s" % locals() args = shlex.split(add_cmd) call(args) log.close()
WAN ProofでホストするFQDNと、各WAN側のIPアドレスをYAMLで記述
/config/scripts/update-lc.yaml
lc.kawataso.net: eth0: 192.168.20.20 eth1: 192.168.30.20
ホストするリアルサーバに対して各WAN側からDNATを設定
set nat destination rule 2020 destination address '192.168.20.20' set nat destination rule 2020 inbound-interface 'eth0' set nat destination rule 2020 protocol 'all' set nat destination rule 2020 translation address '10.168.40.20' set nat destination rule 3020 destination address '192.168.30.20' set nat destination rule 3020 inbound-interface 'eth1' set nat destination rule 3020 protocol 'all' set nat destination rule 3020 translation address '10.168.40.20' commit
WAN Load Balancingを有効化
set load-balancing wan 'enable-local-traffic' set load-balancing wan 'flush-connections' set load-balancing wan hook '/config/scripts/update-lc.py' set load-balancing wan interface-health eth0 failure-count '2' set load-balancing wan interface-health eth0 nexthop '192.168.20.2' set load-balancing wan interface-health eth0 success-count '1' set load-balancing wan interface-health eth0 test 10 resp-time '5' set load-balancing wan interface-health eth0 test 10 target '192.168.10.251' set load-balancing wan interface-health eth0 test 10 ttl-limit '1' set load-balancing wan interface-health eth0 test 10 type 'ping' set load-balancing wan interface-health eth1 failure-count '1' set load-balancing wan interface-health eth1 nexthop '192.168.30.2' set load-balancing wan interface-health eth1 success-count '1' set load-balancing wan interface-health eth1 test 10 resp-time '5' set load-balancing wan interface-health eth1 test 10 target '192.168.10.253' set load-balancing wan interface-health eth1 test 10 ttl-limit '1' set load-balancing wan interface-health eth1 test 10 type 'ping' set load-balancing wan rule 10 inbound-interface 'eth2' set load-balancing wan rule 10 interface eth0 weight '1' set load-balancing wan rule 10 interface eth1 weight '1' set load-balancing wan rule 10 'per-packet-balancing' set load-balancing wan rule 10 protocol 'all' commit
新しいFQDNを追加する場合はリアルサーバと、公開用グローバルアドレスを決めたら/config/scripts/update-lc.yamlをこんな感じに追記して
lc.kawataso.net: eth0: 192.168.20.20 eth1: 192.168.30.20 lc2.kawataso.net: eth0: 192.168.20.30 eth1: 192.168.30.30
NATを追加して
set nat destination rule 2030 destination address '192.168.20.30' set nat destination rule 2030 inbound-interface 'eth0' set nat destination rule 2030 protocol 'all' set nat destination rule 2030 translation address '10.168.40.30' set nat destination rule 3030 destination address '192.168.30.30' set nat destination rule 3030 inbound-interface 'eth1' set nat destination rule 3030 protocol 'all' set nat destination rule 3030 translation address '10.168.40.30' commit
Operation Modeで
restart wan-load-balance
を実行するとWAN Proof対象に新しいFQDNが追加されます。