Add Fortinet hostcheck support

This commit is contained in:
世界
2026-08-30 17:41:44 +08:00
parent 866f93fa2d
commit b491cac8f7
6 changed files with 114 additions and 44 deletions
@@ -58,6 +58,10 @@
}
]
},
"fortinet_host_check": {
"hostcheck": "",
"check_virtual_desktop": ""
},
"no_udp": false,
"dtls_local_port": 0,
"compression_disabled": false,
@@ -342,6 +346,30 @@ TNCC machine certificate path in PEM format.
Conflict with `tncc.certificates.certificate`.
### fortinet_host_check
Fortinet hostcheck result override.
Hostcheck is disabled by default. It is enabled only when `fortinet_host_check.hostcheck` is non-empty. No operating system, security product, or network interface information is collected automatically.
When enabled and a successful Fortinet login response requests hostcheck, both configured values are submitted to the server before the VPN session is used. The values are sent unchanged as `application/x-www-form-urlencoded` fields.
Some Fortinet servers only request hostcheck from recognized FortiClient user agents. Configure `user_agent` when required by the server policy.
### fortinet_host_check.hostcheck
Fortinet hostcheck result string.
The conventional format is `<security-status>,<os-version>`, for example `0100,10.0.19042`. `security-status` contains four `0` or `1` characters representing, in order, third-party firewall, third-party antivirus, FortiClient firewall, and FortiClient antivirus.
An empty value disables Fortinet hostcheck, even if `fortinet_host_check.check_virtual_desktop` is configured.
### fortinet_host_check.check_virtual_desktop
Fortinet virtual desktop check result string.
FortiClient conventionally sends colon-separated MAC addresses joined by `|`, for example `74:78:27:4d:81:93|84:1b:77:3a:95:84`. An empty value is submitted as an empty field when hostcheck is enabled.
### no_udp
Disable the DTLS or ESP secondary data channel and use the TLS data channel only.
@@ -58,6 +58,10 @@
}
]
},
"fortinet_host_check": {
"hostcheck": "",
"check_virtual_desktop": ""
},
"no_udp": false,
"dtls_local_port": 0,
"compression_disabled": false,
@@ -342,6 +346,30 @@ PEM 格式的 TNCC 机器证书路径。
`tncc.certificates.certificate` 冲突。
### fortinet_host_check
Fortinet hostcheck 结果覆盖选项。
默认禁用 hostcheck。仅当 `fortinet_host_check.hostcheck` 非空时启用。不会自动收集操作系统、安全产品或网络接口信息。
启用后,如果成功的 Fortinet 登录响应要求 hostcheck,将在使用 VPN 会话前向服务器提交两个配置值。这些值不经修改,作为 `application/x-www-form-urlencoded` 字段发送。
部分 Fortinet 服务器只会要求可识别的 FortiClient User-Agent 执行 hostcheck。服务器策略有要求时请配置 `user_agent`
### fortinet_host_check.hostcheck
Fortinet hostcheck 结果字符串。
通常格式为 `<security-status>,<os-version>`,例如 `0100,10.0.19042``security-status` 包含四个 `0``1` 字符,依次表示第三方防火墙、第三方杀毒软件、FortiClient 防火墙和 FortiClient 杀毒软件。
空值会禁用 Fortinet hostcheck,即使配置了 `fortinet_host_check.check_virtual_desktop`
### fortinet_host_check.check_virtual_desktop
Fortinet virtual desktop 检查结果字符串。
FortiClient 通常发送以冒号分隔的 MAC 地址,多个地址使用 `|` 连接,例如 `74:78:27:4d:81:93|84:1b:77:3a:95:84`。启用 hostcheck 时,空值会作为空字段提交。
### no_udp
禁用 DTLS 或 ESP 辅助数据通道,仅使用 TLS 数据通道。
+1 -1
View File
@@ -47,7 +47,7 @@ require (
github.com/sagernet/sing v0.9.0-beta.3
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3
github.com/sagernet/sing-mux v0.3.5
github.com/sagernet/sing-openconnect v0.0.0-20260721013312-6c25fa7e089a
github.com/sagernet/sing-openconnect v0.0.0-20260721123934-0d945d43288f
github.com/sagernet/sing-openvpn v0.0.0-20260721005523-64b754d1c277
github.com/sagernet/sing-quic v0.6.4-0.20260803041931-6c84c468bea2
github.com/sagernet/sing-shadowsocks v0.2.8
+2 -2
View File
@@ -317,8 +317,8 @@ github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3 h1:3y6
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3/go.mod h1:XEqEDYRCAYLaoPjZ1ifVWJg5iWAJHL2gOAXe/PM28Cg=
github.com/sagernet/sing-mux v0.3.5 h1:RHnhVEc+SFqkrK4xMygYjDwwLhzp2Bj3lztSukONfhI=
github.com/sagernet/sing-mux v0.3.5/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
github.com/sagernet/sing-openconnect v0.0.0-20260721013312-6c25fa7e089a h1:DafepSfytV5uShjQctZ0Cnw+Q9cOBIMT88p8jU2DSck=
github.com/sagernet/sing-openconnect v0.0.0-20260721013312-6c25fa7e089a/go.mod h1:4AKZLVcvY3r54UaK2Gbnm7aN8pOwdLz+y4EP0QFZ5Eg=
github.com/sagernet/sing-openconnect v0.0.0-20260721123934-0d945d43288f h1:FKT5eCVyNiRNpa5jov6Fl9aQpAgKuCmkuhqVXvBK8b4=
github.com/sagernet/sing-openconnect v0.0.0-20260721123934-0d945d43288f/go.mod h1:4AKZLVcvY3r54UaK2Gbnm7aN8pOwdLz+y4EP0QFZ5Eg=
github.com/sagernet/sing-openvpn v0.0.0-20260721005523-64b754d1c277 h1:4H38L3OxOx1fGEuH4n9lh/5O7XtZTgQ/1V/gdQ+b+Es=
github.com/sagernet/sing-openvpn v0.0.0-20260721005523-64b754d1c277/go.mod h1:PWX7WygD8jpwfqfaGNySXpJYTn0SOwjBI1BKHHC2+Bw=
github.com/sagernet/sing-quic v0.6.4-0.20260803041931-6c84c468bea2 h1:XhJro6+Ou+WOPjfs22m14lY7Sh6sWPm/f0QiyFUgM60=
+6
View File
@@ -25,6 +25,7 @@ type OpenConnectEndpointOptions struct {
CSD *OpenConnectCSDOptions `json:"csd,omitempty"`
HIP *OpenConnectHIPOptions `json:"hip,omitempty"`
TNCC *OpenConnectTNCCOptions `json:"tncc,omitempty"`
FortinetHostCheck *OpenConnectFortinetHostCheckOptions `json:"fortinet_host_check,omitempty"`
NoUDP bool `json:"no_udp,omitempty"`
DTLSLocalPort uint16 `json:"dtls_local_port,omitempty"`
CompressionDisabled bool `json:"compression_disabled,omitempty"`
@@ -79,6 +80,11 @@ type OpenConnectTNCCOptions struct {
Certificates []OpenConnectTNCCCertificateOptions `json:"certificates,omitempty"`
}
type OpenConnectFortinetHostCheckOptions struct {
HostCheck string `json:"hostcheck,omitempty"`
CheckVirtualDesktop string `json:"check_virtual_desktop,omitempty"`
}
type OpenConnectTNCCCertificateOptions struct {
Certificate badoption.Listable[string] `json:"certificate,omitempty"`
CertificatePath string `json:"certificate_path,omitempty"`
+8
View File
@@ -254,6 +254,13 @@ func (e *Endpoint) buildClientOptions(options option.OpenConnectEndpointOptions,
Certificates: tnccCertificates,
}
}
var fortinetHostCheckOptions *openconnect.FortinetHostCheckOptions
if options.FortinetHostCheck != nil {
fortinetHostCheckOptions = &openconnect.FortinetHostCheckOptions{
HostCheck: options.FortinetHostCheck.HostCheck,
CheckVirtualDesktop: options.FortinetHostCheck.CheckVirtualDesktop,
}
}
formEntries := common.Map(options.FormEntries, func(entry option.OpenConnectFormEntryOptions) openconnect.FormEntry {
return openconnect.FormEntry{
FormID: entry.FormID,
@@ -280,6 +287,7 @@ func (e *Endpoint) buildClientOptions(options option.OpenConnectEndpointOptions,
CSD: csdOptions,
HIP: hipOptions,
TNCC: tnccOptions,
FortinetHostCheck: fortinetHostCheckOptions,
NoUDP: options.NoUDP,
DTLSLocalPort: options.DTLSLocalPort,
CompressionDisabled: options.CompressionDisabled,