Avahi を利用してローカル環境で Raspberry Pi に SSH 接続する

# はじめに

[前回の記事](https://qiita.com/ren1007/items/3a37c7dde42e1acbbd4e)で、Raspberry Pi に LAN 内からリモート接続（SSH 接続）をする方法について書いた際、Avahi を使用する方がやりやすいのではというコメントをいただいたので、今回は、<font color=Magenta>**Avahi を使用して SSH 接続する方法**</font> について書いていきたいと思います。

# 目次

- Avahi とは
- DNS（Domain Name System）
- Domain と IP の調査でよく使われるコマンド
  - nslookup
  - dig
  - cURL
- mDNS（multicast DNS）
- Avahi を使用して SSH 接続する
- 最後に

# Avahi とは

[前回の記事](https://qiita.com/ren1007/items/3a37c7dde42e1acbbd4e)では、ラズパイのアドレスを調べる際に一度モニタやキーボード、マウスといった周辺機器を接続して確認をしていました。このように、組み込み機器やラズパイなどモニタレスのものは、DHCP などによって動的に IP アドレスが割り当てられると、確認するのに一手間掛かります。
Avahi はこのような状況において、動的に名前解決する仕組みを利用することで、一意に端末を識別することができる仕組みを提供します。

# DNS（Domain Name System）

ここで登場する名前解決とは何かを簡単に紹介します。
DNS とはインターネット上でドメイン名を管理・運用するために開発されたシステムで、現在インターネットを利用する際には必要不可欠なシステムの一つです。
インターネット上では IP アドレスを実世界の住所のように用いることによって、通信相手を一意に識別します。しかし、IP アドレスは例えば、`192.168.10.5`といったように数字の羅列で表記されます。また、IPv6 アドレスの場合、`2001:dc2:1000:2006::cafe`というように英数字で表されるため、これらを覚えておくことは容易ではありません。
そのため、一意のアドレスに対して名前を付けることでヒューリスティクスを高める手法を取り入れます。この時の名前を『<font color=Red>ドメイン名</font>』と言います。
例えば、Google の検索サイトの場合、`172.217.31.17`（2020 年 12 月 20 日 現在）というアドレスを保持しています。実際に`172.217.31.17`というアドレスを検索エンジンに入力すると、Google のサイトに接続することができます。
しかし、一般的に Google を利用する人は、このようなアドレスを入力しなくても、`google.com`という URL を利用してアクセスする場合がほとんどです。これは`172.217.31.17(アドレス) = google.com(名前)`という対応関係があるため、このようなことが可能になっているわけです。
しかし、実際の通信では IP アドレスを用いるため、`goole.com`では疎通が取れません。この時、**アドレスと名前の対応関係**を用いて、**「名前によるアクセス」を「アドレスに解決」してくれるシステム** が DNS というわけです。
通常、DNS はユニキャスト（1 対 1）で名前解決を図ります。また、その際には UDP ポートとして 53 番を使用します。
余談ですが、AWS（Amazon Web Service）で用いられる名前解決システム Route 53 の 53 とは DNS の[ウェルノウンポート番号](https://ja.wikipedia.org/wiki/TCP%E3%82%84UDP%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%83%9D%E3%83%BC%E3%83%88%E7%95%AA%E5%8F%B7%E3%81%AE%E4%B8%80%E8%A6%A7)の 53 から来ているそうです。

# Domain と IP の調査でよく使われるコマンド

## nslookup

- DNS レコードの取得
- フォーマット：`$ nslookup [ドメイン名]`

```
$ nslookup google.com

Server:		192.168.11.1
Address:	192.168.11.1#53

Non-authoritative answer:
Name:	google.com　　　　　　⬅︎ ドメイン名
Address: 172.217.31.174　　 ⬅︎ IPアドレス
```

## dig

- DNS レコードの取得（詳細表示）
- フォーマット：`$ dig [ドメイン名]`

```
$ dig google.com

; <<>> DiG 9.10.6 <<>> google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58325
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;google.com.			IN	A

;; ANSWER SECTION:　　⬅︎ ドメイン名 & IPアドレス
google.com.		163	IN	A	172.217.31.174

;; Query time: 6 msec
;; SERVER: 192.168.11.1#53(192.168.11.1)
;; WHEN: Sun Dec 20 20:29:53 JST 2020
;; MSG SIZE  rcvd: 55
```

## cURL

- リクエストに対するレスポンスを取得
- フォーマット：`$ curl [リクエスト先] -i`

```
$ curl google.com -i
or
$ curl 172.217.31.174 -i   （両者で同じレスポンスが返される）

HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
Date: Sun, 20 Dec 2020 11:36:27 GMT
Expires: Tue, 19 Jan 2021 11:36:27 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 219
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
```

# mDNS（multicast DNS）

mDNS は LAN 内（ローカルセグメント）の端末をマルチキャストを利用して自動的に探索するための仕組みです。上で、DNS はユニキャストによる名前解決を図ると述べましたが、mDNS の場合、LAN 内の複数の端末に対して名前解決を図ります。
その際、マルチキャストアドレス`224.0.0.251`の UDP `5353`ポートに DNS クエリをマルチキャストすることで、同一 LAN の特定の端末が応答します。
ラズパイの場合、ホスト名に`.local`を付けたものが、ドメイン名（名前）になります。

# Avahi を使用して SSH 接続する

- 長くなりましたが、仕組みが分かったところで実際に SSH 接続していきます。

- mDNS に対応するために avahi-daemon をインストール

```
$ sudo apt-get update
$ sudo apt-get install avahi-daemon
$ sudo apt-get install avahi-autoipd
$ sudo insserv avahi-daemon
```

- ラズパイに名前（ホスト名）を付ける
  - `$ sudo nano /etc/hostname`

```
### ラズパイにつけるホスト名を入力
raspberrypi
```

- ホスト名を確認

```
$ cat /etc/hostname

raspberrypi
```

- SSH サービスの登録
  - `$ sudo nano /etc/avahi/services/ssh.service`

```xml:ssh.service
### 以下のXMLを記述
<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">

<service-group>
  <name replace-wildcards="yes">%h</name>

  <service>
    <type>_ssh._tcp</type>
    <port>22</port>
  </service>
</service-group>
```

- 再起動

```
$ sudo /etc/init.d/avahi-daemon restart

[ ok ] Restarting avahi-daemon (via systemctl): avahi-daemon.service.
```

- avahi-daemon を確認（マルチキャストグループに含まれていれば OK）

```
$ sudo /etc/init.d/avahi-daemon status

● avahi-daemon.service - Avahi mDNS/DNS-SD Stack
   Loaded: loaded (/lib/systemd/system/avahi-daemon.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2020-12-20 01:13:21 JST; 33s ago
 Main PID: 292 (avahi-daemon)
   Status: "avahi-daemon 0.6.32 starting up."
    Tasks: 2 (limit: 4915)
   CGroup: /system.slice/avahi-daemon.service
           ├─292 avahi-daemon: running [raspberrypi.local]
           └─319 avahi-daemon: chroot helper

12月 20 01:13:24 raspberrypi avahi-daemon[292]: Registering new address record for fe80::5d84:5ad1:372e:b4f8 on wlan0.*.
12月 20 01:13:24 raspberrypi avahi-daemon[292]: Withdrawing address record for fe80::5d84:5ad1:372e:b4f8 on wlan0.
12月 20 01:13:24 raspberrypi avahi-daemon[292]: Leaving mDNS multicast group on interface wlan0.IPv6 with address fe80::5d84:5ad1:372e:b4f8.
12月 20 01:13:24 raspberrypi avahi-daemon[292]: Interface wlan0.IPv6 no longer relevant for mDNS.
12月 20 01:13:29 raspberrypi avahi-daemon[292]: Joining mDNS multicast group on interface wlan0.IPv6 with address fe80::5e82:980f:7f2d:6ba4.
12月 20 01:13:29 raspberrypi avahi-daemon[292]: New relevant interface wlan0.IPv6 for mDNS.
12月 20 01:13:29 raspberrypi avahi-daemon[292]: Registering new address record for fe80::5e82:980f:7f2d:6ba4 on wlan0.*.
12月 20 01:13:35 raspberrypi avahi-daemon[292]: Joining mDNS multicast group on interface wlan0.IPv4 with address 192.168.11.4.
12月 20 01:13:35 raspberrypi avahi-daemon[292]: New relevant interface wlan0.IPv4 for mDNS.
12月 20 01:13:35 raspberrypi avahi-daemon[292]: Registering new address record for 192.168.11.4 on wlan0.IPv4.
```

- 生存確認

```
$ ping -c 5 raspberrypi.local

PING raspberrypi.local (192.168.11.4): 56 data bytes
64 bytes from 192.168.11.4: icmp_seq=0 ttl=64 time=9.371 ms
64 bytes from 192.168.11.4: icmp_seq=1 ttl=64 time=2.665 ms
64 bytes from 192.168.11.4: icmp_seq=2 ttl=64 time=9.280 ms
64 bytes from 192.168.11.4: icmp_seq=3 ttl=64 time=2.999 ms
64 bytes from 192.168.11.4: icmp_seq=4 ttl=64 time=7.967 ms

--- raspberrypi.local ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 2.665/6.456/9.371/3.003 ms
```

- 接続
  - 同一の LAN に接続していることを確認して`ssh`コマンドを実行
  - `$ ssh pi@raspberrypi.local`

これにより、ローカル環境でのリモート接続は`.local`と指定するだけで接続することができるようになりました。
マルチキャストは通常同一 LAN 内に対して、働きかけるため LAN 外にラズパイもしくは接続用 PC がある場合、`.local`でのアクセスはできません。

# 最後に

今回は、ローカル環境（LAN 内）でラズパイにリモート接続する方法として Avahi を紹介しました。
Avahi は名前解決を利用することで、[アドレスを固定する方法](https://qiita.com/ren1007/items/3a37c7dde42e1acbbd4e)よりも簡単にアクセスすることができます。
しかし、同一 LAN に複数のラズパイが存在する場合にはうまく機能しない場合があります。
今後、LAN 外からのアクセス設定も考えているのなら、アドレス固定の設定もしつつ Avahi を利用すると良いと思います。

それでは 💪