Skip to content

Commit

Permalink
feat: 网站支持启动 http3 (#6501)
Browse files Browse the repository at this point in the history
Refs #3641
  • Loading branch information
zhengkunwang223 committed Sep 14, 2024
1 parent 1bca3a1 commit 8a5cb6c
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 49 deletions.
1 change: 1 addition & 0 deletions agent/app/dto/request/website.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ type WebsiteHTTPSOp struct {
Algorithm string `json:"algorithm"`
Hsts bool `json:"hsts"`
HttpsPorts []int `json:"httpsPorts"`
Http3 bool `json:"http3"`
}

type WebsiteNginxUpdate struct {
Expand Down
1 change: 1 addition & 0 deletions agent/app/dto/response/website.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type WebsiteHTTPS struct {
Hsts bool `json:"hsts"`
HttpsPorts []int `json:"httpsPorts"`
HttpsPort string `json:"httpsPort"`
Http3 bool `json:"http3"`
}

type WebsiteLog struct {
Expand Down
54 changes: 29 additions & 25 deletions agent/app/service/website.go
Original file line number Diff line number Diff line change
Expand Up @@ -620,28 +620,24 @@ func (w WebsiteService) UpdateWebsiteDomain(req request.WebsiteDomainUpdate) err
if err != nil {
return err
}
if website.Protocol == constant.ProtocolHTTPS {
nginxFull, err := getNginxFull(&website)
if err != nil {
return nil
}
nginxConfig := nginxFull.SiteConfig
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
var params []string
if domain.SSL {
params = append(params, "ssl", "http2")
}
server.UpdateListen(strconv.Itoa(domain.Port), false, params...)
if website.IPV6 {
server.UpdateListen("[::]:"+strconv.Itoa(domain.Port), false, params...)
}
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
if err = nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName); err != nil {
return err
}
nginxFull, err := getNginxFull(&website)
if err != nil {
return nil
}
nginxConfig := nginxFull.SiteConfig
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
server.DeleteListen(strconv.Itoa(domain.Port))
if website.IPV6 {
server.DeleteListen("[::]:" + strconv.Itoa(domain.Port))
}
http3 := isHttp3(server)
setListen(server, strconv.Itoa(domain.Port), website.IPV6, http3, website.DefaultServer, domain.SSL && website.Protocol == constant.ProtocolHTTPS)
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
if err = nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName); err != nil {
return err
}
return websiteDomainRepo.Save(context.TODO(), &domain)
}
Expand Down Expand Up @@ -921,7 +917,7 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS,
} else {
res.HttpConfig = constant.HTTPToHTTPS
}
params, err := getNginxParamsByKeys(constant.NginxScopeServer, []string{"ssl_protocols", "ssl_ciphers", "add_header"}, &website)
params, err := getNginxParamsByKeys(constant.NginxScopeServer, []string{"ssl_protocols", "ssl_ciphers", "add_header", "listen"}, &website)
if err != nil {
return res, err
}
Expand All @@ -932,8 +928,13 @@ func (w WebsiteService) GetWebsiteHTTPS(websiteId uint) (response.WebsiteHTTPS,
if p.Name == "ssl_ciphers" {
res.Algorithm = p.Params[0]
}
if p.Name == "add_header" && len(p.Params) > 0 && p.Params[0] == "Strict-Transport-Security" {
res.Hsts = true
if p.Name == "add_header" && len(p.Params) > 0 {
if p.Params[0] == "Strict-Transport-Security" {
res.Hsts = true
}
if p.Params[0] == "Alt-Svc" {
res.Http3 = true
}
}
}
return res, nil
Expand Down Expand Up @@ -998,6 +999,9 @@ func (w WebsiteService) OpWebsiteHTTPS(ctx context.Context, req request.WebsiteH
dto.NginxParam{
Name: "ssl_ciphers",
},
dto.NginxParam{
Name: "http2",
},
)
if err = deleteNginxConfig(constant.NginxScopeServer, nginxParams, &website); err != nil {
return nil, err
Expand Down
78 changes: 57 additions & 21 deletions agent/app/service/website_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,7 @@ func configDefaultNginx(website *model.Website, domains []model.WebsiteDomain, a
var serverNames []string
for _, domain := range domains {
serverNames = append(serverNames, domain.Domain)
server.UpdateListen(strconv.Itoa(domain.Port), false)
if website.IPV6 {
server.UpdateListen("[::]:"+strconv.Itoa(domain.Port), false)
}
setListen(server, strconv.Itoa(domain.Port), website.IPV6, false, website.DefaultServer, false)
}
server.UpdateServerName(serverNames)

Expand Down Expand Up @@ -463,6 +460,17 @@ func delWafConfig(website model.Website, force bool) error {
return nil
}

func isHttp3(server *components.Server) bool {
for _, listen := range server.Listens {
for _, param := range listen.Parameters {
if param == "quic" {
return true
}
}
}
return false
}

func addListenAndServerName(website model.Website, domains []model.WebsiteDomain) error {
nginxFull, err := getNginxFull(&website)
if err != nil {
Expand All @@ -471,16 +479,10 @@ func addListenAndServerName(website model.Website, domains []model.WebsiteDomain
nginxConfig := nginxFull.SiteConfig
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
http3 := isHttp3(server)

for _, domain := range domains {
var params []string
if website.Protocol == constant.ProtocolHTTPS && domain.SSL {
params = append(params, "ssl", "http2")
}
server.UpdateListen(strconv.Itoa(domain.Port), false, params...)
if website.IPV6 {
server.UpdateListen("[::]:"+strconv.Itoa(domain.Port), false, params...)
}
setListen(server, strconv.Itoa(domain.Port), website.IPV6, http3, website.DefaultServer, website.Protocol == constant.ProtocolHTTPS && domain.SSL)
server.UpdateServerName([]string{domain.Domain})
}

Expand Down Expand Up @@ -512,6 +514,24 @@ func deleteListenAndServerName(website model.Website, binds []string, domains []
return nginxCheckAndReload(nginxConfig.OldContent, nginxConfig.FilePath, nginxFull.Install.ContainerName)
}

func setListen(server *components.Server, port string, ipv6, http3, defaultServer, ssl bool) {
var params []string
if ssl {
params = []string{"ssl"}
}
server.UpdateListen(port, defaultServer, params...)
if ssl && http3 {
server.UpdateListen(port, defaultServer, "quic")
}
if !ipv6 {
return
}
server.UpdateListen("[::]:"+port, defaultServer, params...)
if ssl && http3 {
server.UpdateListen("[::]:"+port, defaultServer, "quic")
}
}

func removeSSLListen(website model.Website, binds []string) error {
nginxFull, err := getNginxFull(&website)
if err != nil {
Expand All @@ -520,11 +540,13 @@ func removeSSLListen(website model.Website, binds []string) error {
nginxConfig := nginxFull.SiteConfig
config := nginxFull.SiteConfig.Config
server := config.FindServers()[0]
http3 := isHttp3(server)
for _, bind := range binds {
server.UpdateListen(bind, false)
server.DeleteListen(bind)
if website.IPV6 {
server.UpdateListen("[::]:"+bind, false)
server.DeleteListen("[::]:" + bind)
}
setListen(server, bind, website.IPV6, http3, website.DefaultServer, website.Protocol == constant.ProtocolHTTPS)
}
if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
Expand Down Expand Up @@ -609,13 +631,11 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
httpPortIPV6 := "[::]:" + httpPort

for _, port := range httpsPort {
httpsPortIPV6 := "[::]:" + strconv.Itoa(port)
server.UpdateListen(strconv.Itoa(port), website.DefaultServer, "ssl", "http2")
if website.IPV6 {
server.UpdateListen(httpsPortIPV6, website.DefaultServer, "ssl", "http2")
}
setListen(server, strconv.Itoa(port), website.IPV6, req.Http3, website.DefaultServer, true)
}

server.UpdateDirective("http2", []string{"on"})

switch req.HttpConfig {
case constant.HTTPSOnly:
server.RemoveListenByBind(httpPort)
Expand All @@ -642,11 +662,21 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
if !req.Hsts {
server.RemoveDirective("add_header", []string{"Strict-Transport-Security", "\"max-age=31536000\""})
}
if !req.Http3 {
for _, port := range httpsPort {
server.RemoveListen(strconv.Itoa(port), "quic")
if website.IPV6 {
httpsPortIPV6 := "[::]:" + strconv.Itoa(port)
server.RemoveListen(httpsPortIPV6, "quic")
}
}
server.RemoveDirective("add_header", []string{"Alt-Svc"})
}

if err := nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return err
}
if err := createPemFile(*website, websiteSSL); err != nil {
if err = createPemFile(*website, websiteSSL); err != nil {
return err
}
nginxParams := getNginxParamsFromStaticFile(dto.SSL, []dto.NginxParam{})
Expand All @@ -670,6 +700,12 @@ func applySSL(website *model.Website, websiteSSL model.WebsiteSSL, req request.W
Params: []string{"Strict-Transport-Security", "\"max-age=31536000\""},
})
}
if req.Http3 {
nginxParams = append(nginxParams, dto.NginxParam{
Name: "add_header",
Params: []string{"Alt-Svc", "'h3=\":443\"; ma=2592000'"},
})
}

if err := updateNginxConfig(constant.NginxScopeServer, nginxParams, website); err != nil {
return err
Expand Down
26 changes: 25 additions & 1 deletion agent/utils/nginx/components/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,19 @@ func (s *Server) AddListen(bind string, defaultServer bool, params ...string) {
s.Listens = append(s.Listens, listen)
}

func isSameArray(arr1, arr2 []string) bool {
set1 := make(map[string]struct{})
for _, v := range arr1 {
set1[v] = struct{}{}
}
for _, v := range arr2 {
if _, exists := set1[v]; !exists {
return false
}
}
return true
}

func (s *Server) UpdateListen(bind string, defaultServer bool, params ...string) {
listen := &ServerListen{
Bind: bind,
Expand All @@ -185,7 +198,7 @@ func (s *Server) UpdateListen(bind string, defaultServer bool, params ...string)
var newListens []*ServerListen
exist := false
for _, li := range s.Listens {
if li.Bind == bind {
if li.Bind == bind && isSameArray(li.Parameters, params) {
exist = true
newListens = append(newListens, listen)
} else {
Expand All @@ -209,6 +222,17 @@ func (s *Server) DeleteListen(bind string) {
s.Listens = newListens
}

func (s *Server) RemoveListen(bind string, params ...string) {
var newListens []*ServerListen
for _, li := range s.Listens {
if li.Bind == bind && isSameArray(li.Parameters, params) {
continue
}
newListens = append(newListens, li)
}
s.Listens = newListens
}

func (s *Server) DeleteServerName(name string) {
var names []string
dirs := s.FindDirectives("server_name")
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/api/interface/website.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ export namespace Website {
httpConfig: string;
SSLProtocol: string[];
algorithm: string;
http3: boolean;
}

export interface HTTPSConfig {
Expand All @@ -306,6 +307,7 @@ export namespace Website {
algorithm: string;
hsts: boolean;
httpsPort?: string;
http3: boolean;
}

export interface CheckReq {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2202,6 +2202,8 @@ const message = {
ipFromExample2: "If the frontend is a CDN, you can enter the CDN's IP address range",
ipFromExample3:
'If unsure, you can enter 0.0.0.0/0 (ipv4) ::/0 (ipv6) [Note: Allowing any source IP is not secure]',
http3Helper:
'HTTP/3 is an upgrade to HTTP/2, offering faster connection speeds and better performance, but not all browsers support HTTP/3. Enabling it may cause some browsers to be unable to access the site.',
},
php: {
short_open_tag: 'Short tag support',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/tw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2049,6 +2049,8 @@ const message = {
ipFromExample1: '如果前端是 Frp 等工具可以填寫 Frp IP 地址類似 127.0.0.1',
ipFromExample2: '如果前端是 CDN可以填寫 CDN IP 地址段',
ipFromExample3: '如果不確定可以填 0.0.0.0/0ipv4) ::/0ipv6) [注意允許任意來源 IP 不安全]',
http3Helper:
'HTTP/3 HTTP/2 的升級版本提供更快的連線速度和更好的性能但並非所有瀏覽器都支援 HTTP/3啟用後可能會導致部分瀏覽器無法訪問',
},
php: {
short_open_tag: '短標簽支持',
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lang/modules/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2050,6 +2050,8 @@ const message = {
ipFromExample1: '如果前端是 Frp 等工具可以填写 Frp IP 地址类似 127.0.0.1',
ipFromExample2: '如果前端是 CDN可以填写 CDN IP 地址段',
ipFromExample3: '如果不确定可以填 0.0.0.0/0ipv4) ::/0ipv6) [注意允许任意来源 IP 不安全]',
http3Helper:
'HTTP/3 HTTP/2 的升级版本提供更快的连接速度和更好的性能但是不是所有浏览器都支持 HTTP/3开启后可能会导致部分浏览器无法访问',
},
php: {
short_open_tag: '短标签支持',
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/views/website/website/config/basic/https/index.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<el-row :gutter="20" v-loading="loading">
<el-col :xs="24" :sm="18" :md="18" :lg="18" :xl="14">
<el-col :xs="24" :sm="18" :md="18" :lg="14" :xl="14">
<el-form
class="moblie-form"
ref="httpsForm"
Expand Down Expand Up @@ -29,8 +29,12 @@
<el-checkbox v-model="form.hsts">{{ $t('commons.button.enable') }}</el-checkbox>
<span class="input-help">{{ $t('website.hstsHelper') }}</span>
</el-form-item>
<el-form-item :label="'HTTP3'" prop="http3">
<el-checkbox v-model="form.http3">{{ $t('commons.button.enable') }}</el-checkbox>
<span class="input-help">{{ $t('website.http3Helper') }}</span>
</el-form-item>
<el-form-item :label="$t('website.sslConfig')" prop="type">
<el-select v-model="form.type" @change="changeType(form.type)">
<el-select v-model="form.type" @change="changeType(form.type)" class="p-w-400">
<el-option :label="$t('website.oldSSL')" :value="'existed'"></el-option>
<el-option :label="$t('website.manualSSL')" :value="'manual'"></el-option>
</el-select>
Expand All @@ -41,6 +45,7 @@
v-model="form.acmeAccountID"
:placeholder="$t('website.selectAcme')"
@change="listSSL"
class="p-w-400"
>
<el-option :key="0" :label="$t('website.imported')" :value="0"></el-option>
<el-option
Expand All @@ -61,6 +66,7 @@
v-model="form.websiteSSLId"
:placeholder="$t('website.selectSSL')"
@change="changeSSl(form.websiteSSLId)"
class="p-w-400"
>
<el-option
v-for="(ssl, index) in ssls"
Expand Down Expand Up @@ -205,6 +211,7 @@ const form = reactive({
'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED',
SSLProtocol: ['TLSv1.3', 'TLSv1.2', 'TLSv1.1', 'TLSv1'],
httpsPort: '443',
http3: false,
});
const loading = ref(false);
const ssls = ref();
Expand Down Expand Up @@ -300,6 +307,7 @@ const get = () => {
form.acmeAccountID = data.SSL.acmeAccountId;
}
form.hsts = data.hsts;
form.http3 = data.http3;
form.httpsPort = data.httpsPort;
}
listSSL();
Expand Down

0 comments on commit 8a5cb6c

Please sign in to comment.