package dockerdaemon_test import ( "context" "encoding/json" "errors" "fmt" "os" "path/filepath" "reflect" "strings" "testing" "proxyctl/internal/app" "proxyctl/internal/dockerdaemon" "proxyctl/internal/profile" "proxyctl/internal/target" "proxyctl/internal/target/targettest" ) type recorder struct{ events []string } type fakeRunner struct { recorder *recorder show string active string fail map[string]error } func (r *fakeRunner) Run(_ context.Context, name string, args ...string) ([]byte, error) { command := strings.Join(append([]string{name}, args...), " ") r.recorder.events = append(r.recorder.events, command) if err := r.fail[command]; err != nil { return nil, err } if strings.Contains(command, " show ") { return []byte(r.show), nil } if strings.Contains(command, " is-active ") { return []byte(r.active), nil } return nil, nil } type recordingFS struct{ recorder *recorder } func (f recordingFS) ReadFile(path string) ([]byte, error) { return os.ReadFile(path) } func (f recordingFS) FileMode(path string) (os.FileMode, error) { info, err := os.Stat(path) if err != nil { return 0, err } return info.Mode().Perm(), nil } func (f recordingFS) AtomicWrite(path string, data []byte, mode os.FileMode) error { f.recorder.events = append(f.recorder.events, "write "+filepath.Base(path)) if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { return err } temporary := path + ".tmp" if err := os.WriteFile(temporary, data, mode); err != nil { return err } return os.Rename(temporary, path) } func (f recordingFS) Remove(path string) error { f.recorder.events = append(f.recorder.events, "remove "+filepath.Base(path)) return os.Remove(path) } func fixture(t *testing.T) (*dockerdaemon.Adapter, *recorder, string, string, *fakeRunner) { t.Helper() directory := t.TempDir() daemonPath := filepath.Join(directory, "etc", "docker", "daemon.json") recoveryPath := filepath.Join(directory, "state", "docker-daemon.json") record := &recorder{} runner := &fakeRunner{recorder: record, show: "LoadState=loaded\nFragmentPath=/lib/systemd/system/docker.service\n", active: "active\n", fail: map[string]error{}} adapter := dockerdaemon.New(dockerdaemon.Configuration{DaemonConfigPath: daemonPath, RecoveryPath: recoveryPath, TrustedRoot: directory}, recordingFS{recorder: record}, runner) return adapter, record, daemonPath, recoveryPath, runner } func activeProfile() *profile.Profile { return &profile.Profile{Name: "office", Mode: profile.AdvancedMode, HTTPURL: "http://alice:secret@proxy.example:8080", HTTPSURL: "https://proxy.example:8443", Bypass: []string{"localhost", ".example.com", "10.0.0.0/8", "2001:db8::1"}} } type faultFS struct { recordingFS permission bool failDaemon bool failRecoveryOnce bool } func (f *faultFS) AtomicWrite(path string, data []byte, mode os.FileMode) error { if f.permission { return os.ErrPermission } if f.failDaemon && filepath.Base(path) == "daemon.json" { return errors.New("write failed") } if f.failRecoveryOnce && filepath.Base(path) == "docker-daemon.json" { f.failRecoveryOnce = false return errors.New("secret recovery failure") } return f.recordingFS.AtomicWrite(path, data, mode) } type fixedAuthority int func (a fixedAuthority) EffectiveUID() int { return int(a) } type immediateLock struct{} func (immediateLock) Lock(context.Context) (func(), error) { return func() {}, nil } func TestUnsafeRecoveryMetadataIsDriftedAndResetNeverMutatesDaemon(t *testing.T) { fingerprint := strings.Repeat("a", 64) cases := map[string]string{ "empty object": `{}`, "truncated": `{"version":1`, "unsupported version": `{"version":2,"originalExists":false,"configFingerprint":"` + fingerprint + `"}`, "missing original": `{"version":1,"originalExists":true,"originalMode":384,"configFingerprint":"` + fingerprint + `"}`, "unexpected original": `{"version":1,"originalExists":false,"original":"e30=","configFingerprint":"` + fingerprint + `"}`, "mode without original": `{"version":1,"originalExists":false,"originalMode":384,"configFingerprint":"` + fingerprint + `"}`, "invalid mode": `{"version":1,"originalExists":true,"original":"e30=","originalMode":4096,"configFingerprint":"` + fingerprint + `"}`, "invalid fingerprint": `{"version":1,"originalExists":false,"configFingerprint":"short"}`, "contradictory restart record": `{"version":1,"originalExists":false,"configFingerprint":"` + fingerprint + `","restartedFingerprint":"` + strings.Repeat("b", 64) + `"}`, } for name, metadata := range cases { t.Run(name, func(t *testing.T) { a, record, daemonPath, recoveryPath, _ := fixture(t) originalConfig := []byte(`{"log-level":"debug"}`) if err := os.MkdirAll(filepath.Dir(daemonPath), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(daemonPath, originalConfig, 0o640); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Dir(recoveryPath), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(recoveryPath, []byte(metadata), 0o600); err != nil { t.Fatal(err) } record.events = nil if inspection := a.Inspect(context.Background(), nil); inspection.State != target.Drifted { t.Fatalf("inspection = %#v", inspection) } reset := app.NewMaintenance(profile.State{}, []target.Target{a}, immediateLock{}, nil).Reset(context.Background(), true) if reset.Err == nil || len(reset.Targets) != 1 || reset.Targets[0].Err == nil { t.Fatalf("unsafe reset = %#v", reset) } if events := mutationEvents(record.events); len(events) != 0 { t.Fatalf("unsafe metadata caused mutation: %#v", events) } if got, err := os.ReadFile(daemonPath); err != nil || !reflect.DeepEqual(got, originalConfig) { t.Fatalf("daemon changed to %q: %v", got, err) } if got, err := os.ReadFile(recoveryPath); err != nil || string(got) != metadata { t.Fatalf("recovery changed to %q: %v", got, err) } }) } } func TestDiagnosticChecksCoverSystemdSchemaAndAuthorityWithoutMutation(t *testing.T) { a, record, daemonPath, recoveryPath, runner := fixture(t) a = dockerdaemon.New(dockerdaemon.Configuration{DaemonConfigPath: daemonPath, RecoveryPath: recoveryPath, TrustedRoot: filepath.Dir(filepath.Dir(daemonPath))}, recordingFS{recorder: record}, runner, fixedAuthority(0)) checks := a.DiagnosticChecks(context.Background(), activeProfile()) for _, name := range []string{"required command and service", "configuration schema", "write authority", "service control authority"} { found := false for _, check := range checks { found = found || check.Name == name } if !found { t.Errorf("diagnostics missing %q: %#v", name, checks) } } for _, event := range record.events { if strings.HasPrefix(event, "write ") || strings.HasPrefix(event, "remove ") || strings.Contains(event, " restart ") || strings.Contains(event, "daemon-reload") { t.Fatalf("diagnostics mutated daemon state: %#v", record.events) } } for _, path := range []string{daemonPath, recoveryPath} { if _, err := os.Stat(path); !errors.Is(err, os.ErrNotExist) { t.Fatalf("diagnostics created %s: %v", path, err) } } } func TestDiagnosticServiceControlAuthorityUsesInjectedEUIDWithoutMutation(t *testing.T) { for _, test := range []struct { name string uid int ok bool }{ {name: "unprivileged", uid: 1000, ok: false}, {name: "root", uid: 0, ok: true}, } { t.Run(test.name, func(t *testing.T) { _, record, daemonPath, recoveryPath, runner := fixture(t) a := dockerdaemon.New(dockerdaemon.Configuration{DaemonConfigPath: daemonPath, RecoveryPath: recoveryPath}, recordingFS{recorder: record}, runner, fixedAuthority(test.uid)) checks := a.DiagnosticChecks(context.Background(), activeProfile()) var found *target.Check for i := range checks { if checks[i].Name == "service control authority" { found = &checks[i] } } if found == nil || found.OK != test.ok { t.Fatalf("service authority check = %#v", found) } for _, event := range record.events { if strings.Contains(event, "restart") || strings.Contains(event, "daemon-reload") { t.Fatalf("authority diagnosis mutated service: %#v", record.events) } } }) } } func TestDetectClassifiesMalformedDaemonConfigurationAsFailure(t *testing.T) { a, _, daemonPath, _, _ := fixture(t) if err := os.MkdirAll(filepath.Dir(daemonPath), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(daemonPath, []byte(`{"proxies":[]}`), 0o600); err != nil { t.Fatal(err) } detection := a.Detect(context.Background(), activeProfile()) if detection.Err == nil || detection.Inspection.State != target.Disabled { t.Fatalf("detection = %#v, want failed Disabled inspection", detection) } if got := a.Inspect(context.Background(), activeProfile()); got.State != target.Disabled { t.Fatalf("maintenance inspection = %#v, want established Disabled semantics", got) } } func TestDiagnosticsAndEnableRejectMalformedDaemonProxyTypesWithoutMutation(t *testing.T) { for _, document := range []string{ `[]`, `{"proxies":[]}`, `{"proxies":{"http-proxy":42}}`, `{"proxies":{"https-proxy":false}}`, `{"proxies":{"no-proxy":[]}}`, } { t.Run(document, func(t *testing.T) { a, record, daemonPath, _, _ := fixture(t) if err := os.MkdirAll(filepath.Dir(daemonPath), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(daemonPath, []byte(document), 0o600); err != nil { t.Fatal(err) } checks := a.DiagnosticChecks(context.Background(), activeProfile()) if checks[1].OK { t.Fatalf("malformed schema passed diagnostics: %#v", checks) } record.events = nil result := a.SetDesired(context.Background(), true, activeProfile()) if result.Err == nil { t.Fatalf("enable accepted malformed schema: %#v", result) } for _, event := range record.events { if strings.HasPrefix(event, "write ") || strings.Contains(event, "restart") || strings.Contains(event, "daemon-reload") { t.Fatalf("malformed schema caused mutation: %#v", record.events) } } got, err := os.ReadFile(daemonPath) if err != nil || string(got) != document { t.Fatalf("daemon config changed: %q, %v", got, err) } }) } } func TestDockerDaemonRunsReusableTargetContract(t *testing.T) { targettest.Run(t, func(t *testing.T) targettest.Fixture { a, _, daemonPath, _, _ := fixture(t) return targettest.Fixture{ Target: a, Active: activeProfile(), Secret: "secret", MakeExternal: func(t *testing.T) { if err := os.MkdirAll(filepath.Dir(daemonPath), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(daemonPath, []byte(`{"proxies":{"http-proxy":"http://external.example:8080"}}`), 0o600); err != nil { t.Fatal(err) } }, MakeDrifted: func(t *testing.T) { if err := os.WriteFile(daemonPath, []byte(`{"proxies":{"http-proxy":"http://changed.example:8080"}}`), 0o600); err != nil { t.Fatal(err) } }, PermissionFailure: func(t *testing.T) target.OperationResult { failed, record, _, _, failedRunner := fixture(t) files := &faultFS{recordingFS: recordingFS{recorder: record}, permission: true} failed = dockerdaemon.New(dockerdaemon.Configuration{DaemonConfigPath: failed.DaemonConfigPath(), RecoveryPath: failed.RecoveryPath(), TrustedRoot: filepath.Dir(filepath.Dir(failed.DaemonConfigPath()))}, files, failedRunner) return failed.SetDesired(context.Background(), true, activeProfile()) }, PartialFailure: func(t *testing.T) target.OperationResult { failed, record, _, _, failedRunner := fixture(t) files := &faultFS{recordingFS: recordingFS{recorder: record}, failDaemon: true} failed = dockerdaemon.New(dockerdaemon.Configuration{DaemonConfigPath: failed.DaemonConfigPath(), RecoveryPath: failed.RecoveryPath()}, files, failedRunner) return failed.SetDesired(context.Background(), true, activeProfile()) }, RedactionFailure: func(t *testing.T) target.OperationResult { failed, _, _, _, failedRunner := fixture(t) failedRunner.fail["systemctl daemon-reload"] = errors.New("http://alice:secret@proxy.example:8080") return failed.SetDesired(context.Background(), true, activeProfile()) }, RecoveryRetry: func(t *testing.T) (target.OperationResult, target.OperationResult, target.OperationResult) { failed, record, daemon, recovery, failedRunner := fixture(t) if err := os.MkdirAll(filepath.Dir(daemon), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(daemon, []byte(`{"proxies":{"http-proxy":"http://external.example:8080"}}`), 0o600); err != nil { t.Fatal(err) } files := &faultFS{recordingFS: recordingFS{recorder: record}, failRecoveryOnce: true} failed = dockerdaemon.New(dockerdaemon.Configuration{DaemonConfigPath: daemon, RecoveryPath: recovery}, files, failedRunner) first := failed.SetDesired(context.Background(), true, activeProfile()) retry := failed.SetDesired(context.Background(), true, activeProfile()) disabled := failed.SetDesired(context.Background(), false, nil) return first, retry, disabled }, } }) } func TestDockerDaemonAvailabilityDescriptionAndConfirmation(t *testing.T) { a, _, _, _, runner := fixture(t) if got := a.Availability(context.Background()); !got.Available || !strings.Contains(got.Evidence, "systemd") { t.Fatalf("availability = %#v", got) } description := a.Description() for _, phrase := range []string{"registry", "not container network traffic", "restart", "permission"} { if !strings.Contains(strings.ToLower(description.Summary+" "+description.Details), phrase) { t.Errorf("description misses %q: %#v", phrase, description) } } if warning := a.Confirmation(true); !strings.Contains(strings.ToLower(warning), "restart") || !strings.Contains(strings.ToLower(warning), "running") { t.Fatalf("confirmation = %q", warning) } runner.show = "LoadState=not-found\nFragmentPath=\n" if got := a.Availability(context.Background()); got.Available || !strings.Contains(got.Evidence, "not detected") { t.Fatalf("unsupported availability = %#v", got) } } func TestDockerDaemonEnableOrdersManagedWriteReloadRestartAndPostInspect(t *testing.T) { a, record, daemonPath, _, _ := fixture(t) result := a.SetDesired(context.Background(), true, activeProfile()) if result.Err != nil || result.Inspection.State != target.Enabled { t.Fatalf("enable = %#v", result) } wantOrder := []string{"write docker-daemon.json", "write daemon.json", "systemctl daemon-reload", "systemctl restart docker.service", "systemctl is-active docker.service", "write docker-daemon.json"} assertSubsequence(t, record.events, wantOrder) var config map[string]any data, err := os.ReadFile(daemonPath) if err != nil || json.Unmarshal(data, &config) != nil { t.Fatalf("daemon config: %v %s", err, data) } proxies := config["proxies"].(map[string]any) if proxies["http-proxy"] != activeProfile().HTTPURL || proxies["https-proxy"] != activeProfile().HTTPSURL || !strings.Contains(proxies["no-proxy"].(string), "10.0.0.0/8") { t.Fatalf("managed proxies = %#v", proxies) } record.events = nil second := a.SetDesired(context.Background(), true, activeProfile()) if second.Err != nil || second.Inspection.State != target.Enabled || len(mutationEvents(record.events)) != 0 { t.Fatalf("idempotent enable = %#v, events=%v", second, record.events) } } func TestDockerDaemonDisableAndResetRejectDriftWithoutMutatingDaemonOrRecovery(t *testing.T) { for _, test := range []struct { name string mutate func(t *testing.T, managed []byte) []byte }{ { name: "unrelated administrator edit", mutate: func(t *testing.T, managed []byte) []byte { t.Helper() var document map[string]any if err := json.Unmarshal(managed, &document); err != nil { t.Fatal(err) } document["log-level"] = "debug" changed, err := json.MarshalIndent(document, "", " ") if err != nil { t.Fatal(err) } return append(changed, '\n') }, }, { name: "managed proxy edit", mutate: func(t *testing.T, managed []byte) []byte { t.Helper() var document map[string]any if err := json.Unmarshal(managed, &document); err != nil { t.Fatal(err) } document["proxies"].(map[string]any)["http-proxy"] = "http://administrator.example:3128" changed, err := json.MarshalIndent(document, "", " ") if err != nil { t.Fatal(err) } return append(changed, '\n') }, }, } { t.Run(test.name, func(t *testing.T) { a, record, daemonPath, recoveryPath, _ := fixture(t) if result := a.SetDesired(context.Background(), true, activeProfile()); result.Err != nil { t.Fatal(result.Err) } managed, err := os.ReadFile(daemonPath) if err != nil { t.Fatal(err) } changed := test.mutate(t, managed) if err := os.WriteFile(daemonPath, changed, 0o600); err != nil { t.Fatal(err) } recoveryBefore, err := os.ReadFile(recoveryPath) if err != nil { t.Fatal(err) } record.events = nil reset := app.NewMaintenance(profile.State{Profiles: []profile.Profile{*activeProfile()}, ActiveProfile: activeProfile().Name}, []target.Target{a}, immediateLock{}, nil).Reset(context.Background(), true) if reset.Err == nil || len(reset.Targets) != 1 || reset.Targets[0].Err == nil || reset.Targets[0].Inspection.State != target.Drifted { t.Fatalf("conflicting reset = %#v", reset) } if events := mutationEvents(record.events); len(events) != 0 { t.Fatalf("conflicting reset mutated daemon state: %#v", events) } if got, err := os.ReadFile(daemonPath); err != nil || !reflect.DeepEqual(got, changed) { t.Fatalf("current daemon configuration changed to %q: %v", got, err) } if got, err := os.ReadFile(recoveryPath); err != nil || !reflect.DeepEqual(got, recoveryBefore) { t.Fatalf("recovery metadata changed to %q: %v", got, err) } }) } } func TestDockerDaemonResetAlreadyRestoredReloadsRestartsAndInspectsBeforeMetadataCleanup(t *testing.T) { a, record, daemonPath, recoveryPath, _ := fixture(t) original := []byte("{\n \"log-level\": \"debug\",\n \"proxies\": {\"http-proxy\": \"http://prior.example:3128\"}\n}\n") prepareAlreadyRestoredDaemon(t, a, daemonPath, original) record.events = nil reset := app.NewMaintenance(profile.State{Profiles: []profile.Profile{*activeProfile()}, ActiveProfile: activeProfile().Name}, []target.Target{a}, immediateLock{}, nil).Reset(context.Background(), true) if reset.Err != nil || len(reset.Targets) != 1 || reset.Targets[0].Err != nil || reset.Targets[0].Inspection.State != target.External { t.Fatalf("already-restored reset = %#v", reset) } assertSubsequence(t, record.events, []string{"systemctl daemon-reload", "systemctl restart docker.service", "systemctl is-active docker.service", "remove docker-daemon.json"}) if contains(record.events, "write daemon.json") { t.Fatalf("already-restored reset rewrote daemon config: %#v", record.events) } if got, err := os.ReadFile(daemonPath); err != nil || !reflect.DeepEqual(got, original) { t.Fatalf("already-restored daemon configuration changed to %q: %v", got, err) } if _, err := os.Stat(recoveryPath); !errors.Is(err, os.ErrNotExist) { t.Fatalf("recovery metadata remains: %v", err) } } func TestDockerDaemonAlreadyRestoredPreservesMetadataAndReportsFreshDriftOnEveryServiceFailure(t *testing.T) { failures := []struct { name string command string active string }{ {name: "daemon reload command", command: "systemctl daemon-reload"}, {name: "restart command", command: "systemctl restart docker.service"}, {name: "active inspection command", command: "systemctl is-active docker.service"}, {name: "inactive service state", active: "failed\n"}, } for _, failure := range failures { t.Run(failure.name, func(t *testing.T) { a, record, daemonPath, recoveryPath, runner := fixture(t) original := []byte("{\n \"log-level\": \"debug\"\n}\n") prepareAlreadyRestoredDaemon(t, a, daemonPath, original) metadataBefore, err := os.ReadFile(recoveryPath) if err != nil { t.Fatal(err) } if failure.command != "" { runner.fail[failure.command] = errors.New("service failure") } if failure.active != "" { runner.active = failure.active } record.events = nil reset := app.NewMaintenance(profile.State{Profiles: []profile.Profile{*activeProfile()}, ActiveProfile: activeProfile().Name}, []target.Target{a}, immediateLock{}, nil).Reset(context.Background(), true) if reset.Err == nil || len(reset.Targets) != 1 || reset.Targets[0].Inspection.State != target.Drifted { t.Fatalf("failed already-restored reset = %#v", reset) } if contains(record.events, "write daemon.json") || contains(record.events, "remove docker-daemon.json") { t.Fatalf("service failure mutated configuration or metadata: %#v", record.events) } if got, err := os.ReadFile(daemonPath); err != nil || !reflect.DeepEqual(got, original) { t.Fatalf("daemon config changed to %q: %v", got, err) } if got, err := os.ReadFile(recoveryPath); err != nil || !reflect.DeepEqual(got, metadataBefore) { t.Fatalf("metadata changed to %q: %v", got, err) } }) } } func prepareAlreadyRestoredDaemon(t *testing.T, a *dockerdaemon.Adapter, daemonPath string, original []byte) { t.Helper() if err := os.MkdirAll(filepath.Dir(daemonPath), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(daemonPath, original, 0o640); err != nil { t.Fatal(err) } if result := a.SetDesired(context.Background(), true, activeProfile()); result.Err != nil { t.Fatal(result.Err) } if err := os.WriteFile(daemonPath, original, 0o640); err != nil { t.Fatal(err) } } func TestDockerDaemonPreservesAndRestoresExistingConfiguration(t *testing.T) { a, _, daemonPath, recoveryPath, _ := fixture(t) original := []byte("{\n \"log-level\": \"debug\",\n \"proxies\": {\"http-proxy\": \"http://prior.example:3128\"}\n}\n") if err := os.MkdirAll(filepath.Dir(daemonPath), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(daemonPath, original, 0o640); err != nil { t.Fatal(err) } if got := a.Inspect(context.Background(), activeProfile()).State; got != target.External { t.Fatalf("initial = %s", got) } if got := a.SetDesired(context.Background(), true, activeProfile()); got.Err != nil { t.Fatal(got.Err) } if got := a.SetDesired(context.Background(), false, nil); got.Err != nil || got.Inspection.State != target.External { t.Fatalf("disable = %#v", got) } restored, _ := os.ReadFile(daemonPath) if !reflect.DeepEqual(restored, original) { t.Fatalf("restored:\n%s\nwant:\n%s", restored, original) } if info, err := os.Stat(daemonPath); err != nil || info.Mode().Perm() != 0o640 { t.Fatalf("restored mode = %v, %v", info, err) } if _, err := os.Stat(recoveryPath); !errors.Is(err, os.ErrNotExist) { t.Fatalf("recovery remains: %v", err) } } func TestDockerDaemonRestartFailureIsRedactedTruthfulAndRetryable(t *testing.T) { a, record, _, _, runner := fixture(t) restart := "systemctl restart docker.service" runner.fail[restart] = fmt.Errorf("restart exposed http://alice:secret@proxy.example:8080") first := a.SetDesired(context.Background(), true, activeProfile()) if first.Err == nil || strings.Contains(first.Err.Error(), "secret") || first.Inspection.State != target.Enabled { t.Fatalf("restart failure = %#v", first) } delete(runner.fail, restart) record.events = nil retry := a.SetDesired(context.Background(), true, activeProfile()) if retry.Err != nil || retry.Inspection.State != target.Enabled { t.Fatalf("retry = %#v", retry) } if contains(record.events, "write daemon.json") || !contains(record.events, restart) { t.Fatalf("retry events = %v", record.events) } } func TestDockerDaemonAcceptsCompleteHTTPProfileWithAdditionalSOCKSProxy(t *testing.T) { a, record, daemonPath, _, _ := fixture(t) p := &profile.Profile{ Name: "dual", Mode: profile.AdvancedMode, HTTPURL: "http://localhost:7890", HTTPSURL: "http://localhost:7890", AllURL: "socks5://localhost:7890", } result := a.SetDesired(context.Background(), true, p) if result.Err != nil || result.Inspection.State != target.Enabled { t.Fatalf("dual-protocol result = %#v events=%v", result, record.events) } data, err := os.ReadFile(daemonPath) if err != nil { t.Fatal(err) } if !strings.Contains(string(data), `"http-proxy": "http://localhost:7890"`) || !strings.Contains(string(data), `"https-proxy": "http://localhost:7890"`) { t.Fatalf("daemon config = %s", data) } } func TestDockerDaemonRejectsIncompatibleProfileBeforeMutation(t *testing.T) { for _, p := range []*profile.Profile{ {Name: "socks", Mode: profile.SingleMode, SingleURL: "socks5://proxy.example:1080"}, {Name: "all", Mode: profile.AdvancedMode, HTTPURL: "http://proxy.example:8080", AllURL: "http://proxy.example:8080"}, {Name: "bypass", Mode: profile.AdvancedMode, HTTPURL: "http://proxy.example:8080", Bypass: []string{"*"}}, } { t.Run(p.Name, func(t *testing.T) { a, record, _, _, _ := fixture(t) result := a.SetDesired(context.Background(), true, p) if result.Err == nil || len(mutationEvents(record.events)) != 0 || result.Inspection.State != target.Disabled { t.Fatalf("incompatible result = %#v events=%v", result, record.events) } }) } } func TestDockerDaemonNeverEscalatesAndReportsPermissionNormally(t *testing.T) { a, record, _, _, runner := fixture(t) runner.fail["systemctl daemon-reload"] = os.ErrPermission got := a.SetDesired(context.Background(), true, activeProfile()) if got.Err == nil || !errors.Is(got.Err, os.ErrPermission) || got.Inspection.State != target.Enabled { t.Fatalf("permission = %#v", got) } for _, event := range record.events { if strings.Contains(event, "sudo") || strings.Contains(event, "pkexec") { t.Fatalf("privilege escalation: %v", record.events) } } } func TestDockerDaemonInspectionHasAllFourStates(t *testing.T) { a, _, daemonPath, _, _ := fixture(t) ctx := context.Background() if got := a.Inspect(ctx, activeProfile()).State; got != target.Disabled { t.Fatalf("disabled = %s", got) } if err := os.MkdirAll(filepath.Dir(daemonPath), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(daemonPath, []byte(`{"proxies":{"http-proxy":"http://external.example:8080"}}`), 0o600); err != nil { t.Fatal(err) } if got := a.Inspect(ctx, activeProfile()).State; got != target.External { t.Fatalf("external = %s", got) } if got := a.SetDesired(ctx, true, activeProfile()); got.Err != nil { t.Fatal(got.Err) } if got := a.Inspect(ctx, activeProfile()).State; got != target.Enabled { t.Fatalf("enabled = %s", got) } if err := os.WriteFile(daemonPath, []byte(`{"proxies":{"http-proxy":"http://changed.example:8080"}}`), 0o600); err != nil { t.Fatal(err) } if got := a.Inspect(ctx, activeProfile()).State; got != target.Drifted { t.Fatalf("drifted = %s", got) } } func assertSubsequence(t *testing.T, got, want []string) { t.Helper() index := 0 for _, event := range got { if index < len(want) && event == want[index] { index++ } } if index != len(want) { t.Fatalf("events %v do not contain ordered %v", got, want) } } func mutationEvents(events []string) []string { var result []string for _, event := range events { if strings.HasPrefix(event, "write ") || strings.HasPrefix(event, "remove ") || strings.Contains(event, "daemon-reload") || strings.Contains(event, " restart ") { result = append(result, event) } } return result } func contains(values []string, want string) bool { for _, value := range values { if value == want { return true } } return false }