ISC-DHCPD + PowerDNSによるDDNSでSlack通知

この記事は [TUT CDSL Advent Calendar 2019 - Adventar](https://adventar.org/calendars/4281) の1日目です．

正式名称はCloud and Distributed Systems Laboratory（クラウド・分散処理）です．2019年に発足したばかりの研究室です．細かなことは今後の記事にまかせます．

この記事では，CDSLの内部にあるシステムを紹介します．今回はDDNSまわりを紹介します．

# DDNSとは

DDNSは動的(Dynamic)に割り当てを行うDNSです．例えば，ホスト名が `hoge` の場合は `hoge.exampe.com` が自動で割り当てられることです．CDSLではこの技術を使って，ネットワーク内の端末へFQDNでアクセスできる仕組みを用意しています．

# アーキテクチャ

DNSサーバは，権威サーバとキャッシュサーバのどちらもPowerDNSを使っています．DHCPサーバーにはISC DHCPDを使っています．Fluentdは後述のSlack通知に使っています．
![cdsl-archi](https://user-images.githubusercontent.com/2428176/69913303-f5e91b00-142d-11ea-99f8-3235ba7db9f6.png)

PowerDNSの設定例：

```
config-dir=/etc/powerdns

local-address=192.168.100.6
local-port=53

allow-from=127.0.0.0/8,192.168.100.0/24
forward-zones=a910.tak-cslab.org=127.0.0.1
forward-zones-recurse=.=8.8.8.8,.=1.1.1.1
hint-file=/usr/share/dns/root.hints

setgid=pdns
setuid=pdns

max-cache-ttl=300
max-negative-ttl=300
```

ISC DHCPDの設定例：

```
option domain-name "a910.tak-cslab.org";
option domain-name-servers 192.168.100.6, 8.8.8.8;

# 12h
default-lease-time 43200;

# 24h
max-lease-time 86400;

ddns-updates on;
ddns-update-style standard;
ignore client-updates;

log-facility local7;

subnet 192.168.0.0 netmask 255.255.255.0 {
  range 192.168.100.10 192.168.100.254;
  option routers 192.168.0.1;
  ddns-domainname "a910.tak-cslab.org";
  ddns-ttl 3600;

  zone a910.tak-cslab.org. {
    primary 127.0.0.1;
  }

  # zone 100.168.192.in-addr.arpa. {
  #   primary 127.0.0.1;
  # }
}
```

# Slack通知

当初，DDNSの割り当てを確認するためにWeb UIの用意を考えていました．しかし，そのためにサーバーの管理や設定を行うことは負担になると考えました．そこで，Slackのチャンネルを作成し，そこへ通知を送ることにしました．

ISC DHCPDにはIPリース時にコマンドを実行する機能があります．しかし，この機能を使おうと設定を行ったところ権限エラーで動作しないため諦めました．

[ISC DHCP serverでIPアドレスのリース時に外部コマンドを実行する - yunazuno.log](http://yunazuno.hatenablog.com/entry/2014/05/25/181236)

方針を転換して，Fluentdのtailプラグインでログの監視を行い，条件に一致した文字列が見つかった場合にSlackへ通知を行うことにしました．

[fluentdでログ監視して特定の文字列をgrepで検知したらSlackに通知する（バースト抑止付き） - Qiita](https://qiita.com/minamijoyo/items/1bb277383d8d2d2c7c11)

Fluentdの設定例：

```
## File input
<source>
  @type tail
  @id input_tail
  @label @raw
  path /var/log/syslog
  pos_file /var/lib/td-agent/syslog.pos
  format syslog
  tag raw.syslog
</source>

<label @raw>
  ## Filtering DHCPD
  <filter>
    @type grep
    <and>
      <regexp>
        key ident
        pattern /^dhcpd$/
      </regexp>
      <regexp>
        key message
        pattern /^Added new forward map from/
      </regexp>
    </and>
  </filter>
  ## Remove prefix
  <filter>
    @type record_transformer
    enable_ruby true
    <record>
      message ${record["message"].gsub("Added new forward map from ","").gsub(" to ", ",")}
      tag ${tag}
    </record>
  </filter>
  ## CSV Parse
  <filter>
    @type parser
    key_name message
    <parse>
      @type csv
      keys fqdn,ip_addr
    </parse>
  </filter>
  ## Remove prefix
  <filter>
    @type record_transformer
    enable_ruby true
    <record>
      message ${record["fqdn"]} is binded new client. (ip_addr: `${record["ip_addr"]}` )
      tag ${tag}
    </record>
  </filter>
  ## Debug
  # <match>
  #   @type stdout
  #   @id output_stdout
  #   # <format>
  #   #   @type json
  #   # </format>
  # </match>
  <match>
    @type slack
    webhook_url https://hooks.slack.com/services/XXXX/YYYY/ZZZZZZZ
    channel _ddns-notice
    username ddns-notice
    icon_emoji :ghost:
    flush_interval 60s
  </match>
</label>
```

通知の例：
![cdsl-slack](https://user-images.githubusercontent.com/2428176/69913304-fbdefc00-142d-11ea-8510-701acb5fc7d0.png)