Ubuntu 24.04 Server で OpenVPN のブリッジ接続サーバを導入しネームサーバ・DHCPサーバと同時運用する 2 : サーバの設定

前の記事はこちら → [1 : Easy-RSA による証明書等の準備](https://qiita.com/335mochi/items/ce1cfba3cdf51d5c621c)

https://qiita.com/335mochi/items/ce1cfba3cdf51d5c621c


# 2 : サーバの設定

以上で、必要な証明書や鍵などのファイルの準備が整いました。

いよいよ、サーバの設定ファイルなど、サーバ本体を構築していきます。

サーバのホストで作業します。


## 2-1 OpenVPNサーバの設定ファイルを作成

サーバの基本的な 設定ファイル `server.conf` （`/etc/openvpn/server.conf`）を作ります。

実際には、 `/etc/openvpn/server/` に ファイル `server.conf` を作って保持し、それへのシンボリックリンクを `/etc/openvpn/` に置きました。

```bash
root<serverhost/etc/openvpn> % cd server
root<serverhost/etc/openvpn/server> % cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf .
root<serverhost/etc/openvpn/server> % vim server.conf
```

下敷きにしたサンプルファイルの中には詳しい説明（コメント）が含まれているので、読めば参考にもなります。
ここでは、最終的な設定内容だけを示しておきます。

``` :/etc/openvpn/server/server.conf
port 1194

proto udp

dev tap0

ca /etc/openvpn/ca.crt
cert /etc/openvpn/serverhost.crt
key /etc/openvpn/serverhost.key  # This file should be kept secret

dh /etc/openvpn/dh.pem

topology subnet

server-bridge

client-to-client

keepalive 10 120

tls-auth /etc/openvpn/ta.key 0 # This file is secret

user nobody
group nogroup

persist-key
persist-tun

status /var/log/openvpn/openvpn-status.log

log-append /var/log/openvpn/openvpn.log

verb 3

explicit-exit-notify 1

management localhost 7505

crl-verify /etc/openvpn/crl.pem

```

ファイルを保存したらシンボリックリンクを作ります。

```bash
root<serverhost/etc/openvpn/server> % cd ..
root<serverhost/etc/openvpn> % ln -s server/server.conf
```


###### `server.conf` の内容に関するメモ

[参考 : `man openvpn`](https://linux.die.net/man/8/openvpn)
[参考 : Deprecated Options in OpenVPN](https://community.openvpn.net/openvpn/wiki/DeprecatedOptions)

| 設定内容 | メモ |
|---|---|
| `port 1194` と `proto udp` | ポートは *UDP 1194* を使うようにしました。 |
| `dev tap0` | OpenVPN の tap接続 は、 OS からはネットワークインタフェースとして扱われ、インタフェース名が `tapX` （`X` は数字）となるようです。ここでは、 `tap0` と指定しました。 |
| `topology subnet` | いまでは `subnet` にするのが当たり前らしいですが、 バージョン 2.0.9 以前の古い Windows クライアントと通信するには `net30` にしなければならず、そのために用意されているオプションのようです。デフォルトが `net30` のため、このオプションは入れておいたほうがいいようです。デフォルトの変更が検討されているとのことで、急にデフォルト挙動が変わったとき焦らないためにも、入れておくほうがよいとのこと。 |
| `server-bridge` | `server-bridge` の後に何も書かないこのオプションは、 `mode server` `tls-server` `push "route-gateway dhcp"` の3つを指定するのと同等とのこと。要するに、接続してきたクライアントがローカルネットワーク内の DHCPサーバ を使うようになるということです。なお、 `server-bridge` の後に色々つける形のオプションは、これと異なり OpenVPNサーバ が DHCP に相当する役割もする設定になります。 |
| `user nobody` と `group nogroup` | 起動時の必要な設定が終わったらサーバの権限を降格させてセキュリティを向上させます。指定する ユーザ名 `nobody` と グループ名 `nogroup` が実際に存在しないと駄目だそうなので、システムに合わせて変える必要があります。 |
| `persist-key` と `persist-tun` | 上のオプションを使って権限を降格させるとサーバのリロード時に権限が足りずたとえば秘密鍵ファイルを読めないなどでエラーになるらしいのですが、それを防ぐ動作をさせるオプションです。 `persist-key` のほうは、「指定しなくてもつねに有効になるので不要」との情報を見つけたのですが、試しに消してみたところ接続できなくなりました。どうやら「今後そうする方向で検討中」ということのようです。 |
| cipher AES-256-CBC | いまのバージョンの OpenVPN では、サーバとクライアントが相談して利用可能な中から最適なサイファを決めるようになっているそうで、オプションでの指定は必要ないようです。 バージョン 2.3 以前のクライアントと通信する場合は互換性のために入れてもよいとの情報がありますが、しないと思うので省きました。 |
| comp lzo | ネット上の情報ではこれを入れるべきという説明が多いのですが、最新の情報では「圧縮は推奨しないしユーザが使うのを避けるべき機能の1つである」だそうです。なので入れませんでした。 |
| `management localhost 7505` | 稼働中のサーバをネットワークからアクセスしてコントロールできる *管理インタフェース* を有効にします。 `7505` は使うポートです。このポートに `telnet` でアクセスすることで利用できます。 |


## 2-2 証明書や鍵のファイルを適切な場所に置く

[1 : Easy-RSA による証明書等の準備](https://qiita.com/335mochi/items/ce1cfba3cdf51d5c621c) で用意した証明書や鍵のファイルのうち OpenVPN のサーバに必要なものを、コピーしてきて適切な場所（`/etc/openvpn`）に置きます。

OpenVPN のサーバが必要とするファイルは以下です。

| 必要なもの | ファイル名 |
|---|---|
| サーバの証明書 | serverhost.crt |
| サーバの秘密鍵 | serverhost.key |
| CAの証明書 | ca.crt |
| 証明書失効リスト | crl.pem |
| DHパラメータ | dh.pem |
| TLS認証鍵 | ta.key |


### 2-2-1 CA証明書

**CAの証明書** はサーバ・クライアントともに持つ必要があります。
サーバの分として、 `ca.crt` を `/etc/openvpn/` にコピーします。

```bash
root<serverhost/etc/openvpn> % cp /etc/openvpn/easy-rsa/pki/ca.crt .
```

**CAの秘密鍵** は CA として証明書を発行するためのもので、 VPNサーバ や VPNクライアント には必要ないものです。
CA から絶対に漏洩してはならないものなので、ディレクトリのパーミッションに注意してそのまま保管します。


### 2-2-2 サーバ証明書・サーバ秘密鍵

#### (1) 適切な場所にコピーする

**サーバの証明書** と **サーバの秘密鍵** はサーバが持つ必要があります。
`serverhost.crt` と `serverhost.key` を `/etc/openvpn/` にコピーします。
実際には、2つのファイルを `/etc/openvpn/server/` にコピーして保持し、それらへのシンボリックリンクを `/etc/openvpn/` に置きました。

```bash
root<serverhost/etc/openvpn> % cp /etc/openvpn/easy-rsa/pki/issued/serverhost.crt ./server/
root<serverhost/etc/openvpn> % cp /etc/openvpn/easy-rsa/pki/private/serverhost.key ./server/
root<serverhost/etc/openvpn> % ln -s server/serverhost.crt
root<serverhost/etc/openvpn> % ln -s server/serverhost.key
```


#### (2) 秘密鍵からパスフレーズを取り除く

**サーバの秘密鍵** にはパスフレーズがついています。
試してみたところ、このままでは、パスフレーズを入力しない限りこれらを利用できないため、 VPN接続 ができないように思えました（実はあまりちゃんと確かめてませんが…）。
そこで、 **サーバの秘密鍵** からパスフレーズを取り除きます。

`openssl` コマンドで行います。具体的には、 `openssl rsa -in 元の秘密鍵ファイル -out パスフレーズの取り除かれた秘密鍵を格納するファイル名` とします。
`Enter pass phrase for serverhost.key.BACKUP:` でそのパスフレーズを入力します。

```bash
root<serverhost/etc/openvpn> % cd server/
root<serverhost/etc/openvpn/server> % mv serverhost.key serverhost.key.BACKUP
root<serverhost/etc/openvpn/server> % openssl rsa -in serverhost.key.BACKUP -out serverhost.key
Enter pass phrase for serverhost.key.BACKUP:
writing RSA key
```

なお、 **サーバの秘密鍵** は決して漏れたり盗まれたりしてはならないものなわけですが、そこからパスフレーズを取り除いてしまうのはセキュリティ上の危険性をだいぶ上げてしまうことと言えます。ですから、このファイルが盗まれたら（パスフレーズがあれば秘密鍵は漏洩しない可能性があったところ、パスフレーズを取り除いてしまったので）一巻の終わり、と考え、ファイルとディレクトリのパーミッションに細心の注意を払っておく必要があります。

このファイルの オーナー は root:root にし、 パーミッション は 600 にしました。


### 2-2-3 DHパラメータ

**DHパラメータ** はサーバが使います。
`dh.pem` を `/etc/openvpn/` にコピーします。
実際には、ファイルを `/etc/openvpn/server/` にコピーして保持し、それへのシンボリックリンクを `/etc/openvpn/` に置きました。

```bash
root<serverhost/etc/openvpn> % cp /etc/openvpn/easy-rsa/pki/dh.pem ./server/
root<serverhost/etc/openvpn> % ln -s server/dh.pem
```


### 2-2-4 CRL

**CRL** はサーバが持ちます。
`crl.pem` を `/etc/openvpn/` にコピーします。
実際には、ファイルを `/etc/openvpn/server/` にコピーして保持し、それへのシンボリックリンクを `/etc/openvpn/` に置きました。

```bash
root<serverhost/etc/openvpn> % cp /etc/openvpn/easy-rsa/pki/crl.pem ./server/
root<serverhost/etc/openvpn> % ln -s server/crl.pem
```

**CRL** は、今後、何らかの証明書が失効するたびに直ちに作り直され、サーバに渡されなければなりません。
つまり、 OpenVPNサーバ が持つ **CRL** は、つねに *手動で* 置き換えて最新のものに保たれ続けなければならないということです。


### 2-2-5 TLS認証鍵

**TLS認証鍵** はサーバ・クライアントともに持つ必要があります。
`ta.key` ですが、サーバのホストでは、すでに適切な場所（`/etc/openvpn`）に置いてあります。

**TLS認証鍵** は漏洩してはならないものですから、パーミッションに注意します。
オーナー は root:root にし、 パーミッション は 600 にしました。


## 2-3 ブリッジインタフェースを操作するスクリプトを用意

仮想インタフェースである `tap0` と 物理インタフェース（`eno1` とします） の間で自由にパケットを行き来させることにより、外部から接続しているクライアントにとって内部と全く同等な環境を実現することができます。
この、2つのインタフェースを繋ぐ機能は、 **ブリッジインタフェース** という仮想インタフェースの形で提供されます。

**ブリッジインタフェース** という仕組みを提供するのは、 OpenVPN ではなく、 OS です。
それを操作するためのツールが `bridge-utils` というパッケージで与えられているわけです。

**ブリッジインタフェース** を作ったり削除したりするツールは `brctl` です。
それを使い OpenVPN の tap接続 に必要な **ブリッジインタフェース** を起動／停止するシェルスクリプトを作ります。起動するほうを `bridge-start`、停止するほうを `bridge-stop` とします。
これらは、 `/etc/openvpn/` に置くことにします。


###### 下敷きにするサンプルファイルをコピーしてくる

```bash
root<serverhost/etc/openvpn> % cp /usr/share/doc/openvpn/examples/sample-scripts/bridge-start .
root<serverhost/etc/openvpn> % cp /usr/share/doc/openvpn/examples/sample-scripts/bridge-stop .
```


###### 起動用のスクリプト

```bash
root<serverhost/etc/openvpn> % vim bridge-start
```

書き換える箇所を diff で示します。
左が元の（サンプル）ファイル、右が書き換え後。

```diff
17,18c17,18
< eth="eth0"
< eth_ip="192.168.8.4"
---
> eth="eno1"
> eth_ip="192.168.0.2"
20c20,21
< eth_broadcast="192.168.8.255"
---
> eth_broadcast="192.168.0.255"
> gw="192.168.0.1"
32a34,35
> sleep 1
> 
36a40,41
> sleep 1
> 
39a45,47
> 
> route add default gw $gw
> 
```

ファイル全体はこうです。

```sh :/etc/openvpn/bridge-start
#!/bin/sh

#################################
# Set up Ethernet bridge on Linux
# Requires: bridge-utils
#################################

# Define Bridge Interface
br="br0"

# Define list of TAP interfaces to be bridged,
# for example tap="tap0 tap1 tap2".
tap="tap0"

# Define physical ethernet interface to be bridged
# with TAP interface(s) above.
eth="eno1"
eth_ip="192.168.0.2"
eth_netmask="255.255.255.0"
eth_broadcast="192.168.0.255"
gw="192.168.0.1"

for t in $tap; do
    openvpn --mktun --dev $t
done

brctl addbr $br
brctl addif $br $eth

for t in $tap; do
    brctl addif $br $t
done

sleep 1

for t in $tap; do
    ifconfig $t 0.0.0.0 promisc up
done

sleep 1

ifconfig $eth 0.0.0.0 promisc up

ifconfig $br $eth_ip netmask $eth_netmask broadcast $eth_broadcast

route add default gw $gw

```


###### 停止用のスクリプト

`bridge-stop` はサンプルファイルのまま、どこも書き換えずに使うことができました。

ファイル全体はこうです。

```sh :/etc/openvpn/bridge-stop
#!/bin/sh

####################################
# Tear Down Ethernet bridge on Linux
####################################

# Define Bridge Interface
br="br0"

# Define list of TAP interfaces to be bridged together
tap="tap0"

ifconfig $br down
brctl delbr $br

for t in $tap; do
    openvpn --rmtun --dev $t
done
```


###### 実行権限をつける

これら2つのスクリプトに実行パーミッションを与えます。

```bash
root<serverhost/etc/openvpn> % chmod a+x bridge-start bridge-stop
```


## 2-4 systemd のサービスの設定

システム起動時に OpenVPN のサーバを起動しブリッジインタフェースを起動するように systemd に指示するため、ユニットファイルを用意し、サービスの設定をします。

OpenVPNサーバ を起動するユニットファイルは `openvpn.service` など用意されているようなのですが、ブリッジインタフェースを起動／停止するユニットファイルはないようなので、作ります。


### 2-4-1 ブリッジインタフェースを起動／停止するユニットファイルを作る

`openvpn-bridge.service` （`/usr/lib/systemd/system/openvpn-bridge.service`）として作りました。

```bash
root<serverhost/etc/openvpn> % cd /usr/lib/systemd/system/
root<serverhost/usr/lib/systemd/system> % ls o*
open-iscsi.service     openvpn-client@.service  openvpn.service
open-vm-tools.service  openvpn-server@.service  openvpn@.service
root<serverhost/usr/lib/systemd/system> % vim openvpn-bridge.service
```

`openvpn-bridge.service` は、 `bridge-start` と `bridge-stop` を呼び出してブリッジインタフェースを起動したり停止したりする内容です。
ファイル全体は次のとおり。

``` :/usr/lib/systemd/system/openvpn-bridge.service
[Unit]
Description=Openvpn bridge service
Requires=openvpn@server.service
After=openvpn@server.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/openvpn/bridge-start
ExecReload=/bin/true
ExecStop=/etc/openvpn/bridge-stop

[Install]
WantedBy=multi-user.target
```


### 2-4-2 systemd で必要なサービスを有効化する

あとは、 systemd で適切なサービスを有効化しておけば、システム起動時に OpenVPN のサーバが自動的に起動します。

有効化するのは、いま作ったブリッジインタフェースを起動する `openvpn-bridge` と、 `server.conf` の設定に従うサーバを起動する `openvpn@server` です。

最初から有効化されている `openvpn` は必要ないので、無効化してしまいます。


###### サービス `openvpn` の無効化

```bash
root<serverhost/usr/lib/systemd/system> % cd
root<serverhost~> % systemctl disable openvpn
Synchronizing state of openvpn.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install disable openvpn
Removed "/etc/systemd/system/multi-user.target.wants/openvpn.service".
```


###### サービス `openvpn-bridge` の有効化

```bash
root<serverhost~> % systemctl enable openvpn-bridge
Created symlink /etc/systemd/system/multi-user.target.wants/openvpn-bridge.service → /usr/lib/systemd/system/openvpn-bridge.service.
```


###### サービス `openvpn@server` の有効化

```bash
root<serverhost~> % systemctl enable openvpn@server
Created symlink /etc/systemd/system/multi-user.target.wants/openvpn@server.service → /usr/lib/systemd/system/openvpn@.service.
```

なお、 `openvpn@server` は `openvpn@.service` というユニットファイルが実体です。 `@` の後ろの `server` は *サーバの設定ファイルの名前* で、今回は `server.conf` なので `server` をつけているのです。


## 2-5 ファイアウォールの設定

ゲートウェイ（ブロードバンドルータ）の設定を変え、ホスト 192.168.0.2 の *udp 1194* ポート を WAN に開放します。


## 2-6 ブリッジインタフェースに関わるリゾルバの設定

いろいろ試してみて気づいたのですが、サーバのホストでブリッジインタフェースを起動すると、なぜかサーバのホストから DNS にアクセスできなくなることがわかりました。 DNSサーバ は同じホストで稼働しています。他のホストからは問題なく DNS を使えているので、ブリッジインタフェースの起動によってサーバのホストのリゾルバの設定がおかしくなってしまうのだと考えられます。

さらにいろいろ試した結果、リゾルバ設定が ネットワークインターフェース `eno1` に関連づけられているためだとわかりました。
リゾルバ設定は NetworkManager によって管理されているので、　NetworkManager の設定を編集して、 ブリッジインタフェース `br0` に関連づけたリゾルバ設定を加えます。
恥ずかしながら NetworkManager もあまりよく理解していないので、ちょっと苦戦しました。

`/etc/netplan/50-cloud-init.yaml` を編集します。

```bash
root<serverhost~> % cd /etc/netplan/
root<serverhost/etc/netplan> % vim 50-cloud-init.yaml
```

`network:` の下の `ethernets:` の下に `br0:` エントリを追加します。
最終的にファイル全体はこうなりました。

```yaml :/etc/netplan/50-cloud-init.yaml
# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    ethernets:
        eno1:
            addresses:
            - 192.168.0.2/24
            nameservers:
                addresses:
                - 192.168.0.2
                search:
                - my.domain
            routes:
            -   to: default
                via: 192.168.0.1
        br0:
            nameservers:
                addresses:
                - 192.168.0.2
                search:
                - my.domain
    version: 2
```

書き換えを反映させるために次のコマンドを実行します。

```bash
root<serverhost/etc/netplan> % netplan apply
```

これで、 DNS の問題は解決しました。


## 2-7 DHCPサーバが正しく機能するようにする

受け売りになりますが、 VPNサーバ を DHCPサーバ と同じホスト上で動かした場合、 DHCPサーバ の起動より先にブリッジインタフェースが作られていないと、 DHCPサーバ がブリッジインタフェースを listen しないことがあるそうです。
[参考 : OpenVPNのtapブリッジングでDHCPを使う場合のTips](https://yudai.arielworks.com/memo/2010/08/12/174249)

そうなると、 VPNクライアント が接続してきたとき、 VPN として接続できたとしてもIPアドレスなどが割り振られず、結局、機能しないことになってしまいます。
実際に、試してみると、そのとおりだと思えるようなことが起こりました。

この問題を解決するために、システムを起動するときの `isc-dhcp-server` の起動が `openvpn-bridge` の起動よりも後になるようにします。

systemd の ユニットファイル `isc-dhcp-server.service` を書き換えます。

```bash
root<serverhost/etc/netplan> % cd /usr/lib/systemd/system/
root<serverhost/usr/lib/systemd/system> % vim isc-dhcp-server.service
```

`[Unit]` セクションに次の1行を加えます。

```
After=openvpn-bridge.service
```

ファイル全体はこうなります。

``` :/usr/lib/systemd/system/isc-dhcp-server.service
[Unit]
Description=ISC DHCP IPv4 server
Documentation=man:dhcpd(8)
Wants=network-online.target
After=network-online.target
After=time-sync.target
After=openvpn-bridge.service
ConditionPathExists=/etc/default/isc-dhcp-server
ConditionPathExists=|/etc/ltsp/dhcpd.conf
ConditionPathExists=|/etc/dhcp/dhcpd.conf

[Service]
EnvironmentFile=/etc/default/isc-dhcp-server
RuntimeDirectory=dhcp-server
# The leases files need to be root:dhcpd even when dropping privileges
ExecStart=/bin/sh -ec '\
    CONFIG_FILE=/etc/dhcp/dhcpd.conf; \
    if [ -f /etc/ltsp/dhcpd.conf ]; then CONFIG_FILE=/etc/ltsp/dhcpd.conf; fi; \
    [ -e /var/lib/dhcp/dhcpd.leases ] || touch /var/lib/dhcp/dhcpd.leases; \
    chown root:dhcpd /var/lib/dhcp /var/lib/dhcp/dhcpd.leases; \
    chmod 775 /var/lib/dhcp ; chmod 664 /var/lib/dhcp/dhcpd.leases; \
    exec dhcpd -user dhcpd -group dhcpd -f -4 -pf /run/dhcp-server/dhcpd.pid -cf $CONFIG_FILE $INTERFACESv4'

[Install]
WantedBy=multi-user.target
```

`isc-dhcp-server6.service` も同様にしておきます。

```bash
root<serverhost/usr/lib/systemd/system> % vim isc-dhcp-server6.service
```

これで、 DHCP の問題は解決しました。


---


次の記事はこちら → [3 : クライアントの設定](https://qiita.com/335mochi/items/58fa22e9a1109c31b24a)

https://qiita.com/335mochi/items/58fa22e9a1109c31b24a