package mirror import ( "bytes" "context" "encoding/json" "net/http" "os" "path/filepath" ) // The mirror's machine keeps llmgw's firewall as the cluster's settings // say, as every other machine does. The mirror runs unprivileged and cannot // change the ruleset: the control plane tells it what the settings say, it // writes that beside its models, and a root path unit on the machine // applies it with `llmgw firewall sync`. // FirewallState is what the cluster's settings say of a machine's firewall. type FirewallState struct { Enabled bool `json:"enabled"` // Networks are let in besides those the machine reaches others on. Networks []string `json:"networks,omitempty"` // Toward are the cluster's machines, by address: each one's network is // let in, as the machine's route to it says. Toward []string `json:"toward,omitempty"` } // FirewallFile is where, in the mirror's directory, the state is written. // Hidden, so it is never taken for a model. const FirewallFile = ".firewall.json" func (a *API) putFirewall(w http.ResponseWriter, r *http.Request) { var st FirewallState if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<20)).Decode(&st); err != nil { fail(w, http.StatusBadRequest, err) return } b, _ := json.Marshal(st) p := filepath.Join(a.m.root, FirewallFile) changed := false if old, err := os.ReadFile(p); err != nil || !bytes.Equal(old, b) { // Renamed into place: the path unit sees one change, of a whole file. tmp := p + ".new" if err := os.WriteFile(tmp, b, 0o644); err != nil { fail(w, http.StatusInternalServerError, err) return } if err := os.Rename(tmp, p); err != nil { fail(w, http.StatusInternalServerError, err) return } changed = true } writeJSON(w, http.StatusOK, map[string]bool{"changed": changed}) } // SetFirewall tells the mirror what the cluster's settings say of its // machine's firewall; it answers whether that changed anything. func (c *Client) SetFirewall(ctx context.Context, st FirewallState) (bool, error) { var out struct { Changed bool `json:"changed"` } err := c.do(ctx, http.MethodPut, "/v1/firewall", st, &out) return out.Changed, err }