VyOSでFirewallを作ってみる


## 概要
- オープンソースで開発されているネットワークOS
- 主にソフトウェアルータとして運用される
- Routing, VPN, Firewall, NAT, High Availabilityといった多くの機能をサポートしている
- AnsibleやAPIを利用しての設定も可能
- 推奨スペックも1CPU, 512MB Memory, 2GB Storageと軽量

## 検証で利用したソフトウェア

| ソフトウェア | バージョン |
| --- | --- |
| VirtualBox | 6.1.26 r145957 |
| Vagrant | 2.2.10 |
| vagrant-vyos | 1.1.10 |
| VyOS | 1.4-rolling-202204060217 |
| CentOS | 7.9.2009 |

## 構成図
![vyos_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663565/1483b0f4-31ea-1a62-b85d-47799e82ad91.png)

## やってみたこと
- Vagrant上でVyOSのFWを構築し、動作検証する
  - vagrant upからVyOSへのログイン、バージョン確認まで
  - FWポリシー追加
  - 動作確認

## やってないこと
- IF設定, HA構成, ログ転送, Ansible, API, ゾーンベースのFWについては検証してません(リクエストあればまた検証して発表します)

## 参考サイト
- VyOS公式ドキュメント
https://docs.vyos.io/en/latest/configuration/index.html


## 検証内容
### Vagrant上での起動
pluginのインストール
```
$ vagrant plugin install vagrant-vyos
(略)
Installed the plugin 'vagrant-vyos (1.1.10)'!
```

`vagrant up`する
```
$ vagrant up
(略)
```

起動状態を確認する
```
$ vagrant status
Current machine states:

vyos01                    running (virtualbox)
web01                     running (virtualbox)
sv01                      running (virtualbox)
sv02                      running (virtualbox)
```

vyos01にログインする。初期パスワードの入力を求められる場合は`vyos`
```
$ vagrant ssh vyos01
Welcome to VyOS!

Check out project news at https://blog.vyos.io
and feel free to report bugs at https://phabricator.vyos.net

You can change this banner using "set system login banner post-login" command.

VyOS is a free software distribution that includes multiple components,
you can check individual component licenses under /usr/share/doc/*/copyright
```

バージョンの確認
```
vyos@vyos:~$ show version

Version:          VyOS 1.4-rolling-202204060217
Release train:    sagitta

Built by:         autobuild@vyos.net
Built on:         Wed 06 Apr 2022 02:17 UTC
Build UUID:       1434d2b1-ac6d-4703-9587-eb7428a1ebbc
Build commit ID:  c522ff506d8544

Architecture:     x86_64
Boot via:         installed image
System type:      KVM guest

Hardware vendor:  innotek GmbH
Hardware model:   VirtualBox
Hardware S/N:     0
Hardware UUID:    86d999cf-7433-4fec-9f10-2d1b334e3333

Copyright:        VyOS maintainers and contributors
```

### FWポリシー設定
- 細かい粒度でのポリシーを設定することが可能。内部的にはLinuxのnetfilterを使っている。サービス(port)、IPアドレス、ネットワーク単位でのグループ作成可能
、インターフェースまたはゾーンベースのファイアウォールポリシーをサポートしている。
- `各種ルールを定義、ルールをインターフェースに割り当てる`ことで適用される。
- `IN・OUT・LOCAL`というルールの適用形式がある
  - in
    - IptablesのFORWARDチェインのINに該当する定義
      実際にパケットが入ってくるインターフェイスに割り当てることで通信を制御する
      ex.) set interfaces ethernet eth0 firewall in <rule-name>
  - out
    - iptablesのFORWARDチェインのOUTに該当する定義
      実際にパケットが出ていくインターフェイスに割り当てることで通信を制御する
      ex.) set interfaces ethernet eth1 firewall out <rule-name>
  - local
    - ルータ自身を宛先にしたパケットを制御する
      ex.) set interfaces ethernet eth2 firewall local <rule-name>
- パケットに対する制御は以下の通り
  - accept:トラフィックを受け入れる
  - drop:トラフィックを破棄する
  - inspect:トラフィックをIPSで処理する
  - reject:トラフィックを停止する．その際TCPリセットを返す．
- デフォルトはall:allでacceptされている
- デフォルトはステートレス、ステートフルにも変えられるらしいが今回は試してない


## 通信許可設定
- 以下の図のような設定を行う
![vyos_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663565/f8c49153-108f-833e-1644-d68ee15a64a6.png)


以下、設定コマンド(より良い設定方法もあると思われる)
```
vyoq01へのpingを無効にする
# set firewall all-ping disable

web01からvyos01へ送られるパケットに対するルール設定(vyos_eth1)
# set firewall name rule_eth1_in default-action drop
# set firewall name rule_eth1_in enable-default-log
# set firewall name rule_eth1_in rule 10 action accept
# set firewall name rule_eth1_in rule 10 source address 192.168.0.0/24
# set firewall name rule_eth1_in rule 10 destination address 192.168.10.0/24
# set interface ethernet eth1 firewall in name rule_eth1_in

sv01とsv02からvyos01へ送られるパケットに対するルール設定(vyos_eth2)
# set firewall name rule_eth2_in default-action drop
# set firewall name rule_eth2_in enable-default-log
# set firewall name rule_eth2_in rule 20 action accept
# set firewall name rule_eth2_in rule 20 source address 192.168.10.0/24
# set firewall name rule_eth2_in rule 20 destination address 192.168.0.0/24
# set interface ethernet eth2 firewall in name rule_eth2_in

vyosからsv01とsv02へ送られるパケットに対するルール設定(vyos_eth2)
# set firewall name rule_eth2_out enable-default-log
# set firewall name rule_eth2_out rule 30 action drop
# set firewall name rule_eth2_out rule 30 destination port 8280
# set firewall name rule_eth2_out rule 30 protocol tcp
# set interface ethernet eth2 firewall out name rule_eth2_out

vyosからweb01へ送られるパケットに対するルール設定(vyos_eth1)
# set firewall name rule_eth1_out enable-default-log
# set firewall name rule_eth1_out rule 40 action drop
# set firewall name rule_eth1_out rule 40 source address 192.168.10.12/32
# set firewall name rule_eth1_out rule 40 destination port 80
# set firewall name rule_eth1_out rule 40 protocol tcp
# set interface ethernet eth1 firewall out name rule_eth1_out
```

簡易的なDos攻撃対策
```
# set firewall name rule_eth1_out rule 50 action reject
# set firewall name rule_eth1_out rule 50 destination port 8080
# set firewall name rule_eth1_out rule 50 protocol tcp
# set firewall name rule_eth1_out rule 50 recent count 3
# set firewall name rule_eth1_out rule 50 recent time 1
# set firewall name rule_eth1_out rule 50 state new enable


3回目のcurlまでは200 OKだが、4回目からConnection refusedが返却される
どれくらい時間をあけると解除されるかは調べても見つかりませんでした
[root@sv02 ~]# curl -v http://192.168.0.1:8080
* About to connect() to 192.168.0.1 port 8080 (#0)
*   Trying 192.168.0.1...
* Connected to 192.168.0.1 (192.168.0.1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.0.1:8080
> Accept: */*
>
< HTTP/1.1 200 OK
(省略)
[root@sv02 ~]# curl -v http://192.168.0.1:8080
* About to connect() to 192.168.0.1 port 8080 (#0)
*   Trying 192.168.0.1...
* Connected to 192.168.0.1 (192.168.0.1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.0.1:8080
> Accept: */*
>
< HTTP/1.1 200 OK
(省略)
[root@sv02 ~]# curl -v http://192.168.0.1:8080
* About to connect() to 192.168.0.1 port 8080 (#0)
*   Trying 192.168.0.1...
* Connected to 192.168.0.1 (192.168.0.1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 192.168.0.1:8080
> Accept: */*
>
< HTTP/1.1 200 OK
(省略)
[root@sv02 ~]# curl -v http://192.168.0.1:8080
* About to connect() to 192.168.0.1 port 8080 (#0)
*   Trying 192.168.0.1...
* Connection refused
* Failed connect to 192.168.0.1:8080; Connection refused
* Closing connection 0
curl: (7) Failed connect to 192.168.0.1:8080; Connection refused
```



## 所感
- 軽いスペックで動作する点が魅力的と感じました。商用利用する際は、VyOS稼働サーバに対するセキュリティ対策が必要ですが、ささっと構築/検証できるため、NWの勉強としても良いと感じました。


## 参考(構築後config)
```
vyos@vyos01:~$ show configuration commands
set firewall all-ping 'disable'
set firewall group
set firewall name rule_eth1_in default-action 'drop'
set firewall name rule_eth1_in enable-default-log
set firewall name rule_eth1_in rule 10 action 'accept'
set firewall name rule_eth1_in rule 10 destination address '192.168.10.0/24'
set firewall name rule_eth1_in rule 10 source address '192.168.0.0/24'
set firewall name rule_eth1_out enable-default-log
set firewall name rule_eth1_out rule 40 action 'drop'
set firewall name rule_eth1_out rule 40 destination port '80'
set firewall name rule_eth1_out rule 40 protocol 'tcp'
set firewall name rule_eth1_out rule 40 source address '192.168.10.12/32'
set firewall name rule_eth1_out rule 50 action 'reject'
set firewall name rule_eth1_out rule 50 destination port '8080'
set firewall name rule_eth1_out rule 50 protocol 'tcp'
set firewall name rule_eth1_out rule 50 recent count '3'
set firewall name rule_eth1_out rule 50 recent time 'minute'
set firewall name rule_eth1_out rule 50 state new 'enable'
set firewall name rule_eth2_in default-action 'drop'
set firewall name rule_eth2_in enable-default-log
set firewall name rule_eth2_in rule 20 action 'accept'
set firewall name rule_eth2_in rule 20 destination address '192.168.0.0/24'
set firewall name rule_eth2_in rule 20 source address '192.168.10.0/24'
set firewall name rule_eth2_out enable-default-log
set firewall name rule_eth2_out rule 30 action 'drop'
set firewall name rule_eth2_out rule 30 destination port '8280'
set firewall name rule_eth2_out rule 30 protocol 'tcp'
set interfaces ethernet eth0 address 'dhcp'
set interfaces ethernet eth0 hw-id '08:00:27:8d:c0:4d'
set interfaces ethernet eth0 offload gro
set interfaces ethernet eth0 offload gso
set interfaces ethernet eth0 offload sg
set interfaces ethernet eth0 offload tso
set interfaces ethernet eth1 address '192.168.0.2/24'
set interfaces ethernet eth1 firewall in name 'rule_eth1_in'
set interfaces ethernet eth1 firewall out name 'rule_eth1_out'
set interfaces ethernet eth2 address '192.168.10.10/24'
set interfaces ethernet eth2 firewall in name 'rule_eth2_in'
set interfaces ethernet eth2 firewall out name 'rule_eth2_out'
set interfaces loopback lo
set service ssh
set system config-management commit-revisions '100'
set system conntrack modules ftp
set system conntrack modules h323
set system conntrack modules nfs
set system conntrack modules pptp
set system conntrack modules sip
set system conntrack modules sqlnet
set system conntrack modules tftp
set system host-name 'vyos01'
set system login user vyos authentication encrypted-password '$6$MjV2YvKQ56q$QbL562qhRoyUu8OaqrXagicvcsNpF1HssCY06ZxxghDJkBCfSfTE/4FlFB41xZcd/HqYyVBuRt8Zyq3ozJ0dc.'
set system login user vyos authentication plaintext-password ''
set system login user vyos authentication public-keys vagrant key 'AAAAB3NzaC1yc2EAAAADAQABAAABAQCmB7BUjRYVGn/eDCJu/ZG9OIdIfpN4LKfcjLrxnjCUEEHjxouiTZNsEq+1iLgKTWyIwK2iux+sziax8HtdwQtwnWlVJEH8S5ftWrA53K86IHAH16O6ct6MbIzqQKUTUYHs6hxgW5tfD3oCVRV7WFWyPQ3sVTCno+5yaJSW/q/pbXTz93DKFJvLDptw7i4OTduhAOURv4++zIQi4YgXXGtuMwwRzDvqdKk8wCPGUN3HzRdOWN2EJf1Lj+YNHbk6LoJzvKFMhCOPR4ytSYZBa0JGjE35Z8mY1FBHUjFWaM5YNwpOHoE63515WhM/uoO89/1JvlXBwHQgE+vD+xvxh72x'
set system login user vyos authentication public-keys vagrant type 'ssh-rsa'
set system name-server 'eth0'
set system ntp server time1.vyos.net
set system ntp server time2.vyos.net
set system ntp server time3.vyos.net
set system syslog global facility all level 'notice'
set system syslog global facility protocols level 'debug'
```


## 参考(初期config)
```
vyos@vyos01:~$ show configuration commands
set interfaces ethernet eth0 address 'dhcp'
set interfaces ethernet eth0 hw-id '08:00:27:8d:c0:4d'
set interfaces ethernet eth0 offload gro
set interfaces ethernet eth0 offload gso
set interfaces ethernet eth0 offload sg
set interfaces ethernet eth0 offload tso
set interfaces ethernet eth1 address '192.168.0.2/24'
set interfaces ethernet eth2 address '192.168.10.10/24'
set interfaces loopback lo
set service ssh
set system config-management commit-revisions '100'
set system conntrack modules ftp
set system conntrack modules h323
set system conntrack modules nfs
set system conntrack modules pptp
set system conntrack modules sip
set system conntrack modules sqlnet
set system conntrack modules tftp
set system host-name 'vyos01'
set system login user vyos authentication encrypted-password '$6$MjV2YvKQ56q$QbL562qhRoyUu8OaqrXagicvcsNpF1HssCY06ZxxghDJkBCfSfTE/4FlFB41xZcd/HqYyVBuRt8Zyq3ozJ0dc.'
set system login user vyos authentication plaintext-password ''
set system login user vyos authentication public-keys vagrant key 'AAAAB3NzaC1yc2EAAAADAQABAAABAQCmB7BUjRYVGn/eDCJu/ZG9OIdIfpN4LKfcjLrxnjCUEEHjxouiTZNsEq+1iLgKTWyIwK2iux+sziax8HtdwQtwnWlVJEH8S5ftWrA53K86IHAH16O6ct6MbIzqQKUTUYHs6hxgW5tfD3oCVRV7WFWyPQ3sVTCno+5yaJSW/q/pbXTz93DKFJvLDptw7i4OTduhAOURv4++zIQi4YgXXGtuMwwRzDvqdKk8wCPGUN3HzRdOWN2EJf1Lj+YNHbk6LoJzvKFMhCOPR4ytSYZBa0JGjE35Z8mY1FBHUjFWaM5YNwpOHoE63515WhM/uoO89/1JvlXBwHQgE+vD+xvxh72x'
set system login user vyos authentication public-keys vagrant type 'ssh-rsa'
set system name-server 'eth0'
set system ntp server time1.vyos.net
set system ntp server time2.vyos.net
set system ntp server time3.vyos.net
set system syslog global facility all level 'notice'
set system syslog global facility protocols level 'debug'
```


## 参考（Vagrantfile)
```
Vagrant.configure("2") do |config|
    config.vm.box = "vyos/current"
    config.vm.synced_folder ".", "/vagrant", disabled: true

    config.vm.define :vyos01 do | vyos01 |
      vyos01.vm.hostname = "vyos01"
      vyos01.vm.network :private_network, ip: "192.168.0.2", virtualbox__intnet: "A-LAN"
      vyos01.vm.network :private_network, ip: "192.168.10.10", virtualbox__intnet: "B-LAN"
      vyos01.vm.network "forwarded_port", guest: 22, host: 10022, id: "ssh"
    end

    config.vm.define :web01 do | web01 |
      web01.vm.box = "centos/7"
      web01.vm.hostname = "web01"
      web01.vm.network :private_network, ip: "192.168.0.1", virtualbox__intnet: "A-LAN"
      web01.vm.network "forwarded_port", guest: 22, host: 12221, id: "ssh"
    end
    config.vm.define :sv01 do | sv01 |
      sv01.vm.box = "centos/7"
      sv01.vm.hostname = "sv01"
      sv01.vm.network :private_network, ip: "192.168.10.11", virtualbox__intnet: "B-LAN"
      sv01.vm.network "forwarded_port", guest: 22, host: 12222, id: "ssh"
    end
    config.vm.define :sv02 do | sv02 |
      sv02.vm.box = "centos/7"
      sv02.vm.hostname = "sv02"
      sv02.vm.network :private_network, ip: "192.168.10.12", virtualbox__intnet: "B-LAN"
      sv02.vm.network "forwarded_port", guest: 22, host: 12223, id: "ssh"
    end
end
```


## 参考(試験サーバの設定と接続確認結果)
```
共通
# yum install -y docker
# systemctl start docker
# systemctl enable docker
Web01 
# docker run --name web01_nginx -d -p 8080:80 nginx
# docker run --name web01_httpd -d -p 80:80 httpd
# ip route add 192.168.10.0/24 via 192.168.0.2 dev eth1
SV01,SV02
# ip route add 192.168.0.0/24 via 192.168.10.10 dev eth1
SV01
# docker run --name sv01_nginx -d -p 8180:80 nginx
SV02
# docker run --name sv02_nginx -d -p 8280:80 nginx


コンテナ起動状態
[root@web01 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
4d43793253e9        httpd               "httpd-foreground"       9 seconds ago       Up 7 seconds        0.0.0.0:80->80/tcp     web01_httpd
9814ea12cf14        nginx               "/docker-entrypoin..."   40 seconds ago      Up 38 seconds       0.0.0.0:8080->80/tcp   web01_nginx
[root@sv01 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
0edfcf25ca85        nginx               "/docker-entrypoin..."   36 seconds ago      Up 34 seconds       0.0.0.0:8180->80/tcp   sv01_nginx
[root@sv01 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
0edfcf25ca85        nginx               "/docker-entrypoin..."   36 seconds ago      Up 34 seconds       0.0.0.0:8180->80/tcp   sv01_nginx


疎通確認
[root@web01 ~]# curl http://192.168.10.11:8180
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
(省略)
[root@web01 ~]# curl http://192.168.10.12:8280
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
(省略)
[root@sv01 ~]# curl http://192.168.0.1:80
<html><body><h1>It works!</h1></body></html>
[root@sv01 ~]# curl http://192.168.0.1:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
(省略)
[root@sv02 ~]# curl http://192.168.0.1:80
<html><body><h1>It works!</h1></body></html>
[root@sv02 ~]# curl http://192.168.0.1:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
(省略)
```

以上