claude-code: write the agent's home as its own account where it has one
On the control node agents are to run as an account that cannot become root (hq ADR 0266). The runtime keeps running as the operator, so the token, the identity and the home-scope items go to the agent's home through a helper started as that account; the licence and the account file follow the move, what was placed in the operator's home goes once the agent holds the licence, and the guard refuses every tool of a session running as anyone else, recording to the journal. An API key is taken only from the agent's home and only when it is one, here and in the licence manager, so the tool cannot carry off the runtime's credential. Unchanged where no agent account is named.
This commit is contained in:
@@ -98,7 +98,7 @@ func readItem(dir, rel string, folder bool) (map[string]string, map[string]fs.Fi
|
||||
return nil, nil, errors.New(why)
|
||||
}
|
||||
full := filepath.Join(dir, filepath.FromSlash(rel))
|
||||
info, err := os.Lstat(full)
|
||||
info, err := fsys.Lstat(full)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil, fmt.Errorf("%s is not in this home", full)
|
||||
@@ -113,7 +113,7 @@ func readItem(dir, rel string, folder bool) (map[string]string, map[string]fs.Fi
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil, nil, fmt.Errorf("%s is not a file", full)
|
||||
}
|
||||
raw, err := os.ReadFile(full)
|
||||
raw, err := fsys.ReadFile(full)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func readItem(dir, rel string, folder bool) (map[string]string, map[string]fs.Fi
|
||||
if !info.IsDir() {
|
||||
return nil, nil, fmt.Errorf("%s is not a folder", full)
|
||||
}
|
||||
err = filepath.WalkDir(full, func(at string, d fs.DirEntry, err error) error {
|
||||
err = fsys.WalkDir(full, func(at string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -137,7 +137,7 @@ func readItem(dir, rel string, folder bool) (map[string]string, map[string]fs.Fi
|
||||
return fmt.Errorf("%s is not a file", at)
|
||||
}
|
||||
in, _ := filepath.Rel(full, at)
|
||||
raw, err := os.ReadFile(at)
|
||||
raw, err := fsys.ReadFile(at)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -232,7 +232,7 @@ func RemoveHome(p Paths, kind, name, why string, now time.Time) (map[string]any,
|
||||
day := filepath.Join(p.State, RemovedDir, now.Format("2006-01-02"))
|
||||
kept := filepath.Join(day, now.Format("150405")+"-"+kind+"-"+name)
|
||||
for n := 2; ; n++ {
|
||||
if _, err := os.Lstat(kept); os.IsNotExist(err) {
|
||||
if _, err := fsys.Lstat(kept); os.IsNotExist(err) {
|
||||
break
|
||||
}
|
||||
kept = filepath.Join(day, fmt.Sprintf("%s-%s-%s-%d", now.Format("150405"), kind, name, n))
|
||||
@@ -244,13 +244,13 @@ func RemoveHome(p Paths, kind, name, why string, now time.Time) (map[string]any,
|
||||
names := map[string]KeptFile{}
|
||||
for in, content := range files {
|
||||
to := filepath.Join(copyRoot, filepath.FromSlash(in))
|
||||
if err := os.MkdirAll(filepath.Dir(to), 0o700); err != nil {
|
||||
if err := fsys.MkdirAll(filepath.Dir(to), 0o700); err != nil {
|
||||
return nil, fmt.Errorf("the copy could not be made, nothing removed: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(to, []byte(content), modes[in]|0o600); err != nil {
|
||||
if err := fsys.WriteFile(to, []byte(content), modes[in]|0o600); err != nil {
|
||||
return nil, fmt.Errorf("the copy could not be made, nothing removed: %w", err)
|
||||
}
|
||||
if back, err := os.ReadFile(to); err != nil || !bytes.Equal(back, []byte(content)) {
|
||||
if back, err := fsys.ReadFile(to); err != nil || !bytes.Equal(back, []byte(content)) {
|
||||
return nil, fmt.Errorf("the copy of %s does not read back the same, nothing removed", in)
|
||||
}
|
||||
names[in] = KeptFile{Digest: digest(content), Mode: fmt.Sprintf("%04o", modes[in])}
|
||||
@@ -258,7 +258,7 @@ func RemoveHome(p Paths, kind, name, why string, now time.Time) (map[string]any,
|
||||
r := Removal{Action: "removed", At: now.Format(time.RFC3339), Node: p.Node, Kind: kind, Name: name, Path: full, Why: why,
|
||||
Kept: filepath.Join(kept, filepath.FromSlash(rel)), Files: names}
|
||||
note, _ := indented(r)
|
||||
if err := os.WriteFile(filepath.Join(kept, "removal.json"), note, 0o600); err != nil {
|
||||
if err := fsys.WriteFile(filepath.Join(kept, "removal.json"), note, 0o600); err != nil {
|
||||
return nil, fmt.Errorf("the copy's note could not be written, nothing removed: %w", err)
|
||||
}
|
||||
|
||||
@@ -273,9 +273,9 @@ func RemoveHome(p Paths, kind, name, why string, now time.Time) (map[string]any,
|
||||
}
|
||||
}
|
||||
if folder {
|
||||
err = os.RemoveAll(full)
|
||||
err = fsys.RemoveAll(full)
|
||||
} else {
|
||||
err = os.Remove(full)
|
||||
err = fsys.Remove(full)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s could not be removed (the copy is at %s): %w", full, kept, err)
|
||||
@@ -392,7 +392,7 @@ func RestoreHome(p Paths, kept string, now time.Time) (map[string]any, error) {
|
||||
if why := linkedParent(dir, rel+"/x"); why != "" {
|
||||
return nil, errors.New(why + ", nothing restored")
|
||||
}
|
||||
if _, err := os.Lstat(full); err == nil {
|
||||
if _, err := fsys.Lstat(full); err == nil {
|
||||
return nil, fmt.Errorf("%s exists now, nothing restored: look at it with claude_code_home_show first", full)
|
||||
} else if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
@@ -405,10 +405,10 @@ func RestoreHome(p Paths, kept string, now time.Time) (map[string]any, error) {
|
||||
written := []string{}
|
||||
undo := func() {
|
||||
for _, w := range written {
|
||||
_ = os.Remove(w)
|
||||
_ = fsys.Remove(w)
|
||||
}
|
||||
if isFolder {
|
||||
_ = os.RemoveAll(full)
|
||||
_ = fsys.RemoveAll(full)
|
||||
}
|
||||
}
|
||||
for in, content := range files {
|
||||
@@ -421,28 +421,22 @@ func RestoreHome(p Paths, kept string, now time.Time) (map[string]any, error) {
|
||||
if _, err := fmt.Sscanf(r.Files[in].Mode, "%o", &m); err == nil && m != 0 {
|
||||
mode = os.FileMode(m).Perm()
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(to), 0o755); err != nil {
|
||||
if err := fsys.MkdirAll(filepath.Dir(to), 0o755); err != nil {
|
||||
undo()
|
||||
return nil, fmt.Errorf("%s could not be restored: %w", full, err)
|
||||
}
|
||||
f, err := os.OpenFile(to, os.O_WRONLY|os.O_CREATE|os.O_EXCL, mode)
|
||||
if err != nil {
|
||||
if err := fsys.CreateExclusive(to, []byte(content), mode); err != nil {
|
||||
undo()
|
||||
return nil, fmt.Errorf("%s could not be restored: %w", full, err)
|
||||
return nil, fmt.Errorf("%s could not be restored: %w", to, err)
|
||||
}
|
||||
_, werr := f.WriteString(content)
|
||||
if cerr := f.Close(); werr != nil || cerr != nil {
|
||||
undo()
|
||||
return nil, fmt.Errorf("%s could not be restored", to)
|
||||
}
|
||||
_ = os.Chmod(to, mode) // the umask may have taken bits the file had
|
||||
_ = fsys.Chmod(to, mode) // the umask may have taken bits the file had
|
||||
written = append(written, to)
|
||||
}
|
||||
|
||||
back := Removal{Action: "restored", At: now.UTC().Format(time.RFC3339), Node: p.Node, Kind: r.Kind, Name: r.Name,
|
||||
Path: full, Why: "undoes the removal of " + r.At + ": " + r.Why, Kept: r.Kept, Files: r.Files}
|
||||
note, _ := indented(back)
|
||||
_ = os.WriteFile(filepath.Join(folder, "restored.json"), note, 0o600)
|
||||
_ = fsys.WriteFile(filepath.Join(folder, "restored.json"), note, 0o600)
|
||||
logged := logRemoval(p, back)
|
||||
say("restored %s from %s, undoing its removal of %s", full, r.Kept, r.At)
|
||||
answer := map[string]any{"restored": full, "kind": r.Kind, "name": r.Name, "from": r.Kept, "removedAt": r.At,
|
||||
|
||||
Reference in New Issue
Block a user