carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package instancectl
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "os"
9 "path/filepath"
10 "strings"
11
12 "github.com/carlosframework/platform/internal/provision"
13 "github.com/carlosframework/platform/internal/safefile"
14)
15
16// Only root configures these roots; requests cannot choose a source or target.
17// The private source hides secrets on the host. Inside the isolated namespace
18// the verified file is readable but cannot be replaced by the tenant/agent.
19func (u ClaimUnits) launchPaths(host string) (string, string, error) {
20 for _, root := range []string{u.UnitRoot, u.ExeRoot, u.DataRoot} {
21 if !filepath.IsAbs(root) || filepath.Clean(root) != root || strings.ContainsAny(root, " \t\r\n\x00:%") {
22 return "", "", ErrClaimUnit
23 }
24 }
25 if !provision.ValidHostShape(host) {
26 return "", "", ErrClaimUnit
27 }
28 unit, _, err := u.Placement(host)
29 if err != nil {
30 return "", "", err
31 }
32 return filepath.Join(u.ExeRoot, ".claim", provision.InstanceID(host)), filepath.Join(u.UnitRoot, unit+".d"), nil
33}
34
35func (u ClaimUnits) freezeLaunch(ctx context.Context, host string, launch Launch) error {
36 source, dropin, err := u.launchPaths(host)
37 if err != nil {
38 return err
39 }
40 if launch.Host != host || launch.Schema != LaunchSchema {
41 return ErrClaimUnit
42 }
43 // These are root-owned trees, opened without symlinks or permissive modes.
44 for _, dir := range []string{u.ExeRoot, filepath.Dir(source), source, u.UnitRoot, dropin} {
45 if err := claimRootDir(dir); err != nil {
46 return fmt.Errorf("root directory %s: %w", dir, err)
47 }
48 }
49 private, err := safefile.OpenDir(filepath.Dir(source))
50 if err != nil {
51 return err
52 }
53 err = private.Chmod(0700)
54 closeErr := private.Close()
55 if err != nil || closeErr != nil {
56 return errors.Join(err, closeErr)
57 }
58 raw, err := json.Marshal(launch)
59 if err != nil {
60 return err
61 }
62 if err := safefile.WriteFile(filepath.Join(source, "launch.json"), raw, 0444); err != nil {
63 return err
64 }
65 destination := filepath.Join(provision.InstanceDir(u.DataRoot, host), LaunchFileName(RoleRun))
66 body := "[Service]\nBindReadOnlyPaths=" + filepath.Join(source, "launch.json") + ":" + destination + "\n"
67 if err := safefile.WriteFile(filepath.Join(dropin, "60-claim-launch.conf"), []byte(body), 0644); err != nil {
68 return err
69 }
70 _, err = u.Systemctl(ctx, "daemon-reload")
71 return err
72}
73
74func (u ClaimUnits) clearLaunch(ctx context.Context, host string) error {
75 // Ordinary Start users do not configure the snapshot roots.
76 if u.UnitRoot == "" {
77 return nil
78 }
79 source, dropin, err := u.launchPaths(host)
80 if err != nil {
81 return err
82 }
83 // Each caller first proved the unit empty (or has not issued start).
84 err = safefile.Remove(filepath.Join(dropin, "60-claim-launch.conf"))
85 if err != nil && !errors.Is(err, os.ErrNotExist) {
86 return err
87 }
88 if _, err = u.Systemctl(ctx, "daemon-reload"); err != nil {
89 return err
90 }
91 for _, name := range []string{"launch.json", "permit.json", "exec.sock"} {
92 if err := safefile.Remove(filepath.Join(source, name)); err != nil && !errors.Is(err, os.ErrNotExist) {
93 return err
94 }
95 }
96 return nil
97}
98