feat(server): 添加自动 SSL 证书管理功能

- 新增 autossl 包,用于管理 SSL 证书
- 在 server/manager.go 中集成自动 SSL 证书管理
- 修改 utils/util.go,增加 NormalizePathWithR 函数以支持相对路径规范化
- 更新 model/model.go,为 HttpServerConfig 添加 EnableSSL 字段和 ConfPath 字段
- 修改 gohttp.go,支持从配置文件加载服务器配置
This commit is contained in:
程广 2025-06-13 17:49:04 +08:00
parent 2730c773fb
commit 6c43f6ce59
7 changed files with 93 additions and 16 deletions

4
go.mod
View File

@ -11,6 +11,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang-jwt/jwt/v5 v5.2.1
github.com/nanmu42/gzip v1.2.0 github.com/nanmu42/gzip v1.2.0
github.com/samber/lo v1.39.0 github.com/samber/lo v1.39.0
golang.org/x/crypto v0.39.0
) )
@ -29,11 +30,10 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/signalsciences/ac v1.2.0 // indirect github.com/signalsciences/ac v1.2.0 // indirect
github.com/ugorji/go/codec v1.2.6 // indirect github.com/ugorji/go/codec v1.2.6 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
golang.org/x/net v0.40.0 // indirect golang.org/x/net v0.40.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.26.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
) )

8
go.sum
View File

@ -84,8 +84,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -110,8 +110,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"os" "os"
"path/filepath"
"git.pyer.club/kingecg/gohttpd/admin" "git.pyer.club/kingecg/gohttpd/admin"
"git.pyer.club/kingecg/gohttpd/model" "git.pyer.club/kingecg/gohttpd/model"
@ -102,6 +103,26 @@ func LoadConfig() {
// 规范化管理员服务器配置中的路径 // 规范化管理员服务器配置中的路径
normalizeServer(model.Config.Admin) normalizeServer(model.Config.Admin)
if model.Config.IncludDir != "" {
model.Config.IncludDir = utils.NormalizePath(model.Config.IncludDir)
configs, err := filepath.Glob(model.Config.IncludDir + "/*.json")
if err == nil {
for _, config := range configs {
server := model.HttpServerConfig{}
// read config content and unmarshal
content, err := os.ReadFile(config)
if err == nil {
err = json.Unmarshal(content, &server)
if err == nil {
server.ConfPath = config
normalizeServer(&server)
model.Config.Servers = append(model.Config.Servers, &server)
}
}
}
}
}
// 遍历配置中的所有服务器,规范化每个服务器配置中的路径 // 遍历配置中的所有服务器,规范化每个服务器配置中的路径
for _, server := range model.Config.Servers { for _, server := range model.Config.Servers {
normalizeServer(server) normalizeServer(server)
@ -120,13 +141,28 @@ func LoadConfig() {
func normalizeServer(server *model.HttpServerConfig) { func normalizeServer(server *model.HttpServerConfig) {
for index, path := range server.Paths { for index, path := range server.Paths {
if path.Root != "" { if path.Root != "" {
server.Paths[index].Root = utils.NormalizePath(path.Root) if server.ConfPath != "" {
server.Paths[index].Root = utils.NormalizePathWithR(server.ConfPath, path.Root)
} else {
server.Paths[index].Root = utils.NormalizePath(path.Root)
}
} }
} }
if server.CertFile != "" { if server.CertFile != "" {
server.CertFile = utils.NormalizePath(server.CertFile) if server.ConfPath != "" {
server.CertFile = utils.NormalizePathWithR(server.ConfPath, server.CertFile)
} else {
server.CertFile = utils.NormalizePath(server.CertFile)
}
// server.CertFile = utils.NormalizePath(server.CertFile)
} }
if server.KeyFile != "" { if server.KeyFile != "" {
server.KeyFile = utils.NormalizePath(server.KeyFile) if server.ConfPath != "" {
server.KeyFile = utils.NormalizePathWithR(server.ConfPath, server.KeyFile)
} else {
server.KeyFile = utils.NormalizePath(server.KeyFile)
}
// server.KeyFile = utils.NormalizePath(server.KeyFile)
} }
} }

View File

@ -40,11 +40,13 @@ type HttpServerConfig struct {
Paths []HttpPath `json:"paths"` Paths []HttpPath `json:"paths"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
EnableSSL bool `json:"enable_ssl"`
CertFile string `json:"certfile"` CertFile string `json:"certfile"`
KeyFile string `json:"keyfile"` KeyFile string `json:"keyfile"`
Directives []string `json:"directives"` Directives []string `json:"directives"`
AuthType string `json:"auth_type"` AuthType string `json:"auth_type"`
Jwt *JwtConfig `json:"jwt"` Jwt *JwtConfig `json:"jwt"`
ConfPath string // 配置文件路径,在加载配置时写入,如果没有就是主配置文件的路径
// 健康检查配置 // 健康检查配置
// 访问控制配置 // 访问控制配置
@ -60,9 +62,10 @@ type JwtConfig struct {
} }
type GoHttpdConfig struct { type GoHttpdConfig struct {
Logging gologger.LoggersConfig `json:"logging"` Logging gologger.LoggersConfig `json:"logging"`
Admin *HttpServerConfig `json:"admin"` Admin *HttpServerConfig `json:"admin"`
Servers []*HttpServerConfig `json:"servers"` IncludDir string `json:"includ_dir"`
Servers []*HttpServerConfig `json:"servers"`
} }
var DefaultAdminConfig HttpServerConfig = HttpServerConfig{ var DefaultAdminConfig HttpServerConfig = HttpServerConfig{

29
server/autossl.go Normal file
View File

@ -0,0 +1,29 @@
package server
import (
"context"
"crypto/tls"
"git.pyer.club/kingecg/gohttpd/utils"
"golang.org/x/crypto/acme/autocert"
)
var CertManager *autocert.Manager
func InitCertManager(certDir string) {
CertManager = &autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: hostPolicy,
Cache: autocert.DirCache(utils.NormalizePath(certDir)),
}
}
func GetTlsConfig() *tls.Config {
if CertManager == nil {
InitCertManager(utils.NormalizePath("./certs"))
}
return CertManager.TLSConfig()
}
func hostPolicy(ctx context.Context, host string) error {
return nil
}

View File

@ -112,8 +112,13 @@ func (s *ServerListener) StartServer(name string) {
server.l.Listener = s.listener.Match(makeMatcher(serverName, s)) server.l.Listener = s.listener.Match(makeMatcher(serverName, s))
} }
if server.Conf.CertFile != "" && server.Conf.KeyFile != "" { if server.Conf.EnableSSL {
err = server.ServeTLS(server.l, server.Conf.CertFile, server.Conf.KeyFile) if server.Conf.CertFile != "" && server.Conf.KeyFile != "" {
err = server.ServeTLS(server.l, server.Conf.CertFile, server.Conf.KeyFile)
} else {
server.Server.TLSConfig = GetTlsConfig()
err = server.ServeTLS(server.l, "", "")
}
} else { } else {
err = server.Serve(server.l) err = server.Serve(server.l)
} }

View File

@ -24,11 +24,15 @@ func GetExecDir() string {
func NormalizePath(path string) string { func NormalizePath(path string) string {
// return filepath.ToSlash(filepath.Clean(path)) // return filepath.ToSlash(filepath.Clean(path))
return NormalizePathWithR(GetExecDir(), path)
}
func NormalizePathWithR(rootDir, path string) string {
p := filepath.ToSlash(path) p := filepath.ToSlash(path)
if filepath.IsAbs(p) { if filepath.IsAbs(p) {
return p return p
} else { } else {
return filepath.Join(GetExecDir(), p) return filepath.Join(rootDir, p)
} }
} }