oficina / home Public

Clone
git clone https://amadan.net/oficina/home

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package operator
4
5import (
6 "context"
7 "os"
8 "time"
9
10 "amadan.net/carlos/platform.git/launchctx"
11 "amadan.net/carlos/platform.git/launchctx/controlname"
12 "amadan.net/oficina/home/internal/operatorlaunch"
13)
14
15const commandConfigEnvironment = "HOME_OPERATOR_COMMAND"
16
17type commandConfig struct {
18 Version int `json:"version"`
19 Account string `json:"account"`
20 App string `json:"app"`
21 Host string `json:"host"`
22 Role string `json:"role"`
23 Origin string `json:"origin"`
24 Installation string `json:"installation"`
25 Registry string `json:"registry"`
26 Device string `json:"device"`
27 PublicKey string `json:"public_key"`
28 KeyID string `json:"key_id"`
29 Actions []string `json:"actions"`
30}
31
32func parseCommandConfig(raw string) (commandConfig, *Verifier, error) {
33 var c commandConfig
34 if len(raw) == 0 || len(raw) > 8192 || strict([]byte(raw), &c) != nil || c.Version != 1 || !controlname.ValidAccount(c.Account) || !controlname.ValidApp(c.App) || !controlname.ValidHostShape(c.Host) {
35 return commandConfig{}, nil, ErrRefused
36 }
37 v, err := NewVerifier(Pins{c.Role, c.Origin, c.Installation, c.Registry, c.Device, c.PublicKey, c.Actions})
38 if err != nil || c.KeyID != v.kid {
39 return commandConfig{}, nil, ErrRefused
40 }
41 return c, v, nil
42}
43
44// commandInvocation remains private to the unmounted command adapter. Nothing
45// in the HTTP app can supply a database, verifier or transport context to it.
46type commandInvocation struct {
47 database *boundDatabase
48 verifier *Verifier
49 request Request
50 launch launchctx.LaunchContext
51 raw []byte
52 ctx context.Context
53 cancel context.CancelFunc
54 inspect bool
55}
56
57func openCommandInvocation(parent context.Context, database string) (*commandInvocation, error) {
58 c, verifier, err := parseCommandConfig(os.Getenv(commandConfigEnvironment))
59 if err != nil {
60 return nil, ErrRefused
61 }
62 return openConfiguredInvocation(parent, database, c, verifier)
63}
64
65func openConfiguredInvocation(parent context.Context, database string, c commandConfig, verifier *Verifier) (*commandInvocation, error) {
66 launch, raw, err := operatorlaunch.ReadOperator(parent, database)
67 if err != nil {
68 return nil, ErrRefused
69 }
70 keep := false
71 defer func() {
72 if !keep {
73 clear(raw)
74 }
75 }()
76 if launch.Account != c.Account || launch.App != c.App || launch.Host != c.Host {
77 return nil, ErrRefused
78 }
79 request, err := verifier.Verify(raw)
80 inspect := false
81 if err != nil {
82 if !verifier.actions["directory.inspect"] {
83 return nil, ErrRefused
84 }
85 if _, err := verifier.verifyInspect(raw, time.Now()); err != nil {
86 return nil, ErrRefused
87 }
88 inspect = true
89 }
90 defer func() {
91 if !keep {
92 clear(request.input)
93 }
94 }()
95 deadline, err := time.Parse(time.RFC3339Nano, launch.ExecutionDeadline)
96 if err != nil || parent.Err() != nil {
97 return nil, ErrRefused
98 }
99 if callerDeadline, ok := parent.Deadline(); ok && callerDeadline.Before(deadline) {
100 deadline = callerDeadline
101 }
102 ctx, cancel := context.WithDeadline(parent, deadline)
103 db, err := newBoundDatabase(launch.DatabasePath, launch.DatabaseDevice, launch.DatabaseInode, deadline)
104 if err != nil {
105 cancel()
106 if err == ErrUnavailable {
107 return nil, ErrUnavailable
108 }
109 return nil, ErrRefused
110 }
111 keep = true
112 return &commandInvocation{db, verifier, request, launch, raw, ctx, cancel, inspect}, nil
113}
114
115func (i *commandInvocation) close() error {
116 if i == nil || i.database == nil {
117 return ErrRefused
118 }
119 defer i.cancel()
120 defer clear(i.raw)
121 defer clear(i.request.input)
122 i.database.connector.mu.Lock()
123 identityErr := i.database.connector.pathMatches()
124 i.database.connector.mu.Unlock()
125 closeErr := i.database.close()
126 i.database = nil
127 if identityErr != nil || closeErr != nil || i.ctx.Err() != nil {
128 return ErrUnavailable
129 }
130 return nil
131}
132