feat: 网站支持启动 http3 (#6501)

Refs https://github.com/1Panel-dev/1Panel/issues/3641
pull/6538/head
zhengkunwang 2 months ago committed by GitHub
parent 1bca3a182e
commit 8a5cb6c946
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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 {

@ -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 {

@ -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)
}
@ -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
}
@ -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
@ -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

@ -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)
@ -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 {
@ -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})
}
@ -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 {
@ -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
@ -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)
@ -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{})
@ -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

@ -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,
@ -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 {
@ -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")

@ -296,6 +296,7 @@ export namespace Website {
httpConfig: string;
SSLProtocol: string[];
algorithm: string;
http3: boolean;
}
export interface HTTPSConfig {
@ -306,6 +307,7 @@ export namespace Website {
algorithm: string;
hsts: boolean;
httpsPort?: string;
http3: boolean;
}
export interface CheckReq {

@ -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',

@ -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: '',

@ -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: '',

@ -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"
@ -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>
@ -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
@ -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"
@ -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();
@ -300,6 +307,7 @@ const get = () => {
form.acmeAccountID = data.SSL.acmeAccountId;
}
form.hsts = data.hsts;
form.http3 = data.http3;
form.httpsPort = data.httpsPort;
}
listSSL();

Loading…
Cancel
Save