fix: harden operations and configuration recovery
This commit is contained in:
@@ -329,6 +329,7 @@ func (a *Adapter) enable(active *profile.Profile) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("read Docker client configuration for safe JSON merge: %w", err)
|
||||
}
|
||||
observedConfig := fingerprintDocument(config, configPresent)
|
||||
metadata, managed, recoveryErr := a.readRecovery()
|
||||
if recoveryErr != nil && !errors.Is(recoveryErr, fs.ErrNotExist) {
|
||||
return fmt.Errorf("read Docker client recovery metadata: %w", recoveryErr)
|
||||
@@ -362,6 +363,9 @@ func (a *Adapter) enable(active *profile.Profile) error {
|
||||
applyDesired(defaults, desired)
|
||||
setChildObject(proxies, "default", defaults)
|
||||
setChildObject(config, "proxies", proxies)
|
||||
if err := a.requireConfigUnchanged(observedConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := a.writeConfig(config); err != nil {
|
||||
return fmt.Errorf("write Docker client proxy defaults after recovery metadata persisted: %w", err)
|
||||
}
|
||||
@@ -380,6 +384,7 @@ func (a *Adapter) disable() error {
|
||||
if err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("read Docker client configuration for restoration: %w", err)
|
||||
}
|
||||
observedConfig := fingerprintDocument(config, configPresent)
|
||||
if !configPresent {
|
||||
config = make(map[string]json.RawMessage)
|
||||
}
|
||||
@@ -422,6 +427,9 @@ func (a *Adapter) disable() error {
|
||||
} else {
|
||||
setChildObject(config, "proxies", proxies)
|
||||
}
|
||||
if err := a.requireConfigUnchanged(observedConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(config) == 0 && !metadata.ConfigPresent {
|
||||
if err := a.files.Remove(a.configuration.ConfigFile); err != nil && !errors.Is(err, fs.ErrNotExist) {
|
||||
return fmt.Errorf("remove newly created empty Docker client configuration: %w", err)
|
||||
@@ -543,23 +551,6 @@ func applyDesired(defaults map[string]json.RawMessage, desired map[string]string
|
||||
}
|
||||
}
|
||||
}
|
||||
func recognizesProxyDefaults(config map[string]json.RawMessage) bool {
|
||||
proxies, _, err := childObject(config, "proxies")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defaults, _, err := childObject(proxies, "default")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, key := range append(append([]string(nil), managedKeys...), "ftpProxy") {
|
||||
var value string
|
||||
if raw, ok := defaults[key]; ok && json.Unmarshal(raw, &value) == nil && value != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type managedValue struct {
|
||||
Present bool `json:"present"`
|
||||
@@ -612,6 +603,27 @@ func fingerprintSnapshot(snapshot map[string]managedValue) string {
|
||||
sum := sha256.Sum256(data)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func fingerprintDocument(config map[string]json.RawMessage, present bool) string {
|
||||
data, _ := json.Marshal(struct {
|
||||
Present bool `json:"present"`
|
||||
Config map[string]json.RawMessage `json:"config"`
|
||||
}{Present: present, Config: config})
|
||||
sum := sha256.Sum256(data)
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func (a *Adapter) requireConfigUnchanged(expected string) error {
|
||||
current, present, err := a.readConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("re-read Docker client configuration before atomic replacement: %w", err)
|
||||
}
|
||||
if fingerprintDocument(current, present) != expected {
|
||||
return errors.New("Docker client configuration changed concurrently; refusing to overwrite external updates")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cloneRaw(value json.RawMessage) json.RawMessage { return append(json.RawMessage(nil), value...) }
|
||||
func (a *Adapter) ownership() *Ownership {
|
||||
if !a.configuration.OwnFiles {
|
||||
|
||||
Reference in New Issue
Block a user