WireGuard 系列文章(六):Netmaker 安装

本文最后更新于:2022年1月22日 晚上

系列文章前情提要:

  1. WireGuard 系列文章(一):什么是 VPN
  2. WireGuard 系列文章(二):WireGuard 简介 - 快速、现代、安全的 VPN 隧道
  3. WireGuard 系列文章(三):WireGuard 安装
  4. WireGuard 系列文章(四):WireGuard 快速上手
  5. WireGuard 系列文章(五):Netmaker 简介 - 创建和管理 WireGuard 网络的平台

接下来开始安装 Netmaker。

🔐 安全

我本人非常重视安全,买了 NAS 后,你会在 NAS 上看到海量的被攻击日志,所以不要心存侥幸。
最基础的实现安全的方式就是:SSL + 认证 + 防火墙。
所以以上 3 块我都会默认启用,当然,由此也必然会带来了一些安装、配置上的复杂度。

本次 Netmaker 安装会启用所有相关的安全特性。为了启用安全特性,你至少需要有属于自己的 域名

〇、前提条件

  • 来自公有云的云服务器
    • 具有公网静态 IP
    • 最小 1C1G
    • 2GB+ 的存储空间
    • 安装 Ubuntu 20.04 操作系统
  • 域名
    • 公网域名(如我的域名是 ewhisper.cn)并备案(备案过程就略过了)
    • 允许和访问通过 DNS 服务(如我的 DNS 供应商是 DNSPod) 修改 DNS 记录

一、准备 DNS

创建一条指向你云服务器的公网 IP 的通配符记录,例如,*.netmaker.ewhisper.cn
如我在 DNSPod 控制台上的 DNS 记录配置如下:

Netmaker 通配符记录

后面的过程会用这个通配符创建 3 个子域名:

  • dashboard.netmaker.ewhisper.cn
  • api.netmaker.ewhisper.cn
  • grpc.netmaker.ewhisper.cn

二、安装依赖项

包括:

  • docker
  • docker-compose
  • wireguard
1
2
3
ssh root@your-host
sudo apt-get update
sudo apt-get install -y docker.io docker-compose wireguard

三、打开防火墙

确保在云服务器和云安全组上为 Netmaker 设置了防火墙设置。

确保以下端口在 VM 和云安全组中都是打开的:

  • 443(tcp): 用于 Dashboard、REST API 和 gRPC
  • 355(udp 和 tcp):用于 CoreDNS
  • 51820-51830:对于 WireGuard-Netmaker,每个网络需要一个端口,从 51821 开始(Wireguard 默认是 51820),因此根据计划拥有的网络数量开放一个范围。例如,51820-51830。
  • ICMP:允许 ICMP

云服务器上的防火墙,开启命令如下:

1
sudo ufw allow proto tcp from any to any port 443 && sudo ufw allow 53/udp && sudo ufw allow 53/tcp && sudo ufw allow 51820:51830/udp

同样,基于您的云提供商,您可能还需要为您的服务器设置入站安全规则。这将取决于您的云供应商。

以天翼云 / 华为云为例,云安全组配置如下:

Netmaker 云安全组配置

四、安装 Netmaker

⚠️ 警告

COREDNS_IP:根据您的云提供商,公网 IP 可能不会直接绑定到您正在运行的 VM。在这种情况下,CoreDNS 不能绑定到这个 IP,您应该使用计算机上默认接口的 IP 来代替 COREDNS_IP。在很多情况下,这个命令会为你提供正确的 CoreDNS IP: (就是云服务器的内网 IP)

ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p'

现在,插入基本域(通配符)(domain)、公网 ip(public ip) 和 coredns ip 的值。

1
2
3
4
wget -O docker-compose.yml https://raw.githubusercontent.com/gravitl/netmaker/master/compose/docker-compose.contained.yml
sed -i 's/NETMAKER_BASE_DOMAIN/<your base domain>/g' docker-compose.yml
sed -i 's/SERVER_PUBLIC_IP/<your server ip>/g' docker-compose.yml
sed -i 's/COREDNS_IP/<default interface ip>/g' docker-compose.yml

在这里:

  • <your base domain>:是类似这种:netmaker.ewhisper.cn
  • <your server ip>:就是公网 IP
  • <default interface ip>: 就是私网 IP,如:192.168.1.226

docker-compose 内容如下(基于原始内容做了部分调整,调整内容见注释):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
version: "3.4"

services:
netmaker:
container_name: netmaker
image: gravitl/netmaker:v0.9.1
volumes:
- ./dnsconfig:/root/config/dnsconfig # 将 dnsconfig 直接放到当前目录
- /usr/bin/wg:/usr/bin/wg
- ./sqldata:/root/data # 将数据库数据也放到当前目录
- /etc/netclient/config:/etc/netclient/config # Netmaker Server 所在的主机会作为 netclient 的一员,将 netclient config 的配置文件放到主机的 /etc/netclient/config 目录
cap_add:
- NET_ADMIN
restart: always
privileged: true
environment:
SERVER_HOST: "SERVER_PUBLIC_IP"
SERVER_API_CONN_STRING: "api.NETMAKER_BASE_DOMAIN:443"
SERVER_GRPC_CONN_STRING: "grpc.NETMAKER_BASE_DOMAIN:443"
COREDNS_ADDR: "SERVER_PUBLIC_IP"
GRPC_SSL: "on"
DNS_MODE: "on"
SERVER_HTTP_HOST: "api.NETMAKER_BASE_DOMAIN"
SERVER_GRPC_HOST: "grpc.NETMAKER_BASE_DOMAIN"
API_PORT: "8081"
GRPC_PORT: "50051"
CLIENT_MODE: "on"
MASTER_KEY: "REPLACE_MASTER_KEY"
SERVER_GRPC_WIREGUARD: "off"
CORS_ALLOWED_ORIGIN: "*"
DISPLAY_KEYS: "on"
DATABASE: "sqlite"
NODE_ID: "netmaker-server-1"
network_mode: host # 这里直接使用 host 网络模式
netmaker-ui:
container_name: netmaker-ui
depends_on:
- netmaker
image: gravitl/netmaker-ui:v0.9.1
links:
- "netmaker:api"
ports:
- "8082:80"
environment:
BACKEND_URL: "https://api.NETMAKER_BASE_DOMAIN"
restart: always
coredns:
depends_on:
- netmaker
image: coredns/coredns
command: -conf /root/dnsconfig/Corefile
container_name: coredns
restart: always
ports:
- "COREDNS_IP:53:53/udp"
- "COREDNS_IP:53:53/tcp"
volumes:
- ./dnsconfig:/root/dnsconfig
# 我的 DNS 域名托管在 DNSPod 上,无法通过默认方式自动申请证书,所以不使用容器方式的 caddy,而是使用 安装了 dnspod 插件的 apt 安装的 caddy
# caddy:
# image: caddy:latest
# container_name: caddy
# restart: unless-stopped
# network_mode: host # Wants ports 80 and 443!
# volumes:
# - /root/Caddyfile:/etc/caddy/Caddyfile
# # - $PWD/site:/srv # you could also serve a static site in site folder
# - caddy_data:/data
# - caddy_conf:/config
# 都指定了具体的目录,所以 volumes 也不需要了
# volumes:
# caddy_data: {}
# caddy_conf: {}
# sqldata: {}
# dnsconfig: {}

生成一个唯一的主密钥并插入它:

1
2
tr -dc A-Za-z0-9 </dev/urandom | head -c 30 ; echo ''
sed -i 's/REPLACE_MASTER_KEY/<your generated key>/g' docker-compose.yml

⚠️ 重要

保存好这个密钥,以便将来与 API 一起使用。

4.1 准备 Caddy

⚠️ 注意

我的 DNS 域名托管在 DNSPod 上,无法通过默认方式自动申请证书,所以不使用容器方式的 caddy,而是使用 安装了 dnspod 插件的 apt 安装的 caddy。
另一种可行的方式是自行 docker build 将 dnspod 插件编译到容器中并使用。

安装这个包时,Caddy 会自动启动并作为一个名为 Caddy 的 systemd 服务运行。

1
2
3
4
5
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo tee /etc/apt/trusted.gpg.d/caddy-stable.asc
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

4.1.1 Caddy 使用 DNSPod 模块

Caddy 2 使用了一个 新的和改进的 DNS 提供者接口 来解决 ACME DNS 的 challenge。

所有您需要做的就是将您需要的服务提供商(如 DNSPod)插入到您的构建中,然后将 DNS challenge 添加到您的配置中!

获得 DNSPod DNS 插件

  1. 访问 Caddy 下载
  2. 在模块列表中找到您的 DNS 提供程序
    找到 Caddy dnspod
  3. 下载,比如我的 linux amd64 版本的 dnspod 插件下载地址为:https://caddyserver.com/api/download?os=linux&arch=amd64&p=github.com%2Fcaddy-dns%2Fdnspod&idempotency=14234280192478

4.1.2 将默认安装的 Caddy 替换为下载的带有 dnspod 插件的 caddy

这个过程旨在简化运行定制的 caddy 二进制文件,同时保留 caddy 包中的支持文件。

此过程允许用户利用官方包中的默认配置、 systemd 服务文件和 bash-completion。

前提:

  • 已安装 caddy
  • 已下载好带有 dns (本次是 dnspod) 插件的 caddy

步骤:

1
2
3
4
dpkg-divert --divert /usr/bin/caddy.default --rename /usr/bin/caddy
mv ./caddy /usr/bin/caddy.custom
update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.default 10
update-alternatives --install /usr/bin/caddy caddy /usr/bin/caddy.custom 50

dpkg-divert/usr/bin/caddy 二进制文件移动到 /usr/bin/caddy.default,并在任何包想要在这个位置安装文件的情况下放置一个转移。

update-alternatives 将创建一个从所需的 caddy 二进制到 /usr/bin/caddy 的符号链接

通过执行下面命令,可以在自定义二进制代码和默认二进制代码之间进行更改

1
update-alternatives --config caddy

并在屏幕上做出选择。

4.1.3 创建 Caddyfile 并启动 Caddy

下面创建 Caddyfile 并启动 Caddy,这里以通过 dnspod 自动向 LetsEncrypt 或 ZeroSSL 申请免费 SSL 证书为例:

前提条件

  1. 买个域名(这个是必须要花钱的,但是一些冷门域名很便宜)
  2. 知道自己的域名是哪个 dns 提供商并拥有相应的 token。以 dnspod 为例,从这里申请 token

vi /etc/caddy/Caddyfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
# LetsEncrypt account
email cuikaidong@foxmail.com
acme_dns dnspod <dnspod_id>,<dnspod_token> # 将 <> 中的内容替换为对应的 dnspod id 和 token
}

# Dashboard
https://dashboard.netmaker.ewhisper.cn {
reverse_proxy http://127.0.0.1:8082
}

# API
https://api.netmaker.ewhisper.cn {
reverse_proxy http://127.0.0.1:8081
}

# gRPC
https://grpc.netmaker.ewhisper.cn {
reverse_proxy h2c://127.0.0.1:50051
}

上面配置很通俗易懂,就不做解释了。

启动 Caddy:

1
systemctl start caddy.service

4.1.4 Caddy 自动申请证书并对外服务

然后,Caddy 会通过 LetsEncrypt 或 ZeroSSL 自动申请证书并定期续约。通过向 dnspod 增加我的域名的 dns 解析来验证这个域名确实属于我(这种证书申请方式就是下面日志中提到的:"challenge_type":"dns-01")。如下图:

通过 DNSPod token 添加 dns 记录的操作日志

之后会获得 LetsEncrypt 或 ZeroSSL 颁发的证书,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Dec 06 16:05:46 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806746.1932435,"logger":"tls.issuance.acme.acme_client","msg":"trying to solve challenge","identifier":"dashboard.netmaker.ewhisper.cn","challenge_type":"dns-01","ca":"http>
Dec 06 16:05:53 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806752.9999793,"logger":"tls.issuance.acme.acme_client","msg":"validations succeeded; finalizing order","order":"https://acme-staging-v02.api.letsencrypt.org/acme/order/360>
Dec 06 16:05:53 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806753.1332862,"logger":"tls.issuance.acme.acme_client","msg":"validations succeeded; finalizing order","order":"https://acme-staging-v02.api.letsencrypt.org/acme/order/360>
Dec 06 16:05:53 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806753.6776898,"logger":"tls.issuance.acme.acme_client","msg":"successfully downloaded available certificate chains","count":1,"first_url":"https://acme-staging-v02.api.let>
Dec 06 16:05:53 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806753.6779523,"logger":"tls.issuance.acme","msg":"waiting on internal rate limiter","identifiers":["grpc.netmaker.ewhisper.cn"],"ca":"https://acme-v02.api.letsencrypt.org/>
Dec 06 16:05:53 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806753.677963,"logger":"tls.issuance.acme","msg":"done waiting on internal rate limiter","identifiers":["grpc.netmaker.ewhisper.cn"],"ca":"https://acme-v02.api.letsencrypt.>
Dec 06 16:05:56 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806756.0425541,"logger":"tls.issuance.acme.acme_client","msg":"trying to solve challenge","identifier":"grpc.netmaker.ewhisper.cn","challenge_type":"dns-01","ca":"https://a>
Dec 06 16:05:59 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806759.301592,"logger":"tls.issuance.acme.acme_client","msg":"successfully downloaded available certificate chains","count":1,"first_url":"https://acme-staging-v02.api.lets>
Dec 06 16:05:59 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806759.3018231,"logger":"tls.issuance.acme","msg":"waiting on internal rate limiter","identifiers":["dashboard.netmaker.ewhisper.cn"],"ca":"https://acme-v02.api.letsencrypt>
Dec 06 16:05:59 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806759.3018348,"logger":"tls.issuance.acme","msg":"done waiting on internal rate limiter","identifiers":["dashboard.netmaker.ewhisper.cn"],"ca":"https://acme-v02.api.letsen>
Dec 06 16:06:00 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806760.909151,"logger":"tls.issuance.acme.acme_client","msg":"trying to solve challenge","identifier":"dashboard.netmaker.ewhisper.cn","challenge_type":"dns-01","ca":"https>
Dec 06 16:06:08 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806768.1700737,"logger":"tls.issuance.acme.acme_client","msg":"validations succeeded; finalizing order","order":"https://acme-v02.api.letsencrypt.org/acme/order/310057420/4>
Dec 06 16:06:09 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806769.6764278,"logger":"tls.issuance.acme.acme_client","msg":"successfully downloaded available certificate chains","count":2,"first_url":"https://acme-v02.api.letsencrypt>
Dec 06 16:06:09 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806769.67741,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"grpc.netmaker.ewhisper.cn"}
Dec 06 16:06:09 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806769.6776047,"logger":"tls.obtain","msg":"releasing lock","identifier":"grpc.netmaker.ewhisper.cn"}
Dec 06 16:06:14 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806774.210461,"logger":"tls.issuance.acme.acme_client","msg":"validations succeeded; finalizing order","order":"https://acme-v02.api.letsencrypt.org/acme/order/310057420/45>
Dec 06 16:06:15 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806775.165971,"logger":"tls.issuance.acme.acme_client","msg":"successfully downloaded available certificate chains","count":2,"first_url":"https://acme-v02.api.letsencrypt.>
Dec 06 16:06:15 09b2brd7robnn5zi-1106883 caddy[119805]: {"level":"info","ts":1638806775.1666183,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"dashboard.netmaker.ewhisper.cn"}

ℹ️ 提示
生成的证书位于:/var/lib/caddy/.local/share/caddy/ 目录

4.2 启动 Netmaker

终于到 Netmaker 了,通过如下命令启动:

1
sudo docker-compose up -d

然后访问:dashboard.<your base domain> (本例中是:dashboard.netmaker.ewhisper.cn) 开始使用 Netmaker。

五、Netmaker 基本界面及使用

访问后,首先创建管理员。创建后会进入首页,如下:

Netmaker 首页

左侧依次为:

  1. 仪表板
  2. 网络
  3. 节点
  4. 访问密钥
  5. 外部客户端
  6. DNS
  7. 文档
  8. 管理员账号
  9. 登出
  10. 用户
  11. 版本信息

仪表板主要有 6 大部分内容:

  1. 网络
  2. 节点
  3. 访问密钥
  4. 外部客户端
  5. DNS
  6. 用户

要创建 Full Mesh 网络,基本的操作流程为:

  1. 在 Network 页面,填入网络基本信息,创建网络;
  2. 创建网络的同时,默认会将 Netmaker 所在主机加入到 Node 中,可以在 Nodes 和 DNS 页面看到该节点状态;
  3. 创建 Access Key,指定该 Key 会被多少个 Netclient 客户端使用;同时页面会弹出通过 netclient 加入网络的指令
  4. 在其他节点上,执行对应的指令,即可完成 Full Mesh 网络的组建;此后 WireGuard 的 peer 等配置会由 Netmaker 统一管理;
  5. 对于 Android 等尚不支持 Netclient 的客户端,可以先将上面的一个节点指定为 Ingress Gateway(可以理解为是 WireGuard 的中继服务器),然后创建 External Client,自动生成配置及配置二维码,安卓手机可以扫码添加配置并加入网络

参考资料