feat(tls): add optional nginx setup and gateway preview
This commit is contained in:
1 parent
9f743e19ad
commit
d2f1f5040e
15 files changed
+753
-16
No files matched your search
@@ -23,6 +23,7 @@ var domainPattern = regexp.MustCompile(`^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:
|
||||
|
||||
type configuration struct {
|
||||
domain string
|
||||
mode string
|
||||
dataDir string
|
||||
cacheDir string
|
||||
httpAddr string
|
||||
@@ -36,6 +37,8 @@ var state struct {
|
||||
|
||||
type CertificateStatus struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Mode string `json:"mode"`
|
||||
ConfiguredMode string `json:"configured_mode"`
|
||||
Domain string `json:"domain"`
|
||||
ConfiguredDomain string `json:"configured_domain"`
|
||||
RestartRequired bool `json:"restart_required"`
|
||||
@@ -55,6 +58,20 @@ func ValidDomain(domain string) bool {
|
||||
}
|
||||
|
||||
func configFromEnvironment(dataDir string) (configuration, error) {
|
||||
mode := os.Getenv("VPN_TLS_MODE")
|
||||
if mode == "" {
|
||||
if raw, err := os.ReadFile(filepath.Join(dataDir, "tls-mode")); err == nil {
|
||||
mode = strings.TrimSpace(string(raw))
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return configuration{}, fmt.Errorf("读取 TLS 模式配置: %w", err)
|
||||
}
|
||||
}
|
||||
if mode == "" {
|
||||
mode = "autocert"
|
||||
}
|
||||
if mode != "autocert" && mode != "nginx" {
|
||||
return configuration{}, fmt.Errorf("VPN_TLS_MODE 仅支持 autocert 或 nginx")
|
||||
}
|
||||
domain := strings.ToLower(strings.TrimSpace(os.Getenv("VPN_TLS_DOMAIN")))
|
||||
if domain == "" {
|
||||
if raw, err := os.ReadFile(filepath.Join(dataDir, "tls-domain")); err == nil {
|
||||
@@ -74,7 +91,7 @@ func configFromEnvironment(dataDir string) (configuration, error) {
|
||||
if httpsAddr == "" {
|
||||
httpsAddr = ":443"
|
||||
}
|
||||
return configuration{domain: domain, dataDir: dataDir, cacheDir: filepath.Join(dataDir, "acme"), httpAddr: httpAddr, httpsAddr: httpsAddr}, nil
|
||||
return configuration{domain: domain, mode: mode, dataDir: dataDir, cacheDir: filepath.Join(dataDir, "acme"), httpAddr: httpAddr, httpsAddr: httpsAddr}, nil
|
||||
}
|
||||
|
||||
func ConfigureDomain(dataDir, domain string) error {
|
||||
@@ -109,6 +126,37 @@ func ConfigureDomain(dataDir, domain string) error {
|
||||
return os.Rename(temp.Name(), filepath.Join(dataDir, "tls-domain"))
|
||||
}
|
||||
|
||||
func ConfigureMode(dataDir, mode string) error {
|
||||
if os.Getenv("VPN_TLS_MODE") != "" {
|
||||
return fmt.Errorf("当前由 VPN_TLS_MODE 环境变量管理入口模式,请修改启动配置")
|
||||
}
|
||||
if mode != "autocert" && mode != "nginx" {
|
||||
return fmt.Errorf("入口模式仅支持 autocert 或 nginx")
|
||||
}
|
||||
if err := os.MkdirAll(dataDir, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
temp, err := os.CreateTemp(dataDir, ".tls-mode-*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(temp.Name())
|
||||
defer temp.Close()
|
||||
if err := temp.Chmod(0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := temp.WriteString(mode + "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := temp.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := temp.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(temp.Name(), filepath.Join(dataDir, "tls-mode"))
|
||||
}
|
||||
|
||||
// Start keeps certificates in the persistent data directory. Issuance occurs
|
||||
// on the first TLS handshake, and autocert handles renewal.
|
||||
func Start(handler http.Handler, dataDir string) error {
|
||||
@@ -116,7 +164,7 @@ func Start(handler http.Handler, dataDir string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.domain == "" {
|
||||
if config.domain == "" || config.mode == "nginx" {
|
||||
state.Lock()
|
||||
state.config = config
|
||||
state.Unlock()
|
||||
@@ -173,10 +221,17 @@ func Status() CertificateStatus {
|
||||
state.RLock()
|
||||
config := state.config
|
||||
state.RUnlock()
|
||||
status := CertificateStatus{Enabled: config.domain != "", Domain: config.domain, DNS: []string{}, HTTPAddr: config.httpAddr, HTTPSAddr: config.httpsAddr, Renewal: "autocert"}
|
||||
status := CertificateStatus{Enabled: config.domain != "" && config.mode == "autocert", Domain: config.domain, Mode: config.mode, DNS: []string{}, HTTPAddr: config.httpAddr, HTTPSAddr: config.httpsAddr, Renewal: config.mode}
|
||||
if desired, err := configFromEnvironment(config.dataDir); err == nil {
|
||||
status.ConfiguredDomain = desired.domain
|
||||
status.RestartRequired = desired.domain != config.domain
|
||||
status.ConfiguredMode = desired.mode
|
||||
status.RestartRequired = desired.domain != config.domain || desired.mode != config.mode
|
||||
}
|
||||
if config.mode == "nginx" {
|
||||
// In proxy mode, certbot owns the certificate; the Go service does not
|
||||
// claim that Nginx is listening merely because a domain was saved.
|
||||
status.Domain = ""
|
||||
return status
|
||||
}
|
||||
if !status.Enabled {
|
||||
return status
|
||||
|
||||
Reference in new issue
Block a user