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:
parent
2730c773fb
commit
6c43f6ce59
4
go.mod
4
go.mod
|
@ -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
8
go.sum
|
@ -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=
|
||||||
|
|
42
gohttp.go
42
gohttp.go
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue