Make Nginx the default public gateway
This commit is contained in:
1 parent
6d5973bea3
commit
ff4c36bcce
10 files changed
+136
-66
No files matched your search
@@ -330,7 +330,6 @@ func (s *Server) RegisterRoutes(r *gin.Engine) {
|
||||
admin.PUT("/system/identity", idp.Configure)
|
||||
admin.POST("/system/identity/gateway", identityprovider.SecureGateway(), s.identityGateway)
|
||||
admin.PUT("/system/tls/domain", s.setTLSDomain)
|
||||
admin.PUT("/system/tls/mode", s.setTLSMode)
|
||||
admin.GET("/system/tls/nginx-preview", s.nginxPreview)
|
||||
admin.GET("/system/tls/nginx-setup", s.nginxSetupStatus)
|
||||
admin.POST("/system/tls/nginx-setup", s.initializeNginx)
|
||||
|
||||
@@ -27,7 +27,7 @@ func (s *Server) nginxSetupStatus(c *gin.Context) {
|
||||
func (s *Server) activateNginx(c *gin.Context) {
|
||||
status := tlsservice.Status()
|
||||
if status.Mode != "nginx" || status.ConfiguredMode != "nginx" || status.ConfiguredDomain == "" {
|
||||
c.JSON(http.StatusPreconditionFailed, gin.H{"error": "请先启用 Nginx 模式并保存公网入口;切换入口模式需重启 opsx-vpn"})
|
||||
c.JSON(http.StatusPreconditionFailed, gin.H{"error": "请先完成旧入口迁移并保存 Nginx 公网入口"})
|
||||
return
|
||||
}
|
||||
scheme := status.ConfiguredScheme
|
||||
@@ -127,27 +127,10 @@ func (s *Server) nginxPreview(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"http_site": httpSite, "https_site": httpsSite,
|
||||
"public_base": status.ConfiguredPublicBase,
|
||||
"warning": "仅预览:保存并重启后按协议启用入口;认证域名须单独配置。",
|
||||
"warning": "仅预览:首次启用站点需手动执行,已启用的 Nginx 入口保存后热重载。",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) setTLSMode(c *gin.Context) {
|
||||
var req struct {
|
||||
Mode string `json:"mode"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请输入入口模式"})
|
||||
return
|
||||
}
|
||||
if err := tlsservice.ConfigureMode(service.BaseDir(), req.Mode); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
store.Audit(c.GetString("username"), "tls_mode_configured", req.Mode, "需检查外部入口后重启")
|
||||
status := tlsservice.Status()
|
||||
c.JSON(http.StatusOK, gin.H{"status": status, "restart_required": status.RestartRequired})
|
||||
}
|
||||
|
||||
func (s *Server) setTLSDomain(c *gin.Context) {
|
||||
var req struct {
|
||||
Domain string `json:"domain"`
|
||||
@@ -167,6 +150,10 @@ func (s *Server) setTLSDomain(c *gin.Context) {
|
||||
port = *req.Port
|
||||
}
|
||||
status := tlsservice.Status()
|
||||
if status.Mode != "nginx" || status.ConfiguredMode != "nginx" {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "当前使用旧 Go 直监听入口,请按部署文档迁移至 Nginx 后再修改公网入口"})
|
||||
return
|
||||
}
|
||||
newBase := tlsservice.PublicBaseURL(scheme, strings.ToLower(strings.TrimSpace(req.Domain)), port)
|
||||
if newBase == "" || port < 0 || port > 65535 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "公网地址、协议或端口无效;IPv4 地址仅支持 HTTP"})
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"vpn-platform/backend/internal/tlsservice"
|
||||
)
|
||||
|
||||
func TestTLSStatusRequiresAdmin(t *testing.T) {
|
||||
@@ -47,3 +49,21 @@ func TestTLSStatusRequiresAdmin(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyGoGatewayRejectsPublicEntryChanges(t *testing.T) {
|
||||
t.Setenv("VPN_TLS_MODE", "autocert")
|
||||
t.Setenv("VPN_TLS_DOMAIN", "")
|
||||
if err := tlsservice.Start(http.NotFoundHandler(), t.TempDir()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gin.SetMode(gin.TestMode)
|
||||
router := gin.New()
|
||||
router.PUT("/api/system/tls/domain", (&Server{}).setTLSDomain)
|
||||
request := httptest.NewRequest(http.MethodPut, "/api/system/tls/domain", bytes.NewBufferString(`{"domain":"other.example.com","scheme":"https","port":443}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
if response.Code != http.StatusConflict {
|
||||
t.Fatalf("legacy Go mode should require manual migration: %d %s", response.Code, response.Body.String())
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func configFromEnvironment(dataDir string) (configuration, error) {
|
||||
mode = raw
|
||||
}
|
||||
if mode == "" {
|
||||
mode = "autocert"
|
||||
mode = "nginx"
|
||||
}
|
||||
if mode != "autocert" && mode != "nginx" {
|
||||
return configuration{}, fmt.Errorf("VPN_TLS_MODE 仅支持 autocert 或 nginx")
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestDomainValidationAndRestart(t *testing.T) {
|
||||
t.Setenv("VPN_TLS_MODE", "autocert")
|
||||
for _, domain := range []string{"test-link.appx365.com", "a.b"} {
|
||||
if !ValidDomain(domain) {
|
||||
t.Errorf("valid domain rejected: %s", domain)
|
||||
@@ -73,6 +74,7 @@ func TestHTTPIPv4PublicEntry(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFailedHTTPSBindReleasesHTTPPort(t *testing.T) {
|
||||
t.Setenv("VPN_TLS_MODE", "autocert")
|
||||
t.Setenv("VPN_TLS_DOMAIN", "test-link.appx365.com")
|
||||
occupied, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
@@ -118,6 +120,30 @@ func TestNginxModeDoesNotBindGoListeners(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultModeIsNginxAndExplicitGoModeIsPreserved(t *testing.T) {
|
||||
t.Setenv("VPN_TLS_MODE", "")
|
||||
t.Setenv("VPN_TLS_DOMAIN", "")
|
||||
t.Setenv("VPN_TLS_SCHEME", "")
|
||||
t.Setenv("VPN_TLS_PORT", "")
|
||||
dir := t.TempDir()
|
||||
if err := ConfigurePublicEntry(dir, "https", "vpn.example.com", 443); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := Start(http.NotFoundHandler(), dir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := Status(); got.Mode != "nginx" || got.Enabled || got.ConfiguredPublicBase != "https://vpn.example.com" {
|
||||
t.Fatalf("default must keep the public listener under Nginx control: %+v", got)
|
||||
}
|
||||
if err := writePersisted(dir, "tls-mode", "autocert"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
config, err := configFromEnvironment(dir)
|
||||
if err != nil || config.mode != "autocert" {
|
||||
t.Fatalf("explicit legacy mode must be readable: %+v, %v", config, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNginxEntryCanAdoptWithoutRestart(t *testing.T) {
|
||||
t.Setenv("VPN_TLS_MODE", "")
|
||||
t.Setenv("VPN_TLS_DOMAIN", "")
|
||||
|
||||
Reference in new issue
Block a user