mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-30 22:26:38 +08:00
feat: replace cert_dir with support_dir in agent and server configurations
- Updated README.md to reflect the new support_dir for auxiliary files. - Refactored agent main.go to use support_dir instead of cert_dir. - Modified config.go to replace cert_dir with support_dir and added legacy support. - Adjusted config tests to validate support_dir usage. - Changed nginx manager to utilize support_dir for file paths. - Updated server configuration to use support_dir for SSL certificates. - Revised documentation to clarify the new configuration parameters. - Enhanced security checks for support file paths to prevent traversal attacks.
This commit is contained in:
@@ -214,6 +214,7 @@ Docker 镜像工作流仅构建 `atsf_server`,并产出 `linux/amd64` 与 `lin
|
||||
| `agent_token` | 节点专属认证 Token |
|
||||
| `discovery_token` | 首次自动注册使用的全局 Token |
|
||||
| `data_dir` | Agent 托管数据目录 |
|
||||
| `support_dir` | Agent 存放受管附属文件的目录,当前包含证书与 Lua 观测脚本 |
|
||||
| `openresty_observability_port` | Agent 读取 OpenResty Lua 本地观测指标的 loopback 端口 |
|
||||
| `nginx_path` | 本机 Nginx 路径,设置后走本机模式 |
|
||||
| `nginx_container_name` | Docker 模式下的 Nginx 容器名 |
|
||||
|
||||
@@ -39,8 +39,8 @@ func main() {
|
||||
Image: cfg.OpenrestyDockerImage,
|
||||
MainConfigPath: cfg.MainConfigPath,
|
||||
RouteConfigPath: cfg.RouteConfigPath,
|
||||
CertDir: cfg.CertDir,
|
||||
NginxCertDir: cfg.OpenrestyCertDir,
|
||||
SupportDir: cfg.SupportDir,
|
||||
NginxSupportDir: cfg.OpenrestySupportDir,
|
||||
OpenrestyObservabilityPort: cfg.OpenrestyObservabilityPort,
|
||||
},
|
||||
)
|
||||
@@ -50,7 +50,7 @@ func main() {
|
||||
"ip", cfg.NodeIP,
|
||||
"heartbeat_interval", cfg.HeartbeatInterval,
|
||||
"route_config", cfg.RouteConfigPath,
|
||||
"cert_dir", cfg.CertDir,
|
||||
"support_dir", cfg.SupportDir,
|
||||
)
|
||||
|
||||
client := httpclient.New(cfg.ServerURL, cfg.InitialAuthToken(), cfg.RequestTimeout.Duration())
|
||||
@@ -63,8 +63,8 @@ func main() {
|
||||
MainConfigPath: cfg.MainConfigPath,
|
||||
RouteConfigPath: cfg.RouteConfigPath,
|
||||
RuntimeRouteConfigPath: runtimeRouteConfigPath,
|
||||
CertDir: cfg.CertDir,
|
||||
NginxCertDir: cfg.OpenrestyCertDir,
|
||||
SupportDir: cfg.SupportDir,
|
||||
NginxSupportDir: cfg.OpenrestySupportDir,
|
||||
OpenrestyObservabilityPort: cfg.OpenrestyObservabilityPort,
|
||||
Executor: nginx.NewExecutor(nginx.ExecutorOptions{
|
||||
NginxPath: cfg.OpenrestyPath,
|
||||
@@ -73,8 +73,8 @@ func main() {
|
||||
Image: cfg.OpenrestyDockerImage,
|
||||
MainConfigPath: cfg.MainConfigPath,
|
||||
RouteConfigPath: cfg.RouteConfigPath,
|
||||
CertDir: cfg.CertDir,
|
||||
NginxCertDir: cfg.OpenrestyCertDir,
|
||||
SupportDir: cfg.SupportDir,
|
||||
NginxSupportDir: cfg.OpenrestySupportDir,
|
||||
OpenrestyObservabilityPort: cfg.OpenrestyObservabilityPort,
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
const (
|
||||
defaultDockerMainConfigRelativePath = "etc/nginx/nginx.conf"
|
||||
defaultDockerRouteConfigRelativePath = "etc/nginx/conf.d/atsflare_routes.conf"
|
||||
defaultCertDirRelativePath = "etc/nginx/certs"
|
||||
defaultSupportDirRelativePath = "etc/nginx/support"
|
||||
defaultDockerStateRelativePath = "var/lib/atsflare/agent-state.json"
|
||||
defaultDockerOpenRestyCertDir = "/etc/nginx/atsflare-certs"
|
||||
defaultDockerOpenRestySupportDir = "/etc/nginx/atsflare-support"
|
||||
defaultOpenRestyObservabilityPort = 18081
|
||||
)
|
||||
|
||||
@@ -35,8 +35,8 @@ type Config struct {
|
||||
DataDir string `json:"data_dir"`
|
||||
MainConfigPath string `json:"main_config_path"`
|
||||
RouteConfigPath string `json:"route_config_path"`
|
||||
CertDir string `json:"cert_dir"`
|
||||
OpenrestyCertDir string `json:"openresty_cert_dir"`
|
||||
SupportDir string `json:"support_dir"`
|
||||
OpenrestySupportDir string `json:"openresty_support_dir"`
|
||||
OpenrestyObservabilityPort int `json:"openresty_observability_port"`
|
||||
StatePath string `json:"state_path"`
|
||||
HeartbeatInterval MillisecondDuration `json:"heartbeat_interval"`
|
||||
@@ -57,8 +57,10 @@ type configFile struct {
|
||||
DataDir string `json:"data_dir"`
|
||||
MainConfigPath string `json:"main_config_path"`
|
||||
RouteConfigPath string `json:"route_config_path"`
|
||||
CertDir string `json:"cert_dir"`
|
||||
OpenrestyCertDir string `json:"openresty_cert_dir"`
|
||||
SupportDir string `json:"support_dir"`
|
||||
OpenrestySupportDir string `json:"openresty_support_dir"`
|
||||
LegacyCertDir string `json:"cert_dir"`
|
||||
LegacyOpenrestyCertDir string `json:"openresty_cert_dir"`
|
||||
OpenrestyObservabilityPort int `json:"openresty_observability_port"`
|
||||
StatePath string `json:"state_path"`
|
||||
HeartbeatInterval MillisecondDuration `json:"heartbeat_interval"`
|
||||
@@ -87,8 +89,8 @@ func Load(path string) (*Config, error) {
|
||||
DataDir: file.DataDir,
|
||||
MainConfigPath: file.MainConfigPath,
|
||||
RouteConfigPath: file.RouteConfigPath,
|
||||
CertDir: file.CertDir,
|
||||
OpenrestyCertDir: file.OpenrestyCertDir,
|
||||
SupportDir: firstNonEmpty(file.SupportDir, file.LegacyCertDir),
|
||||
OpenrestySupportDir: firstNonEmpty(file.OpenrestySupportDir, file.LegacyOpenrestyCertDir),
|
||||
OpenrestyObservabilityPort: file.OpenrestyObservabilityPort,
|
||||
StatePath: file.StatePath,
|
||||
HeartbeatInterval: file.HeartbeatInterval,
|
||||
@@ -138,14 +140,14 @@ func applyDefaults(cfg *Config, baseDir string) {
|
||||
cfg.StatePath = joinManagedPath(cfg.DataDir, defaultDockerStateRelativePath)
|
||||
}
|
||||
}
|
||||
if cfg.CertDir == "" {
|
||||
cfg.CertDir = joinManagedPath(cfg.DataDir, defaultCertDirRelativePath)
|
||||
if cfg.SupportDir == "" {
|
||||
cfg.SupportDir = joinManagedPath(cfg.DataDir, defaultSupportDirRelativePath)
|
||||
}
|
||||
if cfg.OpenrestyCertDir == "" {
|
||||
if cfg.OpenrestySupportDir == "" {
|
||||
if cfg.OpenrestyPath != "" {
|
||||
cfg.OpenrestyCertDir = cfg.CertDir
|
||||
cfg.OpenrestySupportDir = cfg.SupportDir
|
||||
} else {
|
||||
cfg.OpenrestyCertDir = defaultDockerOpenRestyCertDir
|
||||
cfg.OpenrestySupportDir = defaultDockerOpenRestySupportDir
|
||||
}
|
||||
}
|
||||
if cfg.OpenrestyObservabilityPort <= 0 {
|
||||
@@ -173,8 +175,11 @@ func normalizeManagedPaths(cfg *Config) {
|
||||
if usesSlashPath(cfg.RouteConfigPath) {
|
||||
cfg.RouteConfigPath = filepath.ToSlash(cfg.RouteConfigPath)
|
||||
}
|
||||
if usesSlashPath(cfg.CertDir) {
|
||||
cfg.CertDir = filepath.ToSlash(cfg.CertDir)
|
||||
if usesSlashPath(cfg.SupportDir) {
|
||||
cfg.SupportDir = filepath.ToSlash(cfg.SupportDir)
|
||||
}
|
||||
if usesSlashPath(cfg.OpenrestySupportDir) {
|
||||
cfg.OpenrestySupportDir = filepath.ToSlash(cfg.OpenrestySupportDir)
|
||||
}
|
||||
if usesSlashPath(cfg.StatePath) {
|
||||
cfg.StatePath = filepath.ToSlash(cfg.StatePath)
|
||||
@@ -243,6 +248,15 @@ func detectHostname() string {
|
||||
return strings.TrimSpace(host)
|
||||
}
|
||||
|
||||
func firstNonEmpty(values ...string) string {
|
||||
for _, value := range values {
|
||||
if strings.TrimSpace(value) != "" {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func detectNodeIP() string {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
|
||||
@@ -39,8 +39,8 @@ func TestLoadDockerModeUsesManagedPaths(t *testing.T) {
|
||||
if cfg.RouteConfigPath != filepath.Join(dir, "data", defaultDockerRouteConfigRelativePath) {
|
||||
t.Fatalf("unexpected route config path: %s", cfg.RouteConfigPath)
|
||||
}
|
||||
if cfg.CertDir != filepath.Join(dir, "data", defaultCertDirRelativePath) {
|
||||
t.Fatalf("unexpected cert dir: %s", cfg.CertDir)
|
||||
if cfg.SupportDir != filepath.Join(dir, "data", defaultSupportDirRelativePath) {
|
||||
t.Fatalf("unexpected support dir: %s", cfg.SupportDir)
|
||||
}
|
||||
if cfg.OpenrestyContainerName != "atsflare-openresty" {
|
||||
t.Fatalf("unexpected openresty container name: %s", cfg.OpenrestyContainerName)
|
||||
@@ -48,8 +48,8 @@ func TestLoadDockerModeUsesManagedPaths(t *testing.T) {
|
||||
if cfg.OpenrestyDockerImage != "openresty/openresty:alpine" {
|
||||
t.Fatalf("unexpected openresty image: %s", cfg.OpenrestyDockerImage)
|
||||
}
|
||||
if cfg.OpenrestyCertDir != defaultDockerOpenRestyCertDir {
|
||||
t.Fatalf("unexpected openresty cert dir: %s", cfg.OpenrestyCertDir)
|
||||
if cfg.OpenrestySupportDir != defaultDockerOpenRestySupportDir {
|
||||
t.Fatalf("unexpected openresty support dir: %s", cfg.OpenrestySupportDir)
|
||||
}
|
||||
if cfg.StatePath != filepath.Join(dir, "data", defaultDockerStateRelativePath) {
|
||||
t.Fatalf("unexpected state path: %s", cfg.StatePath)
|
||||
@@ -94,8 +94,8 @@ func TestLoadPathModeKeepsExplicitPaths(t *testing.T) {
|
||||
if cfg.StatePath != "/tmp/agent-state.json" {
|
||||
t.Fatalf("unexpected state path: %s", cfg.StatePath)
|
||||
}
|
||||
if cfg.OpenrestyCertDir != cfg.CertDir {
|
||||
t.Fatalf("expected path mode openresty cert dir to equal cert dir, got %s / %s", cfg.OpenrestyCertDir, cfg.CertDir)
|
||||
if cfg.OpenrestySupportDir != cfg.SupportDir {
|
||||
t.Fatalf("expected path mode openresty support dir to equal support dir, got %s / %s", cfg.OpenrestySupportDir, cfg.SupportDir)
|
||||
}
|
||||
if cfg.OpenrestyObservabilityPort != defaultOpenRestyObservabilityPort {
|
||||
t.Fatalf("unexpected path mode openresty observability port: %d", cfg.OpenrestyObservabilityPort)
|
||||
@@ -134,8 +134,8 @@ func TestLoadUsesCustomDataDirForGeneratedFiles(t *testing.T) {
|
||||
if cfg.StatePath != "/srv/atsflare/"+defaultDockerStateRelativePath {
|
||||
t.Fatalf("unexpected state path: %s", cfg.StatePath)
|
||||
}
|
||||
if cfg.CertDir != "/srv/atsflare/"+defaultCertDirRelativePath {
|
||||
t.Fatalf("unexpected cert dir: %s", cfg.CertDir)
|
||||
if cfg.SupportDir != "/srv/atsflare/"+defaultSupportDirRelativePath {
|
||||
t.Fatalf("unexpected support dir: %s", cfg.SupportDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"atsflare-agent/internal/protocol"
|
||||
)
|
||||
|
||||
const CertDirPlaceholder = "__ATSF_CERT_DIR__"
|
||||
const SupportDirPlaceholder = "__ATSF_SUPPORT_DIR__"
|
||||
const RouteConfigPlaceholder = "__ATSF_ROUTE_CONFIG__"
|
||||
const AccessLogPlaceholder = "__ATSF_ACCESS_LOG__"
|
||||
const LuaDirPlaceholder = "__ATSF_LUA_DIR__"
|
||||
@@ -104,8 +104,8 @@ type DockerExecutor struct {
|
||||
Image string
|
||||
MainConfigPath string
|
||||
RouteConfigDir string
|
||||
CertDir string
|
||||
NginxCertDir string
|
||||
SupportDir string
|
||||
NginxSupportDir string
|
||||
OpenrestyObservabilityPort int
|
||||
Runner CommandRunner
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func (e *DockerExecutor) runContainer(ctx context.Context) error {
|
||||
"-p", fmt.Sprintf("127.0.0.1:%d:%d", e.OpenrestyObservabilityPort, e.OpenrestyObservabilityPort),
|
||||
"-v", fmt.Sprintf("%s:%s", e.MainConfigPath, DockerMainConfigPath),
|
||||
"-v", fmt.Sprintf("%s:/etc/nginx/conf.d", e.RouteConfigDir),
|
||||
"-v", fmt.Sprintf("%s:%s", e.CertDir, e.NginxCertDir),
|
||||
"-v", fmt.Sprintf("%s:%s", e.SupportDir, e.NginxSupportDir),
|
||||
e.Image,
|
||||
}
|
||||
runOutput, runErr := e.Runner.Run(ctx, e.DockerBinary, runArgs...)
|
||||
@@ -201,8 +201,8 @@ type Manager struct {
|
||||
MainConfigPath string
|
||||
RouteConfigPath string
|
||||
RuntimeRouteConfigPath string
|
||||
CertDir string
|
||||
NginxCertDir string
|
||||
SupportDir string
|
||||
NginxSupportDir string
|
||||
OpenrestyObservabilityPort int
|
||||
Executor Executor
|
||||
}
|
||||
@@ -302,8 +302,8 @@ func (m *Manager) CurrentChecksum() (string, error) {
|
||||
normalizedMain = strings.ReplaceAll(normalizedMain, fmt.Sprintf("%d", m.OpenrestyObservabilityPort), ObservabilityPortPlaceholder)
|
||||
}
|
||||
normalizedRoute := string(data)
|
||||
if m.NginxCertDir != "" {
|
||||
normalizedRoute = strings.ReplaceAll(normalizedRoute, m.NginxCertDir, CertDirPlaceholder)
|
||||
if m.NginxSupportDir != "" {
|
||||
normalizedRoute = strings.ReplaceAll(normalizedRoute, m.NginxSupportDir, SupportDirPlaceholder)
|
||||
}
|
||||
files, err := m.readSupportFiles()
|
||||
if err != nil {
|
||||
@@ -321,8 +321,8 @@ type ExecutorOptions struct {
|
||||
Image string
|
||||
MainConfigPath string
|
||||
RouteConfigPath string
|
||||
CertDir string
|
||||
NginxCertDir string
|
||||
SupportDir string
|
||||
NginxSupportDir string
|
||||
OpenrestyObservabilityPort int
|
||||
}
|
||||
|
||||
@@ -342,9 +342,9 @@ func NewExecutor(options ExecutorOptions) Executor {
|
||||
if absDir, err := filepath.Abs(routeConfigDir); err == nil {
|
||||
routeConfigDir = absDir
|
||||
}
|
||||
certDir := options.CertDir
|
||||
if absDir, err := filepath.Abs(certDir); err == nil {
|
||||
certDir = absDir
|
||||
supportDir := options.SupportDir
|
||||
if absDir, err := filepath.Abs(supportDir); err == nil {
|
||||
supportDir = absDir
|
||||
}
|
||||
return &DockerExecutor{
|
||||
DockerBinary: options.DockerBinary,
|
||||
@@ -352,8 +352,8 @@ func NewExecutor(options ExecutorOptions) Executor {
|
||||
Image: options.Image,
|
||||
MainConfigPath: mainConfigPath,
|
||||
RouteConfigDir: routeConfigDir,
|
||||
CertDir: certDir,
|
||||
NginxCertDir: options.NginxCertDir,
|
||||
SupportDir: supportDir,
|
||||
NginxSupportDir: options.NginxSupportDir,
|
||||
OpenrestyObservabilityPort: options.OpenrestyObservabilityPort,
|
||||
Runner: runner,
|
||||
}
|
||||
@@ -426,7 +426,7 @@ func (e *DockerExecutor) runEphemeralRuntimeCommandWithBinary(ctx context.Contex
|
||||
"-v",
|
||||
fmt.Sprintf("%s:/etc/nginx/conf.d", e.RouteConfigDir),
|
||||
"-v",
|
||||
fmt.Sprintf("%s:%s", e.CertDir, e.NginxCertDir),
|
||||
fmt.Sprintf("%s:%s", e.SupportDir, e.NginxSupportDir),
|
||||
e.Image,
|
||||
runtimeBinary,
|
||||
}
|
||||
@@ -459,8 +459,8 @@ func (m *Manager) backup() (*backupState, error) {
|
||||
if err := os.MkdirAll(filepath.Dir(m.RouteConfigPath), 0o755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if m.CertDir != "" {
|
||||
if err := os.MkdirAll(m.CertDir, 0o755); err != nil {
|
||||
if m.SupportDir != "" {
|
||||
if err := os.MkdirAll(m.SupportDir, 0o755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -507,13 +507,13 @@ func (m *Manager) restore(state *backupState) error {
|
||||
} else if err := os.Remove(m.RouteConfigPath); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if m.CertDir == "" {
|
||||
if m.SupportDir == "" {
|
||||
return nil
|
||||
}
|
||||
if err := os.RemoveAll(m.CertDir); err != nil && !os.IsNotExist(err) {
|
||||
if err := os.RemoveAll(m.SupportDir); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(m.CertDir, 0o755); err != nil {
|
||||
if err := os.MkdirAll(m.SupportDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range state.Files {
|
||||
@@ -532,13 +532,13 @@ func (m *Manager) restore(state *backupState) error {
|
||||
}
|
||||
|
||||
func (m *Manager) writeSupportFiles(supportFiles []protocol.SupportFile) error {
|
||||
if m.CertDir == "" {
|
||||
if m.SupportDir == "" {
|
||||
return nil
|
||||
}
|
||||
if err := os.RemoveAll(m.CertDir); err != nil && !os.IsNotExist(err) {
|
||||
if err := os.RemoveAll(m.SupportDir); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(m.CertDir, 0o755); err != nil {
|
||||
if err := os.MkdirAll(m.SupportDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range supportFiles {
|
||||
@@ -557,17 +557,17 @@ func (m *Manager) writeSupportFiles(supportFiles []protocol.SupportFile) error {
|
||||
}
|
||||
|
||||
func (m *Manager) readSupportFiles() ([]protocol.SupportFile, error) {
|
||||
if m.CertDir == "" {
|
||||
if m.SupportDir == "" {
|
||||
return nil, nil
|
||||
}
|
||||
if _, err := os.Stat(m.CertDir); err != nil {
|
||||
if _, err := os.Stat(m.SupportDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
files := make([]protocol.SupportFile, 0)
|
||||
err := filepath.Walk(m.CertDir, func(path string, info os.FileInfo, err error) error {
|
||||
err := filepath.Walk(m.SupportDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -578,7 +578,7 @@ func (m *Manager) readSupportFiles() ([]protocol.SupportFile, error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
relativePath, err := filepath.Rel(m.CertDir, path)
|
||||
relativePath, err := filepath.Rel(m.SupportDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -598,8 +598,8 @@ func (m *Manager) readSupportFiles() ([]protocol.SupportFile, error) {
|
||||
}
|
||||
|
||||
func (m *Manager) supportFileTargetPath(relativePath string) (string, error) {
|
||||
if strings.TrimSpace(m.CertDir) == "" {
|
||||
return "", errors.New("cert dir 不能为空")
|
||||
if strings.TrimSpace(m.SupportDir) == "" {
|
||||
return "", errors.New("support dir 不能为空")
|
||||
}
|
||||
candidate := strings.TrimSpace(relativePath)
|
||||
if strings.Contains(candidate, `\`) {
|
||||
@@ -612,22 +612,22 @@ func (m *Manager) supportFileTargetPath(relativePath string) (string, error) {
|
||||
if filepath.IsAbs(normalizedPath) || filepath.VolumeName(normalizedPath) != "" {
|
||||
return "", fmt.Errorf("support file path %q must be relative", relativePath)
|
||||
}
|
||||
targetPath := filepath.Join(m.CertDir, normalizedPath)
|
||||
relativeToBase, err := filepath.Rel(m.CertDir, targetPath)
|
||||
targetPath := filepath.Join(m.SupportDir, normalizedPath)
|
||||
relativeToBase, err := filepath.Rel(m.SupportDir, targetPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if relativeToBase == ".." || strings.HasPrefix(relativeToBase, ".."+string(os.PathSeparator)) {
|
||||
return "", fmt.Errorf("support file path %q escapes cert dir", relativePath)
|
||||
return "", fmt.Errorf("support file path %q escapes support dir", relativePath)
|
||||
}
|
||||
return targetPath, nil
|
||||
}
|
||||
|
||||
func (m *Manager) renderRouteConfig(content string) string {
|
||||
if m.NginxCertDir == "" {
|
||||
if m.NginxSupportDir == "" {
|
||||
return content
|
||||
}
|
||||
return strings.ReplaceAll(content, CertDirPlaceholder, m.NginxCertDir)
|
||||
return strings.ReplaceAll(content, SupportDirPlaceholder, m.NginxSupportDir)
|
||||
}
|
||||
|
||||
func (m *Manager) renderMainConfig(content string) string {
|
||||
@@ -663,10 +663,10 @@ func (m *Manager) accessLogRuntimePath() string {
|
||||
}
|
||||
|
||||
func (m *Manager) luaRuntimePath() string {
|
||||
if strings.TrimSpace(m.NginxCertDir) == "" {
|
||||
if strings.TrimSpace(m.NginxSupportDir) == "" {
|
||||
return ""
|
||||
}
|
||||
return filepath.ToSlash(m.NginxCertDir)
|
||||
return filepath.ToSlash(m.NginxSupportDir)
|
||||
}
|
||||
|
||||
func checksum(content string) string {
|
||||
|
||||
@@ -117,14 +117,14 @@ func TestDockerExecutorCheckHealthFailsWhenContainerStopped(t *testing.T) {
|
||||
},
|
||||
}
|
||||
executor := &DockerExecutor{
|
||||
DockerBinary: "docker",
|
||||
ContainerName: "atsflare-openresty",
|
||||
Image: "openresty/openresty:alpine",
|
||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
||||
CertDir: filepath.Clean("/tmp/certs"),
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
Runner: runner,
|
||||
DockerBinary: "docker",
|
||||
ContainerName: "atsflare-openresty",
|
||||
Image: "openresty/openresty:alpine",
|
||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
||||
SupportDir: filepath.Clean("/tmp/support"),
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
Runner: runner,
|
||||
}
|
||||
if err := executor.CheckHealth(context.Background()); err == nil {
|
||||
t.Fatal("expected CheckHealth to fail when container is not running")
|
||||
@@ -141,14 +141,14 @@ func TestDockerExecutorStartsContainerWhenMissing(t *testing.T) {
|
||||
},
|
||||
}
|
||||
executor := &DockerExecutor{
|
||||
DockerBinary: "docker",
|
||||
ContainerName: "atsflare-openresty",
|
||||
Image: "openresty/openresty:alpine",
|
||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
||||
CertDir: filepath.Clean("/tmp/certs"),
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
Runner: runner,
|
||||
DockerBinary: "docker",
|
||||
ContainerName: "atsflare-openresty",
|
||||
Image: "openresty/openresty:alpine",
|
||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
||||
SupportDir: filepath.Clean("/tmp/support"),
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
Runner: runner,
|
||||
}
|
||||
|
||||
if err := executor.Test(context.Background()); err != nil {
|
||||
@@ -176,14 +176,14 @@ func TestDockerExecutorStartsStoppedContainer(t *testing.T) {
|
||||
},
|
||||
}
|
||||
executor := &DockerExecutor{
|
||||
DockerBinary: "docker",
|
||||
ContainerName: "atsflare-openresty",
|
||||
Image: "openresty/openresty:alpine",
|
||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
||||
CertDir: filepath.Clean("/tmp/certs"),
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
Runner: runner,
|
||||
DockerBinary: "docker",
|
||||
ContainerName: "atsflare-openresty",
|
||||
Image: "openresty/openresty:alpine",
|
||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
||||
SupportDir: filepath.Clean("/tmp/support"),
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
Runner: runner,
|
||||
}
|
||||
|
||||
if err := executor.Reload(context.Background()); err != nil {
|
||||
@@ -207,7 +207,7 @@ func TestDockerExecutorStartsStoppedContainer(t *testing.T) {
|
||||
func TestDockerExecutorRunContainerMountsManagedFiles(t *testing.T) {
|
||||
mainConfigPath := filepath.Clean("/tmp/managed/nginx.conf")
|
||||
routeConfigDir := filepath.Clean("/tmp/managed/conf.d")
|
||||
certDir := filepath.Clean("/tmp/managed/certs")
|
||||
supportDir := filepath.Clean("/tmp/managed/support")
|
||||
runner := &fakeRunner{}
|
||||
executor := &DockerExecutor{
|
||||
DockerBinary: "docker",
|
||||
@@ -215,8 +215,8 @@ func TestDockerExecutorRunContainerMountsManagedFiles(t *testing.T) {
|
||||
Image: "openresty/openresty:alpine",
|
||||
MainConfigPath: mainConfigPath,
|
||||
RouteConfigDir: routeConfigDir,
|
||||
CertDir: certDir,
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
SupportDir: supportDir,
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
OpenrestyObservabilityPort: 18081,
|
||||
Runner: runner,
|
||||
}
|
||||
@@ -237,7 +237,7 @@ func TestDockerExecutorRunContainerMountsManagedFiles(t *testing.T) {
|
||||
"-p", "127.0.0.1:18081:18081",
|
||||
"-v", mainConfigPath + ":" + DockerMainConfigPath,
|
||||
"-v", routeConfigDir + ":/etc/nginx/conf.d",
|
||||
"-v", certDir + ":/etc/nginx/atsflare-certs",
|
||||
"-v", supportDir + ":/etc/nginx/atsflare-support",
|
||||
"openresty/openresty:alpine",
|
||||
}
|
||||
if !reflect.DeepEqual(runner.calls[0].args, expectedArgs) {
|
||||
@@ -260,8 +260,8 @@ func TestDockerExecutorRecreatesContainerOnStartup(t *testing.T) {
|
||||
Image: "openresty/openresty:alpine",
|
||||
MainConfigPath: filepath.Clean("/tmp/nginx.conf"),
|
||||
RouteConfigDir: filepath.Clean("/tmp/routes"),
|
||||
CertDir: filepath.Clean("/tmp/certs"),
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
SupportDir: filepath.Clean("/tmp/support"),
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
OpenrestyObservabilityPort: 18081,
|
||||
Runner: runner,
|
||||
}
|
||||
@@ -287,8 +287,8 @@ func TestNewExecutorUsesAbsoluteDockerMountPath(t *testing.T) {
|
||||
Image: "openresty/openresty:alpine",
|
||||
MainConfigPath: "./data/etc/nginx/nginx.conf",
|
||||
RouteConfigPath: "./data/etc/nginx/conf.d/atsflare_routes.conf",
|
||||
CertDir: "./data/etc/nginx/certs",
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
SupportDir: "./data/etc/nginx/support",
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
OpenrestyObservabilityPort: 18081,
|
||||
})
|
||||
|
||||
@@ -330,19 +330,19 @@ func TestManagerApplyAndChecksumIncludeMainConfig(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
mainPath := filepath.Join(tempDir, "nginx.conf")
|
||||
routePath := filepath.Join(tempDir, "conf.d", "atsflare_routes.conf")
|
||||
certDir := filepath.Join(tempDir, "certs")
|
||||
supportDir := filepath.Join(tempDir, "support")
|
||||
manager := &Manager{
|
||||
MainConfigPath: mainPath,
|
||||
RouteConfigPath: routePath,
|
||||
CertDir: certDir,
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
SupportDir: supportDir,
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
Executor: &fakeExecutor{},
|
||||
}
|
||||
|
||||
err := manager.Apply(
|
||||
context.Background(),
|
||||
"include __ATSF_ROUTE_CONFIG__;\naccess_log __ATSF_ACCESS_LOG__ atsflare_json;\n",
|
||||
"ssl_certificate __ATSF_CERT_DIR__/1.crt;\n",
|
||||
"ssl_certificate __ATSF_SUPPORT_DIR__/1.crt;\n",
|
||||
[]protocol.SupportFile{{Path: "1.crt", Content: "cert"}},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -362,7 +362,7 @@ func TestManagerApplyAndChecksumIncludeMainConfig(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read route config: %v", err)
|
||||
}
|
||||
if string(routeData) != "ssl_certificate /etc/nginx/atsflare-certs/1.crt;\n" {
|
||||
if string(routeData) != "ssl_certificate /etc/nginx/atsflare-support/1.crt;\n" {
|
||||
t.Fatalf("unexpected route config: %s", string(routeData))
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ func TestManagerApplyAndChecksumIncludeMainConfig(t *testing.T) {
|
||||
}
|
||||
expected := bundleChecksum(
|
||||
"include __ATSF_ROUTE_CONFIG__;\naccess_log __ATSF_ACCESS_LOG__ atsflare_json;\n",
|
||||
"ssl_certificate __ATSF_CERT_DIR__/1.crt;\n",
|
||||
"ssl_certificate __ATSF_SUPPORT_DIR__/1.crt;\n",
|
||||
[]protocol.SupportFile{{Path: "1.crt", Content: "cert"}},
|
||||
)
|
||||
if value != expected {
|
||||
@@ -388,8 +388,8 @@ func TestManagerApplyUsesRuntimeRouteConfigPath(t *testing.T) {
|
||||
MainConfigPath: mainPath,
|
||||
RouteConfigPath: routePath,
|
||||
RuntimeRouteConfigPath: DockerRouteConfigPath,
|
||||
CertDir: filepath.Join(tempDir, "certs"),
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
SupportDir: filepath.Join(tempDir, "support"),
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
Executor: &fakeExecutor{},
|
||||
}
|
||||
|
||||
@@ -462,12 +462,12 @@ func TestManagerApplyWritesSupportFilesAndReplacesPlaceholder(t *testing.T) {
|
||||
manager := &Manager{
|
||||
MainConfigPath: filepath.Join(tempDir, "nginx.conf"),
|
||||
RouteConfigPath: filepath.Join(tempDir, "routes.conf"),
|
||||
CertDir: filepath.Join(tempDir, "certs"),
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
SupportDir: filepath.Join(tempDir, "support"),
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
Executor: &fakeExecutor{},
|
||||
}
|
||||
|
||||
err := manager.Apply(context.Background(), "include __ATSF_ROUTE_CONFIG__;", "ssl_certificate __ATSF_CERT_DIR__/1.crt;", []protocol.SupportFile{
|
||||
err := manager.Apply(context.Background(), "include __ATSF_ROUTE_CONFIG__;", "ssl_certificate __ATSF_SUPPORT_DIR__/1.crt;", []protocol.SupportFile{
|
||||
{Path: "1.crt", Content: "cert-data"},
|
||||
{Path: "1.key", Content: "key-data"},
|
||||
})
|
||||
@@ -479,10 +479,10 @@ func TestManagerApplyWritesSupportFilesAndReplacesPlaceholder(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read route config: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(routeData), "/etc/nginx/atsflare-certs/1.crt") {
|
||||
if !strings.Contains(string(routeData), "/etc/nginx/atsflare-support/1.crt") {
|
||||
t.Fatalf("expected placeholder replacement in route config, got %s", string(routeData))
|
||||
}
|
||||
certData, err := os.ReadFile(filepath.Join(manager.CertDir, "1.crt"))
|
||||
certData, err := os.ReadFile(filepath.Join(manager.SupportDir, "1.crt"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read cert file: %v", err)
|
||||
}
|
||||
@@ -495,8 +495,8 @@ func TestManagerRollbackRestoresSupportFiles(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
routePath := filepath.Join(tempDir, "routes.conf")
|
||||
mainPath := filepath.Join(tempDir, "nginx.conf")
|
||||
certDir := filepath.Join(tempDir, "certs")
|
||||
if err := os.MkdirAll(certDir, 0o755); err != nil {
|
||||
supportDir := filepath.Join(tempDir, "support")
|
||||
if err := os.MkdirAll(supportDir, 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll failed: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(mainPath, []byte("old-main"), 0o644); err != nil {
|
||||
@@ -505,14 +505,14 @@ func TestManagerRollbackRestoresSupportFiles(t *testing.T) {
|
||||
if err := os.WriteFile(routePath, []byte("old-route"), 0o644); err != nil {
|
||||
t.Fatalf("WriteFile failed: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(certDir, "1.crt"), []byte("old-cert"), 0o600); err != nil {
|
||||
if err := os.WriteFile(filepath.Join(supportDir, "1.crt"), []byte("old-cert"), 0o600); err != nil {
|
||||
t.Fatalf("WriteFile failed: %v", err)
|
||||
}
|
||||
manager := &Manager{
|
||||
MainConfigPath: mainPath,
|
||||
RouteConfigPath: routePath,
|
||||
CertDir: certDir,
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
SupportDir: supportDir,
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
Executor: &fakeExecutor{
|
||||
testErr: errors.New("openresty test failed"),
|
||||
},
|
||||
@@ -539,7 +539,7 @@ func TestManagerRollbackRestoresSupportFiles(t *testing.T) {
|
||||
if string(routeData) != "old-route" {
|
||||
t.Fatalf("expected route rollback, got %s", string(routeData))
|
||||
}
|
||||
certData, err := os.ReadFile(filepath.Join(certDir, "1.crt"))
|
||||
certData, err := os.ReadFile(filepath.Join(supportDir, "1.crt"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read cert file: %v", err)
|
||||
}
|
||||
@@ -549,8 +549,8 @@ func TestManagerRollbackRestoresSupportFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestManagerSupportFileTargetPathRejectsEscapes(t *testing.T) {
|
||||
manager := &Manager{CertDir: filepath.Join(t.TempDir(), "certs")}
|
||||
if err := os.MkdirAll(manager.CertDir, 0o755); err != nil {
|
||||
manager := &Manager{SupportDir: filepath.Join(t.TempDir(), "support")}
|
||||
if err := os.MkdirAll(manager.SupportDir, 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll failed: %v", err)
|
||||
}
|
||||
|
||||
@@ -581,8 +581,8 @@ func TestManagerSupportFileTargetPathRejectsEscapes(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("expected path %q to be accepted: %v", testCase.path, err)
|
||||
}
|
||||
if !strings.HasPrefix(targetPath, manager.CertDir) {
|
||||
t.Fatalf("expected target path %q to stay under %q", targetPath, manager.CertDir)
|
||||
if !strings.HasPrefix(targetPath, manager.SupportDir) {
|
||||
t.Fatalf("expected target path %q to stay under %q", targetPath, manager.SupportDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -592,8 +592,8 @@ func TestManagerApplyRejectsSupportFilePathTraversal(t *testing.T) {
|
||||
manager := &Manager{
|
||||
MainConfigPath: filepath.Join(tempDir, "nginx.conf"),
|
||||
RouteConfigPath: filepath.Join(tempDir, "routes.conf"),
|
||||
CertDir: filepath.Join(tempDir, "certs"),
|
||||
NginxCertDir: "/etc/nginx/atsflare-certs",
|
||||
SupportDir: filepath.Join(tempDir, "support"),
|
||||
NginxSupportDir: "/etc/nginx/atsflare-support",
|
||||
Executor: &fakeExecutor{},
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ type configBundle struct {
|
||||
}
|
||||
|
||||
const (
|
||||
nginxCertDirPlaceholder = "__ATSF_CERT_DIR__"
|
||||
nginxSupportDirPlaceholder = "__ATSF_SUPPORT_DIR__"
|
||||
nginxRouteConfigPlaceholder = "__ATSF_ROUTE_CONFIG__"
|
||||
nginxAccessLogPlaceholder = "__ATSF_ACCESS_LOG__"
|
||||
nginxLuaDirPlaceholder = "__ATSF_LUA_DIR__"
|
||||
@@ -751,8 +751,8 @@ func renderHTTPRedirectServer(domain string) string {
|
||||
}
|
||||
|
||||
func renderHTTPSServer(domain string, originURL string, certificateID uint, customHeaders []ProxyRouteCustomHeaderInput) string {
|
||||
certPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateCertFileName(certificateID))
|
||||
keyPath := fmt.Sprintf("%s/%s", nginxCertDirPlaceholder, certificateKeyFileName(certificateID))
|
||||
certPath := fmt.Sprintf("%s/%s", nginxSupportDirPlaceholder, certificateCertFileName(certificateID))
|
||||
keyPath := fmt.Sprintf("%s/%s", nginxSupportDirPlaceholder, certificateKeyFileName(certificateID))
|
||||
return fmt.Sprintf("server {\n listen 443 ssl;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n\n location / {\n%s proxy_pass %s;\n }\n}\n\n", domain, certPath, keyPath, renderProxyHeaderBlock(customHeaders), originURL)
|
||||
}
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ func TestCreateTLSCertificateAndRenderHTTPSConfig(t *testing.T) {
|
||||
if !strings.Contains(result.Version.RenderedConfig, "return 301 https://$host$request_uri;") {
|
||||
t.Fatal("expected rendered config to include http redirect")
|
||||
}
|
||||
if !strings.Contains(result.Version.RenderedConfig, "__ATSF_CERT_DIR__/") {
|
||||
t.Fatal("expected rendered config to keep certificate dir placeholder")
|
||||
if !strings.Contains(result.Version.RenderedConfig, "__ATSF_SUPPORT_DIR__/") {
|
||||
t.Fatal("expected rendered config to keep support dir placeholder for certificates")
|
||||
}
|
||||
if !strings.Contains(result.Version.SupportFilesJSON, ".crt") || !strings.Contains(result.Version.SupportFilesJSON, ".key") {
|
||||
t.Fatal("expected support files to contain certificate and key")
|
||||
|
||||
+9
-8
@@ -256,8 +256,8 @@ go run ./cmd/agent -config ./agent.json
|
||||
"openresty_path": "/usr/local/openresty/nginx/sbin/openresty",
|
||||
"main_config_path": "/usr/local/openresty/nginx/conf/nginx.conf",
|
||||
"route_config_path": "/usr/local/openresty/nginx/conf/conf.d/atsflare_routes.conf",
|
||||
"cert_dir": "/usr/local/openresty/nginx/conf/certs",
|
||||
"openresty_cert_dir": "/usr/local/openresty/nginx/conf/certs",
|
||||
"support_dir": "/usr/local/openresty/nginx/conf/support",
|
||||
"openresty_support_dir": "/usr/local/openresty/nginx/conf/support",
|
||||
"openresty_observability_port": 18081,
|
||||
"state_path": "./data/agent-state.json",
|
||||
"heartbeat_interval": 10000,
|
||||
@@ -282,8 +282,8 @@ go run ./cmd/agent -config ./agent.json
|
||||
| `data_dir` | Agent 数据目录,用于存储托管配置、证书和状态文件 | 否 | 配置文件所在目录下的 `data` 子目录 | `./data` |
|
||||
| `main_config_path` | 第五版主配置接管时 OpenResty 主配置文件写入路径 | 第五版本机模式建议必填 | Docker 模式可使用受管默认路径;本机模式建议显式设置 | `/usr/local/openresty/nginx/conf/nginx.conf` |
|
||||
| `route_config_path` | 路由配置文件写入路径 | 否 | 默认为 `data_dir` 下托管路径 | `/etc/nginx/conf.d/atsflare_routes.conf` |
|
||||
| `cert_dir` | Agent 在本机写入证书文件的目录 | 否 | 默认为 `data_dir` 下托管证书目录 | `./data/etc/nginx/certs` |
|
||||
| `openresty_cert_dir` | OpenResty 实际读取证书的目录 | 否 | 本机模式默认等于 `cert_dir`;Docker 模式默认 `/etc/nginx/atsflare-certs` | `/usr/local/openresty/nginx/conf/certs` |
|
||||
| `support_dir` | Agent 在本机写入受管附属文件的目录,当前包含证书与 Lua 观测脚本 | 否 | 默认为 `data_dir` 下托管 support 目录 | `./data/etc/nginx/support` |
|
||||
| `openresty_support_dir` | OpenResty 实际读取受管附属文件的目录 | 否 | 本机模式默认等于 `support_dir`;Docker 模式默认 `/etc/nginx/atsflare-support` | `/usr/local/openresty/nginx/conf/support` |
|
||||
| `state_path` | Agent 本地状态文件路径 | 否 | 默认为 `data_dir` 下托管状态文件 | `./data/agent-state.json` |
|
||||
| `heartbeat_interval` | 心跳间隔 | 否 | `10000` 毫秒 | `10000` |
|
||||
| `request_timeout` | HTTP 请求超时时间 | 否 | `10000` 毫秒 | `10000` |
|
||||
@@ -297,6 +297,7 @@ go run ./cmd/agent -config ./agent.json
|
||||
* `node_name` 与 `node_ip` 未填写时会自动探测;若自动探测失败,配置校验会报错
|
||||
* 未配置 `openresty_path` 时,默认为 Docker OpenResty 模式
|
||||
* `openresty_observability_port` 默认仅绑定本地回环地址;若节点本机已有端口冲突,可改为其他未占用端口
|
||||
* 为兼容旧节点,Agent 仍可读取历史字段 `cert_dir` / `openresty_cert_dir`,但保存配置时会统一写回 `support_dir` / `openresty_support_dir`
|
||||
* 配置保存时,`agent_version`、`nginx_version` 由程序运行时维护,不需要写入 JSON
|
||||
* 第五版主配置接管完成后,本机模式下应优先通过 `main_config_path` 由 Agent 写入受管主配置,而不是依赖节点手工维护 include 规则
|
||||
|
||||
@@ -308,14 +309,14 @@ go run ./cmd/agent -config ./agent.json
|
||||
| --- | --- |
|
||||
| `main_config_path` | 第五版 Docker 模式默认可落在 `data_dir/etc/nginx/nginx.conf`;本机模式建议显式配置 |
|
||||
| `route_config_path` | `data_dir/etc/nginx/conf.d/atsflare_routes.conf` |
|
||||
| `cert_dir` | `data_dir/etc/nginx/certs` |
|
||||
| `support_dir` | `data_dir/etc/nginx/support` |
|
||||
| `state_path` | `data_dir/var/lib/atsflare/agent-state.json` |
|
||||
|
||||
Docker OpenResty 模式下:
|
||||
|
||||
| 字段 | 默认值 |
|
||||
| --- | --- |
|
||||
| `openresty_cert_dir` | `/etc/nginx/atsflare-certs` |
|
||||
| `openresty_support_dir` | `/etc/nginx/atsflare-support` |
|
||||
|
||||
补充说明:
|
||||
|
||||
@@ -347,8 +348,8 @@ Docker OpenResty 模式下:
|
||||
"openresty_path": "/usr/local/openresty/nginx/sbin/openresty",
|
||||
"main_config_path": "/usr/local/openresty/nginx/conf/nginx.conf",
|
||||
"route_config_path": "/usr/local/openresty/nginx/conf/conf.d/atsflare_routes.conf",
|
||||
"cert_dir": "/usr/local/openresty/nginx/conf/certs",
|
||||
"openresty_cert_dir": "/usr/local/openresty/nginx/conf/certs"
|
||||
"support_dir": "/usr/local/openresty/nginx/conf/support",
|
||||
"openresty_support_dir": "/usr/local/openresty/nginx/conf/support"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+3
-3
@@ -263,7 +263,7 @@ export LOG_LEVEL='info'
|
||||
|
||||
验证点:
|
||||
|
||||
1. 首次启动后确认 `data/etc/nginx/nginx.conf`、`data/etc/nginx/conf.d/atsflare_routes.conf` 与 `data/etc/nginx/certs` 已由 Agent 创建
|
||||
1. 首次启动后确认 `data/etc/nginx/nginx.conf`、`data/etc/nginx/conf.d/atsflare_routes.conf` 与 `data/etc/nginx/support` 已由 Agent 创建
|
||||
2. 确认容器实际挂载了主配置、路由目录和证书目录
|
||||
3. 确认宿主机本地可访问 `http://127.0.0.1:18081/atsflare/observability` 与 `http://127.0.0.1:18081/atsflare/stub_status`
|
||||
3. 在管理端发布一次新版本后,确认节点 `current_version` 追平激活版本
|
||||
@@ -293,8 +293,8 @@ docker exec atsflare-openresty openresty -t
|
||||
"openresty_path": "/usr/local/openresty/nginx/sbin/openresty",
|
||||
"main_config_path": "/usr/local/openresty/nginx/conf/nginx.conf",
|
||||
"route_config_path": "/usr/local/openresty/nginx/conf/conf.d/atsflare_routes.conf",
|
||||
"cert_dir": "/usr/local/openresty/nginx/conf/certs",
|
||||
"openresty_cert_dir": "/usr/local/openresty/nginx/conf/certs",
|
||||
"support_dir": "/usr/local/openresty/nginx/conf/support",
|
||||
"openresty_support_dir": "/usr/local/openresty/nginx/conf/support",
|
||||
"openresty_observability_port": 18081
|
||||
}
|
||||
```
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
维护期内优先处理以下高风险或高敏感问题:
|
||||
|
||||
* Agent 写入 `support_files` 时缺少对目标路径必须位于 `cert_dir` 内的强约束,存在路径穿越风险
|
||||
* Agent 写入 `support_files` 时缺少对目标路径必须位于 `support_dir` 内的强约束,存在路径穿越风险
|
||||
* 手动上传 Server 二进制后会执行 `--version` 检测,属于高敏感执行链路,必须进一步加固
|
||||
|
||||
---
|
||||
@@ -106,7 +106,7 @@
|
||||
* 为 Agent 支持文件写入增加安全路径校验
|
||||
* 拒绝绝对路径
|
||||
* 拒绝 `..` 跳目录
|
||||
* 通过 `filepath.Rel` 或安全辅助函数确认最终路径仍位于 `cert_dir` 内
|
||||
* 通过 `filepath.Rel` 或安全辅助函数确认最终路径仍位于 `support_dir` 内
|
||||
* `writeSupportFiles`、`restore`、未来新增的写文件入口全部复用同一套安全函数
|
||||
* 收紧手动上传升级链路
|
||||
* 明确只允许 root 用户
|
||||
@@ -214,4 +214,4 @@
|
||||
|
||||
* 评估配置接口压缩与更细粒度 manifest 同步
|
||||
* 评估手动上传升级链路的更安全替代实现
|
||||
* 根据实际运行数据决定是否继续做更细的持久化优化
|
||||
* 根据实际运行数据决定是否继续做更细的持久化优化
|
||||
|
||||
Reference in New Issue
Block a user