mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-03 15:06:36 +08:00
fix(agent): unify agent and openresty runtime user as openflare
Introduce the shared openflare service account for the agent process and OpenResty workers, normalize data_dir ownership on startup, and ensure managed paths are chowned with 0755/0644 during sync and apply. Docker entrypoint fixes volume ownership before dropping privileges; local systemd install runs the service as openflare with CAP_NET_BIND_SERVICE.
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
const (
|
||||
pagesMaxExtractedFileBytes = 100 * 1024 * 1024
|
||||
pagesDirPerm = 0o755
|
||||
pagesFilePerm = 0o644
|
||||
pagesManifestFilePerm = 0o644
|
||||
)
|
||||
|
||||
@@ -137,6 +138,11 @@ func (s *Service) syncPagesDeployments(ctx context.Context, snapshot *state.Snap
|
||||
return err
|
||||
}
|
||||
}
|
||||
if s.nginxManager != nil {
|
||||
if err := s.nginxManager.EnsureWorkerReadAccess(); err != nil {
|
||||
return fmt.Errorf("ensure openresty worker read access: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -333,7 +339,7 @@ func extractPagesFile(item *zip.File, targetPath string) error {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = source.Close() }()
|
||||
target, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, item.FileInfo().Mode().Perm()) //nolint:gosec // targetPath is under managed PagesDir from validated zip entry
|
||||
target, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, pagesFilePerm) //nolint:gosec // targetPath is under managed PagesDir from validated zip entry
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -416,10 +422,6 @@ func copyPagesDir(sourceDir string, targetDir string) error {
|
||||
if entry.IsDir() {
|
||||
return os.MkdirAll(targetPath, pagesDirPerm)
|
||||
}
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
input, err := os.Open(sourcePath) //nolint:gosec // sourcePath is under managed PagesDir walk root
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -428,7 +430,7 @@ func copyPagesDir(sourceDir string, targetDir string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(targetPath), pagesDirPerm); err != nil {
|
||||
return err
|
||||
}
|
||||
output, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode().Perm()) //nolint:gosec // targetPath is under managed PagesDir walk root
|
||||
output, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, pagesFilePerm) //nolint:gosec // targetPath is under managed PagesDir walk root
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ type NginxManager interface {
|
||||
CurrentChecksum() (string, error)
|
||||
WAFIPGroupChecksums() (map[string]string, error)
|
||||
SyncWAFIPGroups(groups []protocol.WAFIPGroup) error
|
||||
EnsureWorkerReadAccess() error
|
||||
}
|
||||
|
||||
// Service orchestrates configuration synchronisation between the server and the local OpenResty instance.
|
||||
|
||||
@@ -141,6 +141,10 @@ func (m *fakeManager) SyncWAFIPGroups(groups []protocol.WAFIPGroup) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *fakeManager) EnsureWorkerReadAccess() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSyncOnceSuccess(t *testing.T) {
|
||||
client := &fakeClient{
|
||||
config: protocol.ActiveConfigResponse{
|
||||
@@ -254,6 +258,54 @@ func TestSyncOnceDownloadsPagesDeploymentBeforeApply(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncPagesDeploymentEnsuresWorkerReadAccess(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
dataDir := filepath.Join(tempDir, "data")
|
||||
if err := os.MkdirAll(dataDir, 0o700); err != nil {
|
||||
t.Fatalf("MkdirAll failed: %v", err)
|
||||
}
|
||||
pagesDir := filepath.Join(dataDir, "var", "lib", "openflare", "pages")
|
||||
|
||||
packageBytes := testPagesPackage(t, map[string]string{"index.html": "hello"})
|
||||
checksum := testBytesChecksum(packageBytes)
|
||||
config := protocol.ActiveConfigResponse{
|
||||
Version: "20260309-106",
|
||||
Checksum: "pages-config-checksum",
|
||||
SourceConfigJSON: testPagesSourceConfigJSON(7, checksum),
|
||||
CreatedAt: time.Now().Format(time.RFC3339),
|
||||
}
|
||||
client := &fakeClient{
|
||||
config: config,
|
||||
pagesPackages: map[uint][]byte{7: packageBytes},
|
||||
}
|
||||
stateStore := state.NewStore(filepath.Join(tempDir, "state.json"))
|
||||
snapshot, _ := stateStore.Load()
|
||||
|
||||
runtimeManager := &nginx.Manager{PagesDir: pagesDir}
|
||||
service := New(client, runtimeManager, stateStore)
|
||||
service.SetPagesDir(pagesDir)
|
||||
|
||||
if err := service.syncPagesDeployments(context.Background(), snapshot, &config); err != nil {
|
||||
t.Fatalf("syncPagesDeployments failed: %v", err)
|
||||
}
|
||||
|
||||
dataInfo, err := os.Stat(dataDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Stat dataDir failed: %v", err)
|
||||
}
|
||||
if dataInfo.Mode().Perm()&0o005 == 0 {
|
||||
t.Fatalf("expected dataDir to be world-traversable, got %o", dataInfo.Mode().Perm())
|
||||
}
|
||||
indexPath := filepath.Join(pagesDir, "deployments", "7", "current", "index.html")
|
||||
indexInfo, err := os.Stat(indexPath)
|
||||
if err != nil {
|
||||
t.Fatalf("expected Pages file to be extracted: %v", err)
|
||||
}
|
||||
if indexInfo.Mode().Perm() != 0o644 {
|
||||
t.Fatalf("expected index.html mode 0644, got %o", indexInfo.Mode().Perm())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncOnceExtractsPagesPackageWithZeroByteFiles(t *testing.T) {
|
||||
packageBytes := testPagesPackage(t, map[string]string{
|
||||
"index.html": "hello",
|
||||
|
||||
Reference in New Issue
Block a user