反向代理 ssh 服务
Zane Lv4

Security Frp & SSH

关于如何配置我认为比较安全的 Frp反向代理 并通过 ssh 远程连接环境。


前言

现在我的的开发环境更多的是使用一种「Thin Client」的方式,指将更多的开发环境放置需要被远程连接的机器,而前端的计算机则通过连接服务器进行开发。

具体环境就是在工作中笔记本作为前端提供 ssh 连接服务器的能力,而后端的服务器提供服务,并具备包括编辑环境、编译环境的能力。

更换笔记本以后,旧的本身就已经是使用的 Arch Linux 发行版,所以非常适合这种方式。但这就引入了另一个问题,我该如何将这种方式带到任何地点而非仅仅是局域网。

概览

security-framework

如上图,拥有公网 IP 的 Cloud Server 不仅对外提供各种服务同时作为 frp server 端对需要的内网机器提供反向代理的能力。为了保证建立连接时的可靠性,在 FRP 间采用 TLS 双向认证的策略,在 ssh 连接设置上关闭密码验证,而是采用 authorized_keys 管理客户端公钥的方式进行连接认证。

Frp

关于概念就不过多介绍了,主要功能就是反向代理的应用,支持多种协议,并且还支持连接的 TLS 双向认证,安全地将搭建在内网的服务暴露到公网。

证书与密钥生成

  • 证书请求配置
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
cat > my-openssl.cnf << EOF
[ ca ]
default_ca = CA_default
[ CA_default ]
x509_extensions = usr_cert
[ req ]
default_bits = 2048
default_md = sha256
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca
string_mask = utf8only
[ req_distinguished_name ]
[ req_attributes ]
[ usr_cert ]
basicConstraints = CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = CA:true
EOF
  • 生成 CA 证书
1
2
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.ca.com" -days 5000 -out ca.crt
  • 生成 frps 证书
1
2
3
4
5
6
7
8
9
10
11
12
openssl genrsa -out server.key 4096

openssl req -new -sha256 -key server.key \
-subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=server.com" \
-reqexts SAN \
-config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:localhost,IP:127.0.0.1,DNS:example.server.com")) \
-out server.csr

openssl x509 -req -days 365 -sha256 \
-in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(printf "subjectAltName=DNS:localhost,IP:127.0.0.1,DNS:example.server.com") \
-out server.crt

此处的 IP 需要使用实际的公网 IP 地址。

  • 生成 frpc 证书
1
2
3
4
5
6
7
8
9
10
11
openssl genrsa -out client.key 4096
openssl req -new -sha256 -key client.key \
-subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=client.com" \
-reqexts SAN \
-config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:client.com,DNS:example.client.com")) \
-out client.csr

openssl x509 -req -days 365 -sha256 \
-in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(printf "subjectAltName=DNS:client.com,DNS:example.client.com") \
-out client.crt

frp 配置

  • frps 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
vim /etc/frp/conf/frpc.ini
"""
[common]
bind_prt = 7000
bind_addr = 0.0.0.0
dashboard_port = 7500
dashboard_user = 用户名
dashboard_pwd = password # 用于登陆 dashboard

tls_enable = true
tls_verify_client = true
tls_trusted_ca_file = /etc/frp/ssl/ca.crt
tls_cert_file = /etc/frp/ssl/server.crt
tls_key_file = /etc/frp/ssl/server.key

allow_ports = 6000-6010
"""
  • frpc 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
vim /etc/frp/conf/frpc.ini
"""
[common]
server_addr = 公网 IP
server_port = 7000

tls_enable = true
tls_cert_file = /etc/frp/ssl/client.crt
tls_key_file = /etc/frp/ssl/client.key
tls_trusted_ca_file = /etc/frp/ssl/ca.crt
tls_server_name = # 公网 IP 或 域名,取决于证书时的 SAN 内容

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6001
"""

配置文件的目的主要是指定本地的双向认证的证书与密钥文件,包括将 frpc 的 22 端口映射到 frps 的 6001 端口。

注:.ini的配置文件格式在未来可能弃用,建议采用 yaml 或 toml 格式

Systemd 管理

  • frpc.service 配置
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
vim /etc/systemd/system/frpc.service
"""
[Unit]
Description=FRP Server Daemon
After=network.target

[Service]
Type=simple
WorkingDirectory=/etc/frp
ExecStart=/usr/local/bin/frpc -c /etc/frp/conf/frpc.ini
Restart=always
RestartSec=5

# security
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadOnlyDirectories=/
ReadWriteDirectories=/etc/frp
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
"""
sudo systemctl enable frpc.service
sudo systemctl start frpc.service
  • frpc.service 配置
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
vim /etc/systemd/system/frps.service
"""
[Unit]
Description=FRP Server Daemon
After=network.target

[Service]
Type=simple
WorkingDirectory=/etc/frp
ExecStart=/usr/local/bin/frps -c /etc/frp/conf/frps.ini
Restart=always
RestartSec=5

PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadOnlyDirectories=/
ReadWriteDirectories=/etc/frp
NoNewPrivileges=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
"""
sudo systemctl enable frps.service
sudo systemctl start frps.service

SSH

ssh 的配置主要是针对于内网提供 ssh 服务的机器,尽可能更多的限制连接的条件,我能想到的就是只使用主动添加到 authorized_keys 中保存的机器公钥。

  • 提供 ssh 服务方
1
2
3
4
5
vim /etc/ssl/sshd.conf
# 配置以下字段
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile ~/.ssh/authorized_keys
  • ssh 客户端
1
2
ssh-keygen -t en25519 -C "example@mail.com"
cat ~/.ssh/id_ed25519.pub

将公钥的内容放置 ssh 服务端的需要被使用的用户的 ~/.ssh/authorized_keys 中。

连接

1
ssh -p 6001 user_name@xxx.xxx.xxx.xxx

ssh连接公网IP指定端口和用户名即可。

参考

由 Hexo 驱动 & 主题 Keep