diff --git a/cmd/api/api/instances.go b/cmd/api/api/instances.go index d145fa9b..330d0d77 100644 --- a/cmd/api/api/instances.go +++ b/cmd/api/api/instances.go @@ -25,6 +25,25 @@ import ( "github.com/samber/lo" ) +// toDomainNetworkEgress maps the OpenAPI egress policy to the domain type. +// Normalization and validation happen in the instances package. +func toDomainNetworkEgress(egress *oapi.CreateInstanceRequestNetworkEgress) *instances.NetworkEgressPolicy { + if egress == nil { + return nil + } + enabled := egress.Enabled != nil && *egress.Enabled + policy := &instances.NetworkEgressPolicy{Enabled: enabled} + if egress.Proxy != nil { + policy.Proxy = instances.EgressProxyMode(*egress.Proxy) + } + if egress.Enforcement != nil && egress.Enforcement.Mode != nil { + policy.EnforcementMode = instances.EgressEnforcementMode(*egress.Enforcement.Mode) + } else if enabled { + policy.EnforcementMode = instances.EgressEnforcementModeAll + } + return policy +} + // ListInstances lists instances, optionally filtered by state and/or tags. func (s *ApiService) ListInstances(ctx context.Context, request oapi.ListInstancesRequestObject) (oapi.ListInstancesResponseObject, error) { log := logger.FromContext(ctx) @@ -140,14 +159,8 @@ func (s *ApiService) CreateInstance(ctx context.Context, request oapi.CreateInst networkEnabled = *request.Body.Network.Enabled } var networkEgress *instances.NetworkEgressPolicy - if request.Body.Network != nil && request.Body.Network.Egress != nil { - enabled := request.Body.Network.Egress.Enabled != nil && *request.Body.Network.Egress.Enabled - networkEgress = &instances.NetworkEgressPolicy{Enabled: enabled} - if request.Body.Network.Egress.Enforcement != nil && request.Body.Network.Egress.Enforcement.Mode != nil { - networkEgress.EnforcementMode = instances.EgressEnforcementMode(*request.Body.Network.Egress.Enforcement.Mode) - } else if enabled { - networkEgress.EnforcementMode = instances.EgressEnforcementModeAll - } + if request.Body.Network != nil { + networkEgress = toDomainNetworkEgress(request.Body.Network.Egress) } var credentials map[string]instances.CredentialPolicy if request.Body.Credentials != nil { @@ -1039,6 +1052,8 @@ func (s *ApiService) UpdateInstance(ctx context.Context, request oapi.UpdateInst HealthCheck: healthCheck, RestartPolicy: restartPolicy, RestartPolicySet: request.Body.RestartPolicy != nil, + NetworkEgress: toDomainNetworkEgress(request.Body.Egress), + NetworkEgressSet: request.Body.Egress != nil, }) if err != nil { switch { diff --git a/lib/egressproxy/README.md b/lib/egressproxy/README.md index 0796ef4a..e58e0c48 100644 --- a/lib/egressproxy/README.md +++ b/lib/egressproxy/README.md @@ -32,6 +32,10 @@ That means the VM can make authenticated outbound requests without ever receivin - Per instance, `network.egress.enforcement.mode` controls host-side direct egress blocking: - `all` (default when proxy is enabled): reject direct non-proxy TCP egress from the VM TAP interface. - `http_https_only`: reject direct TCP egress only on destination ports `80` and `443`. + - `all_traffic`: reject direct TCP and UDP egress. This also blocks QUIC and direct DNS, so the guest must resolve names through its proxy. +- Per instance, `network.egress.proxy` selects the host-side proxy behavior: + - `mitm` (default): the full mediated path described in this document. + - `none`: enforcement only. The host applies the TAP-level egress rules but does not run the MITM proxy, inject `HTTP_PROXY`/`HTTPS_PROXY` into the guest, or install the proxy CA. Intended for guests configured to reach an external proxy at the network gateway. Credentials require `mitm`. Enforcement-only policies can also be set or cleared on a live instance via `PATCH /instances/{id}` (`egress`); `mitm` policies cannot, since their guest-visible configuration is fixed at boot. - Inside the VM, each credential key is rewritten to `mock-` (for example `mock-OUTBOUND_OPENAI_KEY`). - Header injection is applied to HTTPS requests only after MITM decryption. - For HTTPS egress, the proxy validates upstream TLS certificates with the host trust store before forwarding. @@ -64,7 +68,7 @@ Operationally, this is intended for key rotation, revocation/reissue flows, and - Real secret values are persisted in the normal instance `env` metadata, which is already host-side state. - TLS interception requires guest trust of the proxy CA; hypeman installs this CA in the guest when proxy mode is enabled. - Egress enforcement is applied per instance TAP device and removed when the instance stops/standbys/deletes. -- Enforcement intentionally targets TCP egress only. DNS/other non-TCP traffic is not rewritten and is not blocked by `all` mode. +- The `all` and `http_https_only` modes target TCP egress only; DNS/other non-TCP traffic is not rewritten and not blocked. Use `all_traffic` to also block direct UDP egress. ## Limits of enforcement diff --git a/lib/egressproxy/enforce.go b/lib/egressproxy/enforce.go new file mode 100644 index 00000000..737da724 --- /dev/null +++ b/lib/egressproxy/enforce.go @@ -0,0 +1,14 @@ +package egressproxy + +// ApplyEnforcement installs host-side egress enforcement for an instance TAP +// device without registering the instance with the MITM proxy service. Used +// for enforcement-only egress policies (network.egress.proxy=none). +func ApplyEnforcement(instanceID, tapDevice, gatewayIP string, blockAllTCPEgress, blockUDPEgress bool) error { + return applyEgressEnforcement(instanceID, tapDevice, gatewayIP, blockAllTCPEgress, blockUDPEgress) +} + +// RemoveEnforcement removes any host-side egress enforcement rules for an +// instance. Safe to call for instances that never had enforcement applied. +func RemoveEnforcement(instanceID string) error { + return removeEgressEnforcement(instanceID) +} diff --git a/lib/egressproxy/enforce_linux.go b/lib/egressproxy/enforce_linux.go index 898f8475..e22746e7 100644 --- a/lib/egressproxy/enforce_linux.go +++ b/lib/egressproxy/enforce_linux.go @@ -13,34 +13,46 @@ const ( enforcementSuffixPort80 = "80" enforcementSuffixPort443 = "443" enforcementSuffixAllTCP = "all-tcp" + enforcementSuffixAllUDP = "all-udp" ) -func applyEgressEnforcement(instanceID, tapDevice, gatewayIP string, proxyPort int, blockAllTCPEgress bool) error { - if instanceID == "" || tapDevice == "" || gatewayIP == "" || proxyPort <= 0 { +func applyEgressEnforcement(instanceID, tapDevice, gatewayIP string, blockAllTCPEgress, blockUDPEgress bool) error { + if instanceID == "" || tapDevice == "" || gatewayIP == "" { return fmt.Errorf("invalid egress enforcement inputs") } comment80 := enforcementComment(instanceID, enforcementSuffixPort80) comment443 := enforcementComment(instanceID, enforcementSuffixPort443) commentAllTCP := enforcementComment(instanceID, enforcementSuffixAllTCP) + commentAllUDP := enforcementComment(instanceID, enforcementSuffixAllUDP) // Clean old rules first so updates are idempotent across restarts and mode changes. _ = removeRuleByComment(comment80) _ = removeRuleByComment(comment443) _ = removeRuleByComment(commentAllTCP) + _ = removeRuleByComment(commentAllUDP) + + if blockUDPEgress { + if err := insertRejectAllRule(tapDevice, gatewayIP, "udp", commentAllUDP); err != nil { + return fmt.Errorf("insert all-udp egress enforcement: %w", err) + } + } if blockAllTCPEgress { - if err := insertRejectAllTCPRule(tapDevice, gatewayIP, commentAllTCP); err != nil { + if err := insertRejectAllRule(tapDevice, gatewayIP, "tcp", commentAllTCP); err != nil { + _ = removeRuleByComment(commentAllUDP) return fmt.Errorf("insert all-tcp egress enforcement: %w", err) } return nil } if err := insertRejectRule(tapDevice, gatewayIP, 80, comment80); err != nil { + _ = removeRuleByComment(commentAllUDP) return fmt.Errorf("insert port 80 egress enforcement: %w", err) } if err := insertRejectRule(tapDevice, gatewayIP, 443, comment443); err != nil { _ = removeRuleByComment(comment80) + _ = removeRuleByComment(commentAllUDP) return fmt.Errorf("insert port 443 egress enforcement: %w", err) } @@ -51,12 +63,10 @@ func removeEgressEnforcement(instanceID string) error { if instanceID == "" { return nil } - comment80 := enforcementComment(instanceID, enforcementSuffixPort80) - comment443 := enforcementComment(instanceID, enforcementSuffixPort443) - commentAllTCP := enforcementComment(instanceID, enforcementSuffixAllTCP) - _ = removeRuleByComment(comment80) - _ = removeRuleByComment(comment443) - _ = removeRuleByComment(commentAllTCP) + _ = removeRuleByComment(enforcementComment(instanceID, enforcementSuffixPort80)) + _ = removeRuleByComment(enforcementComment(instanceID, enforcementSuffixPort443)) + _ = removeRuleByComment(enforcementComment(instanceID, enforcementSuffixAllTCP)) + _ = removeRuleByComment(enforcementComment(instanceID, enforcementSuffixAllUDP)) return nil } @@ -76,11 +86,11 @@ func insertRejectRule(tapDevice, gatewayIP string, port int, comment string) err return nil } -func insertRejectAllTCPRule(tapDevice, gatewayIP, comment string) error { +func insertRejectAllRule(tapDevice, gatewayIP, protocol, comment string) error { cmd := exec.Command( "iptables", "-I", "FORWARD", "1", "-i", tapDevice, - "-p", "tcp", + "-p", protocol, "!", "-d", gatewayIP, "-m", "comment", "--comment", comment, "-j", "REJECT", diff --git a/lib/egressproxy/enforce_other.go b/lib/egressproxy/enforce_other.go index 3eb72c04..4c9e4c34 100644 --- a/lib/egressproxy/enforce_other.go +++ b/lib/egressproxy/enforce_other.go @@ -2,8 +2,7 @@ package egressproxy -func applyEgressEnforcement(instanceID, tapDevice, gatewayIP string, proxyPort int, blockAllTCPEgress bool) error { - _ = blockAllTCPEgress +func applyEgressEnforcement(instanceID, tapDevice, gatewayIP string, blockAllTCPEgress, blockUDPEgress bool) error { return nil } diff --git a/lib/egressproxy/service.go b/lib/egressproxy/service.go index acd6172c..fd5f4eb3 100644 --- a/lib/egressproxy/service.go +++ b/lib/egressproxy/service.go @@ -193,7 +193,7 @@ func (s *Service) RegisterInstance(ctx context.Context, gatewayIP string, cfg In log := s.loggerForContext(ctx) result := "error" var opErr error - enforcementMode := enforcementModeLabel(cfg.BlockAllTCPEgress) + enforcementMode := enforcementModeLabel(cfg.BlockAllTCPEgress, cfg.BlockUDPEgress) ctx, span := s.startControlPlaneSpan(ctx, "EgressProxy.RegisterInstance", attribute.String("operation", "register"), attribute.String("enforcement_mode", enforcementMode), @@ -212,7 +212,7 @@ func (s *Service) RegisterInstance(ctx context.Context, gatewayIP string, cfg In return GuestConfig{}, err } - if err := applyEgressEnforcement(cfg.InstanceID, cfg.TAPDevice, gatewayIP, s.listenPort, cfg.BlockAllTCPEgress); err != nil { + if err := applyEgressEnforcement(cfg.InstanceID, cfg.TAPDevice, gatewayIP, cfg.BlockAllTCPEgress, cfg.BlockUDPEgress); err != nil { log.WarnContext(ctx, "failed to apply egress proxy enforcement", "instance_id", cfg.InstanceID, "error", err) opErr = err return GuestConfig{}, err @@ -651,7 +651,10 @@ func (s *Service) finishControlPlaneSpan(span trace.Span, result string, err err span.End() } -func enforcementModeLabel(blockAllTCPEgress bool) string { +func enforcementModeLabel(blockAllTCPEgress, blockUDPEgress bool) string { + if blockAllTCPEgress && blockUDPEgress { + return "all_traffic" + } if blockAllTCPEgress { return "all" } diff --git a/lib/egressproxy/types.go b/lib/egressproxy/types.go index 3a2e59fa..49a3051f 100644 --- a/lib/egressproxy/types.go +++ b/lib/egressproxy/types.go @@ -22,6 +22,7 @@ type InstanceConfig struct { SourceIP string TAPDevice string BlockAllTCPEgress bool + BlockUDPEgress bool HeaderInjectRules []HeaderInjectRuleConfig } diff --git a/lib/instances/create.go b/lib/instances/create.go index 1bc6c867..024088c3 100644 --- a/lib/instances/create.go +++ b/lib/instances/create.go @@ -586,6 +586,11 @@ func validateCreateRequest(req *CreateInstanceRequest) error { return err } req.NetworkEgress.EnforcementMode = mode + proxyMode, err := normalizeEgressProxyMode(req.NetworkEgress.Proxy) + if err != nil { + return err + } + req.NetworkEgress.Proxy = proxyMode } normalizedCredentials, err := normalizeCredentialPolicies(req.Credentials) if err != nil { @@ -596,6 +601,9 @@ func validateCreateRequest(req *CreateInstanceRequest) error { if req.NetworkEgress == nil || !req.NetworkEgress.Enabled { return fmt.Errorf("%w: credentials require network.egress.enabled=true", ErrInvalidRequest) } + if req.NetworkEgress.Proxy == EgressProxyModeNone { + return fmt.Errorf("%w: credentials require network.egress.proxy=mitm", ErrInvalidRequest) + } if err := validateCredentialEnvBindings(normalizedCredentials, req.Env); err != nil { return err } diff --git a/lib/instances/create_egress_proxy_test.go b/lib/instances/create_egress_proxy_test.go index f7ef69bd..d445846d 100644 --- a/lib/instances/create_egress_proxy_test.go +++ b/lib/instances/create_egress_proxy_test.go @@ -139,6 +139,109 @@ func TestValidateCreateRequest_AllowsHTTPHTTPSOnlyEgressMode(t *testing.T) { assert.Equal(t, EgressEnforcementModeHTTPHTTPSOnly, cfg.EnforcementMode) } +func TestValidateCreateRequest_NormalizesEgressProxyMode(t *testing.T) { + t.Parallel() + + cfg := &NetworkEgressPolicy{Enabled: true} + req := CreateInstanceRequest{ + Name: "test-egress", + Image: "docker.io/library/alpine:latest", + NetworkEnabled: true, + NetworkEgress: cfg, + } + + err := validateCreateRequest(&req) + require.NoError(t, err) + assert.Equal(t, EgressProxyModeMITM, cfg.Proxy) +} + +func TestValidateCreateRequest_RejectsInvalidEgressProxyMode(t *testing.T) { + t.Parallel() + + req := CreateInstanceRequest{ + Name: "test-egress", + Image: "docker.io/library/alpine:latest", + NetworkEnabled: true, + NetworkEgress: &NetworkEgressPolicy{ + Enabled: true, + Proxy: EgressProxyMode("bogus"), + }, + } + + err := validateCreateRequest(&req) + require.Error(t, err) + assert.ErrorIs(t, err, ErrInvalidRequest) + assert.Contains(t, err.Error(), "invalid network.egress.proxy") +} + +func TestValidateCreateRequest_AllowsEnforcementOnlyEgress(t *testing.T) { + t.Parallel() + + cfg := &NetworkEgressPolicy{ + Enabled: true, + Proxy: EgressProxyModeNone, + EnforcementMode: EgressEnforcementModeAllTraffic, + } + req := CreateInstanceRequest{ + Name: "test-egress", + Image: "docker.io/library/alpine:latest", + NetworkEnabled: true, + NetworkEgress: cfg, + } + + err := validateCreateRequest(&req) + require.NoError(t, err) + assert.Equal(t, EgressProxyModeNone, cfg.Proxy) + assert.Equal(t, EgressEnforcementModeAllTraffic, cfg.EnforcementMode) +} + +func TestValidateCreateRequest_CredentialsRequireMITMProxy(t *testing.T) { + t.Parallel() + + req := CreateInstanceRequest{ + Name: "test-egress", + Image: "docker.io/library/alpine:latest", + NetworkEnabled: true, + Env: map[string]string{"OUTBOUND_OPENAI_KEY": "real"}, + NetworkEgress: &NetworkEgressPolicy{ + Enabled: true, + Proxy: EgressProxyModeNone, + }, + Credentials: map[string]CredentialPolicy{ + "OUTBOUND_OPENAI_KEY": { + Source: CredentialSource{Env: "OUTBOUND_OPENAI_KEY"}, + Inject: []CredentialInjectRule{{ + As: CredentialInjectAs{Header: "Authorization", Format: "Bearer ${value}"}, + }}, + }, + }, + } + + err := validateCreateRequest(&req) + require.Error(t, err) + assert.ErrorIs(t, err, ErrInvalidRequest) + assert.Contains(t, err.Error(), "credentials require network.egress.proxy=mitm") +} + +func TestEgressBlockFlags(t *testing.T) { + t.Parallel() + + cases := []struct { + mode EgressEnforcementMode + blockAllTCP bool + blockUDP bool + }{ + {EgressEnforcementModeAll, true, false}, + {EgressEnforcementModeHTTPHTTPSOnly, false, false}, + {EgressEnforcementModeAllTraffic, true, true}, + } + for _, tc := range cases { + blockAllTCP, blockUDP := egressBlockFlags(tc.mode) + assert.Equal(t, tc.blockAllTCP, blockAllTCP, "mode %s", tc.mode) + assert.Equal(t, tc.blockUDP, blockUDP, "mode %s", tc.mode) + } +} + func TestValidateCreateRequest_NormalizesCredentialsInPlace(t *testing.T) { t.Parallel() diff --git a/lib/instances/egress_proxy.go b/lib/instances/egress_proxy.go index 82a3c4e1..fe6aebbb 100644 --- a/lib/instances/egress_proxy.go +++ b/lib/instances/egress_proxy.go @@ -22,6 +22,7 @@ func cloneNetworkEgressPolicy(cfg *NetworkEgressPolicy) *NetworkEgressPolicy { } return &NetworkEgressPolicy{ Enabled: cfg.Enabled, + Proxy: cfg.Proxy, EnforcementMode: cfg.EnforcementMode, } } @@ -66,11 +67,45 @@ func normalizeEgressEnforcementMode(mode EgressEnforcementMode) (EgressEnforceme return EgressEnforcementModeAll, nil case EgressEnforcementModeHTTPHTTPSOnly: return EgressEnforcementModeHTTPHTTPSOnly, nil + case EgressEnforcementModeAllTraffic: + return EgressEnforcementModeAllTraffic, nil default: return "", fmt.Errorf("%w: invalid network.egress.enforcement.mode %q", ErrInvalidRequest, trimmed) } } +func normalizeEgressProxyMode(mode EgressProxyMode) (EgressProxyMode, error) { + trimmed := strings.TrimSpace(string(mode)) + switch EgressProxyMode(trimmed) { + case "", EgressProxyModeMITM: + return EgressProxyModeMITM, nil + case EgressProxyModeNone: + return EgressProxyModeNone, nil + default: + return "", fmt.Errorf("%w: invalid network.egress.proxy %q", ErrInvalidRequest, trimmed) + } +} + +// egressProxyMode resolves the effective proxy mode for a policy; metadata +// persisted before the Proxy field existed resolves to mitm. +func egressProxyMode(policy *NetworkEgressPolicy) EgressProxyMode { + if policy == nil || policy.Proxy == "" { + return EgressProxyModeMITM + } + return policy.Proxy +} + +func egressBlockFlags(mode EgressEnforcementMode) (blockAllTCP, blockUDP bool) { + switch mode { + case EgressEnforcementModeHTTPHTTPSOnly: + return false, false + case EgressEnforcementModeAllTraffic: + return true, true + default: + return true, false + } +} + func normalizeCredentialPolicies(in map[string]CredentialPolicy) (map[string]CredentialPolicy, error) { if len(in) == 0 { return nil, nil @@ -263,6 +298,7 @@ func (m *manager) maybeRegisterEgressProxy(ctx context.Context, stored *StoredMe return nil, fmt.Errorf("network.egress requires network.enabled=true") } + proxyMode := egressProxyMode(stored.NetworkEgress) rules := buildEgressProxyInjectRules(stored.NetworkEgress, stored.Credentials, stored.Env) enforcementMode := string(stored.NetworkEgress.EnforcementMode) if enforcementMode == "" { @@ -275,12 +311,25 @@ func (m *manager) maybeRegisterEgressProxy(ctx context.Context, stored *StoredMe span.SetAttributes( attribute.String("operation", "register"), attribute.String("enforcement_mode", enforcementMode), - attribute.Bool("proxy_enabled", true), + attribute.Bool("proxy_enabled", proxyMode == EgressProxyModeMITM), attribute.Int("inject_rule_count", len(rules)), ) defer span.End() } + blockAllTCP, blockUDP := egressBlockFlags(stored.NetworkEgress.EnforcementMode) + + if proxyMode == EgressProxyModeNone { + if err := egressproxy.ApplyEnforcement(stored.Id, netConfig.TAPDevice, netConfig.Gateway, blockAllTCP, blockUDP); err != nil { + if span != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + } + return nil, fmt.Errorf("apply egress enforcement: %w", err) + } + return nil, nil + } + svc, err := m.getOrCreateEgressProxyService() if err != nil { if span != nil { @@ -294,7 +343,8 @@ func (m *manager) maybeRegisterEgressProxy(ctx context.Context, stored *StoredMe InstanceID: stored.Id, SourceIP: netConfig.IP, TAPDevice: netConfig.TAPDevice, - BlockAllTCPEgress: stored.NetworkEgress.EnforcementMode != EgressEnforcementModeHTTPHTTPSOnly, + BlockAllTCPEgress: blockAllTCP, + BlockUDPEgress: blockUDP, HeaderInjectRules: rules, }) if err != nil { @@ -313,6 +363,11 @@ func (m *manager) unregisterEgressProxyInstance(ctx context.Context, instanceID svc := m.egressProxy m.egressProxyMu.Unlock() if svc == nil { + // Enforcement-only instances never start the proxy service; their + // iptables rules still need removal. + if err := egressproxy.RemoveEnforcement(instanceID); err != nil { + logger.FromContext(ctx).WarnContext(ctx, "failed to remove egress enforcement", "instance_id", instanceID, "error", err) + } return } if err := svc.UnregisterInstance(ctx, instanceID); err != nil { diff --git a/lib/instances/types.go b/lib/instances/types.go index 6ff42406..f9c89c2d 100644 --- a/lib/instances/types.go +++ b/lib/instances/types.go @@ -31,6 +31,18 @@ type EgressEnforcementMode string const ( EgressEnforcementModeAll EgressEnforcementMode = "all" EgressEnforcementModeHTTPHTTPSOnly EgressEnforcementMode = "http_https_only" + EgressEnforcementModeAllTraffic EgressEnforcementMode = "all_traffic" +) + +// EgressProxyMode selects the host-side proxy behavior for the mediated egress path. +type EgressProxyMode string + +const ( + // EgressProxyModeMITM runs the host MITM proxy and injects proxy env/CA into the guest. + EgressProxyModeMITM EgressProxyMode = "mitm" + // EgressProxyModeNone applies host-side egress enforcement only; no proxy + // process, guest env injection, or CA install. + EgressProxyModeNone EgressProxyMode = "none" ) // VolumeAttachment represents a volume attached to an instance @@ -45,7 +57,8 @@ type VolumeAttachment struct { // NetworkEgressPolicy configures host-mediated outbound networking behavior. type NetworkEgressPolicy struct { Enabled bool // Whether host-mediated egress policy is enabled - EnforcementMode EgressEnforcementMode // all (default) blocks direct non-proxy TCP egress, http_https_only blocks only 80/443 + Proxy EgressProxyMode // mitm (default; empty means mitm for pre-existing metadata) or none (enforcement only) + EnforcementMode EgressEnforcementMode // all (default) blocks direct non-proxy TCP egress, http_https_only blocks only 80/443, all_traffic blocks TCP and UDP } // CredentialSource references where real credential material is loaded from. @@ -290,6 +303,8 @@ type UpdateInstanceRequest struct { HealthCheck *healthcheck.Policy // Replaces the persisted health check policy when non-nil RestartPolicy *restartpolicy.Policy // Replaces the persisted restart policy when non-nil RestartPolicySet bool // True when restart policy was present in the update request + NetworkEgress *NetworkEgressPolicy // Replaces the persisted egress policy (enforcement-only policies) + NetworkEgressSet bool // True when egress was present in the update request } // ForkInstanceRequest is the domain request for forking an instance. diff --git a/lib/instances/update.go b/lib/instances/update.go index cdea9e4c..88fcb68b 100644 --- a/lib/instances/update.go +++ b/lib/instances/update.go @@ -48,6 +48,18 @@ func (m *manager) updateInstance(ctx context.Context, id string, req UpdateInsta } req.RestartPolicy = normalizedRestartPolicy } + if req.NetworkEgressSet && req.NetworkEgress != nil && req.NetworkEgress.Enabled { + mode, err := normalizeEgressEnforcementMode(req.NetworkEgress.EnforcementMode) + if err != nil { + return nil, err + } + req.NetworkEgress.EnforcementMode = mode + proxyMode, err := normalizeEgressProxyMode(req.NetworkEgress.Proxy) + if err != nil { + return nil, err + } + req.NetworkEgress.Proxy = proxyMode + } if err := validateUpdateInstanceRequest(meta, req); err != nil { return nil, err @@ -67,8 +79,16 @@ func (m *manager) updateInstance(ctx context.Context, id string, req UpdateInsta nextMeta.RestartPolicy = cloneRestartPolicy(req.RestartPolicy) nextMeta.RestartStatus = restartStatusAfterPolicyUpdate(nextMeta.RestartStatus) } + if req.NetworkEgressSet { + nextMeta.NetworkEgress = cloneNetworkEgressPolicy(req.NetworkEgress) + } if len(req.Env) == 0 { + revertEgress, err := m.applyEgressEnforcementUpdate(ctx, id, inst.State, meta.NetworkEgress, nextMeta.NetworkEgress, req.NetworkEgressSet) + if err != nil { + return nil, err + } if err := m.saveMetadata(nextMeta); err != nil { + revertEgress() return nil, fmt.Errorf("save metadata: %w", err) } @@ -114,8 +134,24 @@ func (m *manager) updateInstance(ctx context.Context, id string, req UpdateInsta } func validateUpdateInstanceRequest(meta *metadata, req UpdateInstanceRequest) error { - if len(req.Env) == 0 && req.AutoStandby == nil && req.HealthCheck == nil && !req.RestartPolicySet { - return fmt.Errorf("%w: request must include env, auto_standby, health_check, and/or restart_policy", ErrInvalidRequest) + if len(req.Env) == 0 && req.AutoStandby == nil && req.HealthCheck == nil && !req.RestartPolicySet && !req.NetworkEgressSet { + return fmt.Errorf("%w: request must include env, auto_standby, health_check, restart_policy, and/or egress", ErrInvalidRequest) + } + if req.NetworkEgressSet { + if meta == nil { + return fmt.Errorf("%w: instance metadata is required", ErrInvalidRequest) + } + if meta.NetworkEgress != nil && meta.NetworkEgress.Enabled && egressProxyMode(meta.NetworkEgress) == EgressProxyModeMITM { + return fmt.Errorf("%w: egress policy of instances using proxy=mitm cannot be updated; recreate the instance", ErrInvalidRequest) + } + if req.NetworkEgress != nil && req.NetworkEgress.Enabled { + if !meta.NetworkEnabled { + return fmt.Errorf("%w: egress requires network.enabled=true", ErrInvalidRequest) + } + if req.NetworkEgress.Proxy != EgressProxyModeNone { + return fmt.Errorf("%w: only enforcement-only egress (proxy=none) can be set via update", ErrInvalidRequest) + } + } } if req.HealthCheck != nil { if meta == nil { @@ -156,6 +192,49 @@ func validateUpdateInstanceRequest(meta *metadata, req UpdateInstanceRequest) er return nil } +// applyEgressEnforcementUpdate reconciles host-side egress enforcement with the +// next policy for a live instance, returning a revert func that restores the +// previous enforcement state if the metadata save fails. Policies that require +// the MITM proxy are rejected by validation before this point, so only iptables +// state changes here — nothing guest-visible. +func (m *manager) applyEgressEnforcementUpdate(ctx context.Context, id string, state State, prev, next *NetworkEgressPolicy, set bool) (func(), error) { + noop := func() {} + if !set { + return noop, nil + } + if state != StateRunning && state != StateInitializing { + // Stopped/standby instances pick up the persisted policy on next + // start/restore via maybeRegisterEgressProxy. + return noop, nil + } + + alloc, err := m.networkManager.GetAllocation(ctx, id) + if err != nil { + return nil, fmt.Errorf("get network allocation for egress update: %w", err) + } + if alloc == nil { + return nil, fmt.Errorf("%w: instance has no active network allocation", ErrInvalidState) + } + + apply := func(policy *NetworkEgressPolicy) error { + if policy == nil || !policy.Enabled { + return egressproxy.RemoveEnforcement(id) + } + blockAllTCP, blockUDP := egressBlockFlags(policy.EnforcementMode) + return egressproxy.ApplyEnforcement(id, alloc.TAPDevice, alloc.Gateway, blockAllTCP, blockUDP) + } + + if err := apply(next); err != nil { + return nil, fmt.Errorf("apply egress enforcement: %w", err) + } + revert := func() { + if err := apply(prev); err != nil { + logger.FromContext(ctx).WarnContext(ctx, "failed to revert egress enforcement after metadata save failure", "instance_id", id, "error", err) + } + } + return revert, nil +} + func cloneEnvMap(in map[string]string) map[string]string { if len(in) == 0 { return nil diff --git a/lib/instances/update_test.go b/lib/instances/update_test.go index f3f1e16f..6f3a8d00 100644 --- a/lib/instances/update_test.go +++ b/lib/instances/update_test.go @@ -30,7 +30,58 @@ func TestValidateUpdateInstanceRequest(t *testing.T) { err := validateUpdateInstanceRequest(baseMeta, UpdateInstanceRequest{}) require.Error(t, err) assert.ErrorIs(t, err, ErrInvalidRequest) - assert.Contains(t, err.Error(), "env, auto_standby, health_check, and/or restart_policy") + assert.Contains(t, err.Error(), "env, auto_standby, health_check, restart_policy, and/or egress") + }) + + t.Run("rejects egress update on mitm instances", func(t *testing.T) { + err := validateUpdateInstanceRequest(baseMeta, UpdateInstanceRequest{ + NetworkEgress: &NetworkEgressPolicy{Enabled: false}, + NetworkEgressSet: true, + }) + require.Error(t, err) + assert.ErrorIs(t, err, ErrInvalidRequest) + assert.Contains(t, err.Error(), "proxy=mitm") + }) + + t.Run("rejects mitm egress via update", func(t *testing.T) { + err := validateUpdateInstanceRequest(&metadata{StoredMetadata: StoredMetadata{NetworkEnabled: true}}, UpdateInstanceRequest{ + NetworkEgress: &NetworkEgressPolicy{Enabled: true, Proxy: EgressProxyModeMITM}, + NetworkEgressSet: true, + }) + require.Error(t, err) + assert.ErrorIs(t, err, ErrInvalidRequest) + assert.Contains(t, err.Error(), "proxy=none") + }) + + t.Run("rejects egress update without networking", func(t *testing.T) { + err := validateUpdateInstanceRequest(&metadata{}, UpdateInstanceRequest{ + NetworkEgress: &NetworkEgressPolicy{Enabled: true, Proxy: EgressProxyModeNone}, + NetworkEgressSet: true, + }) + require.Error(t, err) + assert.ErrorIs(t, err, ErrInvalidRequest) + assert.Contains(t, err.Error(), "network.enabled") + }) + + t.Run("allows enforcement-only egress update", func(t *testing.T) { + meta := &metadata{StoredMetadata: StoredMetadata{NetworkEnabled: true}} + err := validateUpdateInstanceRequest(meta, UpdateInstanceRequest{ + NetworkEgress: &NetworkEgressPolicy{Enabled: true, Proxy: EgressProxyModeNone, EnforcementMode: EgressEnforcementModeAllTraffic}, + NetworkEgressSet: true, + }) + require.NoError(t, err) + }) + + t.Run("allows disabling enforcement-only egress", func(t *testing.T) { + meta := &metadata{StoredMetadata: StoredMetadata{ + NetworkEnabled: true, + NetworkEgress: &NetworkEgressPolicy{Enabled: true, Proxy: EgressProxyModeNone}, + }} + err := validateUpdateInstanceRequest(meta, UpdateInstanceRequest{ + NetworkEgress: &NetworkEgressPolicy{Enabled: false}, + NetworkEgressSet: true, + }) + require.NoError(t, err) }) t.Run("rejects instances without credential backed envs", func(t *testing.T) { diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index f95fefd5..ad340037 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -81,9 +81,16 @@ const ( CreateInstanceRequestHypervisorVz CreateInstanceRequestHypervisor = "vz" ) +// Defines values for CreateInstanceRequestNetworkEgressProxy. +const ( + CreateInstanceRequestNetworkEgressProxyMitm CreateInstanceRequestNetworkEgressProxy = "mitm" + CreateInstanceRequestNetworkEgressProxyNone CreateInstanceRequestNetworkEgressProxy = "none" +) + // Defines values for CreateInstanceRequestNetworkEgressEnforcementMode. const ( All CreateInstanceRequestNetworkEgressEnforcementMode = "all" + AllTraffic CreateInstanceRequestNetworkEgressEnforcementMode = "all_traffic" HttpHttpsOnly CreateInstanceRequestNetworkEgressEnforcementMode = "http_https_only" ) @@ -129,8 +136,8 @@ const ( // Defines values for HealthCheckHTTPScheme. const ( - HealthCheckHTTPSchemeHttp HealthCheckHTTPScheme = "http" - HealthCheckHTTPSchemeHttps HealthCheckHTTPScheme = "https" + Http HealthCheckHTTPScheme = "http" + Https HealthCheckHTTPScheme = "https" ) // Defines values for ImageStatus. @@ -607,17 +614,37 @@ type CreateInstanceRequestNetworkEgress struct { // Enforcement Egress enforcement policy applied when mediation is enabled. Enforcement *CreateInstanceRequestNetworkEgressEnforcement `json:"enforcement,omitempty"` + + // Proxy Host-side proxy behavior for the mediated egress path. + // `mitm` (default) runs the host MITM proxy, injects `HTTP_PROXY`/`HTTPS_PROXY` + // into the guest, and installs the proxy CA in the guest trust store. + // `none` applies host-side egress enforcement only: no proxy process, no guest + // env injection, and no CA install. Use `none` when the guest is configured to + // reach an external proxy at the network gateway. Credentials require `mitm`. + Proxy *CreateInstanceRequestNetworkEgressProxy `json:"proxy,omitempty"` } +// CreateInstanceRequestNetworkEgressProxy Host-side proxy behavior for the mediated egress path. +// `mitm` (default) runs the host MITM proxy, injects `HTTP_PROXY`/`HTTPS_PROXY` +// into the guest, and installs the proxy CA in the guest trust store. +// `none` applies host-side egress enforcement only: no proxy process, no guest +// env injection, and no CA install. Use `none` when the guest is configured to +// reach an external proxy at the network gateway. Credentials require `mitm`. +type CreateInstanceRequestNetworkEgressProxy string + // CreateInstanceRequestNetworkEgressEnforcement Egress enforcement policy applied when mediation is enabled. type CreateInstanceRequestNetworkEgressEnforcement struct { // Mode `all` (default) rejects direct non-mediated TCP egress from the VM, - // while `http_https_only` rejects direct egress only on TCP ports 80 and 443. + // `http_https_only` rejects direct egress only on TCP ports 80 and 443, + // and `all_traffic` rejects direct TCP and UDP egress (including QUIC and + // direct DNS; the guest must resolve names through its proxy). Mode *CreateInstanceRequestNetworkEgressEnforcementMode `json:"mode,omitempty"` } // CreateInstanceRequestNetworkEgressEnforcementMode `all` (default) rejects direct non-mediated TCP egress from the VM, -// while `http_https_only` rejects direct egress only on TCP ports 80 and 443. +// `http_https_only` rejects direct egress only on TCP ports 80 and 443, +// and `all_traffic` rejects direct TCP and UDP egress (including QUIC and +// direct DNS; the guest must resolve names through its proxy). type CreateInstanceRequestNetworkEgressEnforcementMode string // CreateSnapshotRequest defines model for CreateSnapshotRequest. @@ -1552,6 +1579,14 @@ type UpdateInstanceRequest struct { // observed from the host conntrack table. AutoStandby *AutoStandbyPolicy `json:"auto_standby,omitempty"` + // Egress Replaces the instance's egress policy. Only enforcement-only policies + // (`proxy: none`) can be set via update, and only on instances that were + // not created with `proxy: mitm` egress — MITM mediation requires guest + // env and CA configuration that cannot change on a running instance. + // When the instance is running, host-side enforcement is applied or + // removed immediately; otherwise the policy takes effect on next start. + Egress *CreateInstanceRequestNetworkEgress `json:"egress,omitempty"` + // Env Environment variables to update (merged with existing). // Only keys referenced by the instance's existing credential `source.env` bindings // are accepted. Use this to rotate real credential values without restarting the VM. @@ -15930,305 +15965,312 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/+y963IbOZI/+ioIntkYaYakSN1sa6Pjf9SS7dZ2y9axbM/ZbfpQYBVIYlQFVAMoSrTD", - "X/cB9hH3SU4gAdQVRZZkXaxpx25My6wqXBKJRGYi85dfOgGPE84IU7Jz8KUjgzmJMfx5qBQO5h95lMbk", - "HfkjJVLpnxPBEyIUJfBSzFOmxglWc/2vkMhA0ERRzjoHnTOs5uhqTgRBC2gFyTlPoxBNCILvSNjpdsg1", - "jpOIdA46WzFTWyFWuNPtqGWif5JKUDbrfO12BMEhZ9HSdDPFaaQ6B1McSdKtdHuqm0ZYIv1JD77J2ptw", - "HhHMOl+hxT9SKkjYOfi9OI1P2ct88k8SKN35Yar4ucIsnCzPeESDZX2yv1GWXkNvCKeKx1jRAEnzDUrg", - "IzTBkoSIM4QDRRcEUTbhKQvR+6MzFHDGSKAbkyPGJ5KIBQnRVPAYqTlBcy4VvKMEDi6RwpOI9Ees062s", - "B2H6SbieSv+YEzUnwjNYKpFtBU25QGpOJaJMPw1Iv7hgSqSkTtluh4YRGSsaE56qOqF+4Vco4mwG03Lt", - "ojiVCs3xgqDPRHD0R4ojOl1SNmsm0oRMuSDol2VCYsxQEuGASEQVokxxNxtDo5zH9mIfc9EZ44KMQyIV", - "ZVi3P064MDuiPPq38AeOUOFdGBq8j9QcK8fljCt0SUhSnii+wpdlMv6+vd19MRgMPnU7VJHYbCt8TeM0", - "7hzs7+3t7HU7MWXm38Ns9JQpMiNCD9/+goXAy8J0JE9FQMYBDcWqmQQRJUyho5Pjd7ecQGc46MP/bT3v", - "dDvDF9v94f5z+Pdwv1OcVo3w5ZF/Xb31zhVWqazLILObxpZRxgUmqc/6TRpPiEB8ioJUCMJUtESwpUjY", - "gulK0x74liLgbEpnqXBb0LflSuScY4kwM0KjV5EXeWOt9l2ghVjIr9hYkBhTpmlcG8Q79wjpHYrsJtJD", - "CjhTgkeRFgpKkThR0u2irhbjDOEkiWgAoqe0qXbjgex0OyyNIv2wMsJ8tUlEZxReaEUaKguL5L5FiiPC", - "FBHZDm9DmpJYbOo4J7d3NXK52F4KSsoC/3RZleaxlvCCBGa62QlQosiEBDwmSDddXoHtwfZ+b7DbG+y/", - "Hz47GOweDPb+q9PtTLmIseocdEKsSE8veJtlWi2/j3Iq6ReRfTE/qjy061dkcDt2ibBU2a6GTU7Vcow9", - "Y3pPYyIVjhO9sfUYCsRs2tauweo6OMqvJPDwmwjMyLUaWwp55+PjD3KdkEAfMdxtz+zE1u11EZ0ijDIZ", - "oNnVCMaVE3nxTRMRBEs9YK136NPp907KZJros5CE4yTCSrerlRRgg3FMpdSfZj+EVJqN2e04Jh8zrsYi", - "Zcy8yIi64uKy+KZtZUyTTrczx3K8mCVpp7vqHCgzNXRBIpxIaM+uuBgTIbjoGF1zOZ5y4RZJH2I5CVc0", - "VaOQzM4sD4U63U6JAJl8dHNx485W1Ts46AV4SRg13ejVMJn6wItt1YebDW21pDRi2WilbpmR/ViWJUBI", - "8YxxqWggW8lNOI318sY89IjO46w5REPCFJ1SIqyiSpBIGRxrrhGkG0GUoVRW9kGmS4/JQhs/48XuWAVJ", - "nSgVS6G4eIXDPj9iCsdctvzZTlnDpOW5ey2RBaawJ4/JgpqjpawM2aUZh4IuiPCI7+xENaLQvIc29F7X", - "IoRxRjZLlGILGlLcRhyEMKYx9XDP2dEJMo/RyTHamJPrcifbzybPO81NMhx7eOGXNMaspzeEHpZrH94t", - "tv3brlfn53GcjmeCp0m95ZO3p6cfEDxEDFTGYovPt32qXxLQMQ5DQaT0z989LI5tMBgMDvD2wWDQH/hG", - "uSAs5KKRpOaxn6TDQUhWNNmKpLb9GknffDw5PjlER1wkXIARtHbjFMlTnFeRbcqr4uP/n1MahXWun+if", - "iRhnh4iPYCdOjTo5dnqC/Q59PEUbWoaEZJLOZpTNNtvwe8A1OfRR5zvEYajIvqPNROW0lFuft4EgeE13", - "+o1WndW3WmpWchzLptbdK1qixjSKqCQBZ6Es9kGZ2t9tnkxhw5gTqtbVS/0ziomUeEbQBrhUwPwwwlQr", - "NlNMIxJutlNmmybzTz4pHCEl9ga26OFJMNze8cqOGM/IOKQz6xOrHlH6d81iuh2F4G3/ROAwbzcP6FKQ", - "ab2/VyC6oRNBpkQQzePf2F0i+IIwbK2Xv0C/nf9rK3cWbllP4RYQ8yx//Wu380dKUjJOuKRmhDXJZZ9o", - "NgJSI/jCP2Z4tGqtCxwlFRar9we8cQc7Mdfr1tLGui20aoNnaz95r9+pyk4QjZkuUZACjSLypVZqPNoB", - "Z8o+qLgv+QxFlBmLQ6t2Zi1Ar1om5KeIg0i8Izpk5K9vfj3uWwgv80NDa/pZN1PAIz4rUnNOsFATUiJm", - "wxFmG8pH10j+s9L2qZxVWJLxaglyRhkjIfiL7cY2b2o11mtmwC66pGq8IEJ69xwM61eqkH2jsamIB5dT", - "GpHxHMu5dbCFITXOwrPSTDzaWskRj8Eedw2CFgH26/kvh9t7+8h24KGh9VzqF+ozKXytmzfvIoXFBEeR", - "lzea2e3mZ3SdQ/wckDsrm86ejAMdYxpJ17Grae3kVM7NXyC79ajg7NNiQLNXpP/+5Jn0EQgJYyU03t74", - "dcDMMzyLuKbpEqWM/pGWFOw+OpmCg1gfFDQkYRdheAB+B23/zQgjQsup3DNUUILRBunP+l000nphT2vB", - "PbzdGwx6g1GnrMZGuz1j3idYKSL0AP+/33Hv82Hvvwa9F5/yP8f93qe//8XHAG01c6cV2nluuL3fRW6w", - "RXW9OtB1qvytpX9x+D6JY5b6RMuJm6700UldcTBzDXlwSUSf8q2ITgQWyy02o+z6IMKKSFWe+ep3vSvi", - "XEN1Vx4WM1JwKmCJuNzCIpj/vrXAgmKmPpkxolEnoiy93sJxuL876mx2UYxVoLcLOoYRoV7PtdNHb2Oq", - "Mj9ByXHRR2+4QhhN6TUJkd6NB/BSvd+ZwHGMhebxBY5oCOxtnEc9SUOCMAsRZfBMv5ESrXOpVDC0Oxi4", - "J+PCoPQhy6WbyqiDrqiaI90tgmkhLhAW8f6u8ecFJNFdKh7iilO1QIr6XtEb5e9b2V8b7s/+p79v/p+/", - "3CmzAqOt4FI203x8Qz6tWKUgZzYifkVEoI/IiOjZyq4+JamSXViGEE4XpNWYf0cBZlpYGe2PC0RYaCkN", - "75XZOV72cEJ71Ay10+3E+Po3wmZq3jnY3/ESd8P+0fv0N/dTA2FFGhGPFHrHU6UZFx4X713dGLI7tFUr", - "4qibRqCHx5SdmM+G9UvCb1thN5FVK22s7cal1qdE5sNcM5D6Bby2hmOPbfd2QYTQW9HItaPTY7QR0Uti", - "BRoSKUOjdDDYCeAF+JPYXwIex5iF5rdNKzIU16qSU8PMnXrl+pMEcw6aZBTxm9x3gioPFiiOVipaq0jj", - "pfZR1m5dLfuFS9WLMcMzAv4C+yKaCH5J9EDNpQ0lEl2SpVZDl2imG+0tqIQrOMIWaIGNW6g/Yu/nXBLz", - "insk4fKFLgiKeXBp7ubnHFwtRix20dVc64TgtCU4yqVljCkbMS2gezLgCdEC1b4GU0MXhC0uUIwT2OZY", - "ENjjWvYTQXFEP5sYC7gGIiHVMnrECGwMlGC954OAixCuQDkiOJgXqPBXiS6MRnkBzV9Qptn6wmzMSjTB", - "l87bD+9/fvvhzfH47dnLN4cn419f/qf+2XzUOfj9S8fE0mSq5M8ECyLQX77AfL8a+yMkonPQOUzVnAv6", - "2bjTvnY7mgZS8xdOaJ8nhGHaD3jc6Xb+Vvznp6+fnMZs7hkWeht4BvbVq60aZccjko6du1Yi6wJ0l0+a", - "ZFpEvT77sKXVpwRLqeaCp7N5eWNY3e1GWyKk8nJM+XiS+MZE5SU62XqLtGaJIqo3aKZJDgeD05+35Kij", - "/7Hn/rHZR8dm18LwtQziwiq4cq7ZJwvLOTr7gHAU8cA6uaZNN/CuK5+AJ0yJZcKpz8quCKf81bqM6vXy", - "pzcQRVsTyrakXoZecDO6A9/c2tZ7yRZUcBZrexvUpUlEZHmvvHl7/HL88s3HzoE+CMI0sG7js7fv3ncO", - "OjuDwaDjY1DNQWtk4OuzD+ZaGrYNwZGaj4M5CS7XffgLvHsEr8KOU0mUzsaSfvZoIYcZaVBMYi6Me8R+", - "gzbmZSXFbHkE6zrq7Lz+2fDl8DWwpFtPe/+XtWIarlzZvv7Zx2jzZULEgkqfD/WX7JljmnooV2lbGCU2", - "43fYAP2CbRpEPA17hS67nSkVJIDQGf2vP0isjbTF5/KVo+c7v2uzlXGyxurAUUIZWWF2fCfa5RUXlxHH", - "YW94x8qlvSz3hD2ZB+X1zawhxxK1aMIJZuEVDdV8HPIrpofsEcn2CcpezuTytZ4Jjv73v//n42luQw9f", - "TxIrpIfbe98opCtiWTft9Y9lE0kT/zQ+JP5JfDz93//+HzeTx52E0WFupQ/a9X9pWqgGRNk4UePqbrj1", - "zw7+LBhJcessgc+R4721V/w+Gc8XRER4WRC8dkyd4QCkX2VUgkIELLLfaTF6ifTHa8Swbs3pB6+rDpzt", - "gV/Q/nBWfOfOCkFA9I6TLDZ71QZ5Z97O7UgP03l47md9Fthzvw2nZYw23D61f27XWc7PcfKSJmMwqMZ4", - "lt3XrArnPr+kibXS4AuzTaPICPowBbtuwrnqj5iJLtN7EziHXJMAeEEqrNDh2YlEVzSKwLsLh0ZdddA2", - "XyEsEV6XSv+vSFkXTVKlDTmuCLImNXSSwljg5QlBKcMulqViVtkJ1kODgCyXRDASjY3ZJFtSxnyE7EeN", - "xIGpTrG04aVCpUmZXse/np6jjeMlwzEN0K+m1VMephFB5yYyaLNMve6IJQJCjHQnmh2p7ZdPEU9Vj097", - "ShDihhhDY5l/3AZaLF6ffbChOnKzP2LviCYsYaEN0ncahQ3gDjn7q5bIJCw3W+y/QvSmcCzJcCLnvO3m", - "Orev57urvZ+p21kESVpe0u1uYwD3ggqV4kifpSVLwxucYxJQPBalyW8pWrb2XMsD3lU5qqKtM860DNko", - "3lB3j0/NaMKtfWoFL0/Nu+ZcEF/aDXZN+yfMDWSlTzH3QnxDX+emkVrgnfm562Z2CyqdZDSpeCLvhjyH", - "suC1aZU4Yk52c/xJtHGBE9q3fNwPeHzRRRd/K/2g974zHbX+eIUMNUCeMP1Tsf2qv2qtJ+lGqRrFxcHy", - "9utxKBujFNFiiJTATJr40jlOSB/9AkIcKRJr1QTCuiXKwjIR41f/jrhRWt2nI6aHJk2MlyVH5k+UdMYo", - "m21qM04fTDgMjdNxmqpU6PcWVObULLOOc+zVNEMzOmLkMWQ3URZEaUjQhXP+XZSVorprsG7yW19hzYI1", - "JAHLFYx5tRWnSnevJ5wpmjxVJujTTr0ckFtxQK4LhrBjya7Jb7H+55m4qCaxLTwmrJ6cvaAFj3HBdd3k", - "IbaKit97fUmWsOTOU41rvuqik9rvShZE8mhB7LFbdHNPIE2PG8Up93AbX7V1T+vtX01Q8zlu1y2Fpldr", - "8pdNQU96nlQ9N9mcY6x15/I5nBTSkzP9dbXyLwkQH0zLAwTq2EXX2MIELA/ENLNEKKSCBKrWPGWzEYP4", - "rwv7S9+2dqE3udZR7iTpEXKIQGkvLi0qrKxT+6AZPTUeU6VI2C3rBpeEJHL9pLR6be80PBcvglwJ6gSZ", - "SwhoqZ4RNuUiILE1Er7NMfCy0JjXTL9ZE/VwLEPfwphdbhVklpHQxP6Z9QAPfCnlqpp5HFasNhP+U+7y", - "AkfRBdqwL20iQf4JWTR2rRhnObO/PzpzLJCFrHw87WqO1FLgYq5UMtb/I8d6F19UG7Pfuh2eZ4U+H4B9", - "tbu7Y1fVOlXNgCvNlv2n3pCm5qVx6nfjpavmCz1KGyPWRpU/yj/JneyXlIVtG/hVv9vofc0UI2dp3LcD", - "NhGklyYzgSE8/i7dr7e+UgdqNkvwNTn4vhDnPLs3lYrHxVyZjUp4Fi0HcpWJteBRL8QKg6u6pT/dDLee", - "NBAvTVPGFvP6PehnMp5NPDF/9DOk8czoDE+Wqny1NPRm4n5rfIMbi29ZmpJvjAVJwrHiq9MP6BS5d9tE", - "G5tcIcXHiynlq1OzbOxaKXfWHEfWrtVN9JKAWncC6DjB3ESHGyKA0vjxtHit2x+xHhy/B+g46yBrNmsS", - "g26JQ3MztsFFYRAmCQtNlpsIo4+nffQ+G+1fJdIGy4K4bKQ5lmhCCEMpXC3AadgzZ3FxAKmEQ1NVP7e+", - "E5O4tAm319w+62d4AeClydAPIMxxQivzMVnPsFA2XACzoheslddqVdLGOzKjUolKygbaePfqaGdn50XV", - "P7291xsMe8O998PBwUD//3+1z+64+9wsX1uHZdliA0eL0ufow8nxtnWWlvtRn3fxi+fX11i92KdX8sXn", - "eCJm/9zBD5K95Rdlx3nEK9pIJRE9JybB1+2Jcy2EkzbEsd46PPWeok3z4PlV7xpKvNdv3kdami/hwYbb", - "3zxxrCow16ZMFCZXt+SXCdid+S4paHA2Mjmg3hjsYyovfxYEX0K6bf3cjvGMyLE5z/yhLqk08Vfk2no3", - "BOdqKs29eNnrOdx9tvt8Z3/3+WDgycaqMzwP6DjQJ1CrAbw9OkERXhKB4Bu0AReaIZpEfFJm9L2d/efP", - "Bi+G223HYa7w2tEhM7zcV2jDUuTvDmPIPSkNanv72f7Ozs5gf397t9WorL+41aCcb7mkkjzbebY7fL69", - "24oKPoX+pcuOqyrwvqzoQ4PMof/VkwkJ6JQGCPLrkP4AbcRwhJHstqq8Jyc4dLnj/rNDYRrJlRExpjP7", - "pnG0xWmkaBIR8wwWpJUvGmZ+DC150W0Yy3L1b9aSzSlcGwHi5pK9gkq5oSXSnRowgoLyREkUHpgdulbO", - "wWrmA/vUxAd2Di254TdtOvUisiBRkQnM0WWy4gVBGZ+YRSvNyl3+UpakXpZoJOWrVIAuahpFeMJTZa4Z", - "LbhC3glkLIDtMdXiup2d+4qLy7WhxfokzjAk1nqFDsGRPrWuGjjFMbJfu/SigtKXXQeaS1P7XKJ35gvj", - "Icp/TtIyIlUXerKeJIYEkYqDJLUOQ9tMW+3Sr7eAs9TFD5j+ctn5QLFNvakJB7lbC1vMCGCnqLUai+YU", - "E6BxDq+3zlTQH651pLSgOyNXD0F0yLXpabbtSYaT+6H4qmDDzNeQvwSnsKAh6SPYXRD15HJ7KzvtXPEk", - "IWHm/+mPmA31z36S5gZFf2jooOaECsQFndFyx2UH231GLd6EFR033Zodix82RSHJ7MLHt+nxVBmclEuX", - "7kiKuYd2ETrdznmGKmMlUZk07zJknhpF8ijc2hBfn324aexhIviU+rDCIBbCPrWWmYvK+213cN4b/j8m", - "wlbzG6holJn4iZiHFRAY+367k+f12YezpjFlsCyoOLranLKIl1XAdI4i9lLJ3kpaC8axvz5Ysk5y3fuF", - "T5edChyTSTqdEjGOPc61V/o5Mi+Y0CbK0OnPZX1W681treaz0uKA2TzFgUXVaEd9j0OuMo1ugZqf/Mv1", - "jphjuCkXVy+VsO/YdNw+epMB4aDXZx8kyqOUPJ668vI2plKczZeSBjgyLZrUesqKDjZgztYa8ln+oXVF", - "evRkP36S2whoYzFLUtiG5+96J28/bsUhWXRLY4LIojmPiB73ZkFaLFxGbp73URISiyZPh2EM2XYDFWiV", - "7eDWRCrsVw91FFc4GsuI+4I13uuHCB6ijY+vTEKeHkEXJaWl1L8XqFDi733vjtESqanbc+iw6jItbXCv", - "7VhGsjXulcL0Sp36torJgqjrOHXwMn5ZXmh+uR4wyzTS3O+RS9SoOLWtIolMPgeCfA53/4zMp8ZrbV0j", - "kiRYYEWipdEssqMvolMSLIPI7HFSv0ok1yS4QabIS/36V5OJnwoyVnNB5JxH5QvonW4dOFFCEOSCWAAb", - "M6eC411xFGNxCQejU6RRygwFyjG0O+tAYedKJTeY1C/v358Z61oRsTARZcUobFm7Wj0mEV6iCVFXhDA3", - "FSwRRq95hhRUTZuSDSAiQo0TIigv07Cz4+n33ARmopnAAUHmK4djapdEwqnZlpS2Fw8YXhAQKRvWd7hq", - "fe2n0zRqt8a+YQ3Xwv4GN1ng90dnDgwjw/V0ZN6uU/mMiJ7Zcg7gc/XSbsvVsCyuK8YZqXcm+IQATosN", - "fS/mcLnAEsCh0Z+X8qYKwkEWE5ZsP7ALDKm6Zp9/aqXsVbe77yI9xiz04aOaiGeTnDpLY70gesgihbsj", - "GpqoE5Mva9TyYuC2IDikjEhZyewLUhF1up3e1M7qYGsr4gGO5lyqg92d4fOt1fF7KwM3bZzKOKSr7DsX", - "zWLiHVwCnMEphUmXWWILJ0kLD5ih45rzAcRTPVAMMFH12VbQ8Fzk8GBQS1K8xoFy0FLgEitdeeLitk00", - "S5bmAw1mwNx7L14U9+fAewed4+I79t+q8b6emQkm0zxi3A0VOhom/+zVqLjw4QdxoWwK0oS4aLPsPHSx", - "XPZSpdTZ88Hz4ixbwY+DsKlsc7vvPFM1b5fTEkmB3Hb/2gYgLqasc7gtvRptUNNlDU9piVhjqdUE5Qlh", - "N6Ln3u7O9s3o2XYiJy5rsiKXfKAIR6fHRicKOFOYMiJQTBS2tRYKQgZ8SVrKaAM/xCSGPIXpv68WLQ3x", - "C0WUg8Yb8KMaYOG93H43AG29M/GbIYoxo1MtkO2bxZ7lHG/v7R8YOMCQTHf39vv9/k1zv1/myd6tlmLL", - "5LcW0sD7cv5t63APKd5t5vKlc3b4/hctyFIpzKG1JSeUHRT+nf0zfwB/mH9OKPOmhrdCkKTTGnJkOR5M", - "G/zm94MC2L/Te1oBevudwRAWCngUXqAlhWdatzEcd3+IShmPmwHcJllxRfKeVlfeQl2WBtrcGgwyRyRW", - "BRDIYppPC0BI+nn1/bZzd8E7ts+UKRrlWJn1m+1boZ3KlYBwNTC4hLAMAi6KzF8BZwu9XX14cKUj0j37", - "pqiQlTrhP+qq4HpB4DTCNfvJ7/7LBHNbHEwLhuQ5Ih/9OLpN5FS597ez//jj/5Vnz/45/OO3jx//c/H6", - "P47f0P/8GJ29/Sbcg9U4WI8KZnVn+FUQLlQCsWrLSqdYBR43mbbAGihsnxijXwXzPjoCd/7BiPXQb1QR", - "gaMDNOpUEr9GHbRBwFiBr7TGqZuy+aub+uMzc6mnP/7iNNGv1TZCm6gq7IJk+AMynYQ8xpRtjtiI2baQ", - "m4gE1Vz/FaIAJwo8KpQhbYIu0URAoSV76ZJ33kVfcJJ83RwxuLcg10roGSRYqAxZ0fUATGFHZSJB7esk", - "S0U39x4jlh2YGRCUuXnrZ/o3RFxU82j8RFltQlnj5fnAB5kFsfx6ISMqlbECMs7WbJQlGaDng826SbVG", - "zc94aAX7wU6oV2BzTNliLxkGhq6N4B47L+GaCAktm8weQWDEKQ7/PUeuoZwW2RIb173J7JDm5ldFspDT", - "sdnx1n+A1W05IXP1CZ9FLXLBX5qkn/e/nSNFROzSMDcCTc4pDfT8IKiTSplqVqQYHR6dvtzstyghB7TN", - "xr9iHd9nM6ym7Nqr0KYb3tzixDHpopNjSLqyOzTXLCFY+hUXKDICJt/XB+iDJBXjFdLqIFbTrGS0zO9T", - "zQkw6my6FpOqpDhA7zKFFmdDKZWtK1/R5vsSmrXhNCaSu9Z6t1YQSjhl1oo2iNvGKkv90ydusyho70Fx", - "yB56z1fs/Rvv7eIVd6M1X1j7u4ZJvHt1Z+dm6o6rSZLMsfRx97x4RwMvrSgERiuOZ9EckFDqd20trqw7", - "KC+mj77i574qSnu94fD9cPfmzoibItyVkUYKKEQZyF17dLr7QHmrG+bXVI0bQ1WRfmwDU52V9/EUzbFk", - "f1XwsGLrDXeetSoooXttG+RZDO/kUzOkTEo52JIsONEAuFzSKDIxv5LOGI7QC7RxfvL615PffttEPfT2", - "7Wl1KVZ94VufFmB3TlS8PvsA93xYjl2cVHNqEM7T68g1lUrWUV1ahRt+C7ie+bRdxQA3SdNGXjpgNULf", - "LyUUPS9Mz+YdQuu5GNEaGR8CNO8xk2++P8C+lRB734qTZ42Xe4LJaxTuPoi5spw3P98t4N29DGdt/c3i", - "We8yI2+NMNftUE9W2KHUIpiE6OQsr0KQOxld85U52WK2w8GgPxy08QXHOFjR9+nhUfvOB9tGtTjAk4Mg", - "PCDTb/BFW8Y2yjiOrvBSopEzl0YdY58VDLPCtrUmVat78zqQ3+1w+6oKTQMyHyh2LiBB2spVTcpNi7Sf", - "KgpNnEYms7RY7apewFcmBnLNoElnuuyIwQC7FvolK7qLg0CkuT/DVVM0mm+aWL4fMUFkwpk0NVb76Fey", - "lCimcLmRdQ8hTRJl0eXhiG0Il4mQpRwkOJUk1D9AmG/XhZPqoVEFqNn6gxGT8xSKQ2720RFnMo2JsK4e", - "NKHgh95EMjXGHYwXqAGliiUNiRgx/ZoH1O1Lpqgf7A8Gg0FWtLJzsKP/PfBx071eavQtaKHJpQP4JGbh", - "CwGXSaQMpSwkAr3jkiiFETHsUA19ueGFyDeiFbrP2+lR9vNcgfJvzHXghu1QC78VKm5V+a7zcuGu1mbq", - "N1S3bRXa7xRVG9Rvvxrf5GaSoICnUahtn4k+7YxrioTWgyaJymuiwQH5gV0yvUdLU7chhYqjP1Iilujj", - "6WnpOlOQqS351GLiICUa1oEnN1qG7TXegrWjuSWA4EOABlY1lYKGeOcQgcVrC5dcaDi0xfVFbjF6A8Ip", - "M0uj+WTFnCqO55AsxmnqM0T0Iwcp8OHDyXGJOTDeHz4fPH/Rez4Z7vd2w8Gwh4c7+73tPTyY7gTPdhqK", - "LrZPCLl9jofX7PRV0XORnmMXceotlN4Q71s5920M4xVlIb8qHS3eoLJi7zZgbV339XDU1kPwBrFDZXZo", - "qUFKnBbqr5sgzkqNtQZ32v77wXCNO61d2fgG+ftepCwwWF8giTNHdbFgfHGx6kWjby5OYUAuWHwdtYqd", - "tyfa4GDvxcHetxLNBTyvG2OVnR5wcZuiKBxgZCWi2mX1FJwyhYrroG8Y37ENwO50O1mMOPwNB20l/jB7", - "3CrxoWnDdv1iZJX8bsj/OynZAnC1bHCjwgOtBWSpY5NUoSytVKsXRxFPQ1RwaBkYHbjtOSnYBboZuHyx", - "/i6Dh2YCmLX9AACcAP9MmRbEcMulG7HJggfoNbwLj3BsTCY7CANCXrzgweHSXHDr/eW6NgbM6iGfW9sF", - "vtGGDNL/gmlrMli/5+omjOZzgN5w+CazpBivOlDN62DC1F+vOls3bBafy/aGzqwad4BeZapbpvxZZW9D", - "Evvn2AqsHGRhs5Tqale8o7klX7lCFme3Yyja6XYcoSDbs573+SHn+tr+K7KiL/KD4Aj2cp5XlyoaWUxR", - "mAmVigbSBlzrxW3SL2x9BBKOjWHSFMdlkrWs8ZJ95NSXj6doA9DD/o6sd1j/azOL+Sqdddsvdl/sP9t+", - "sd8KIyQf4Hq18whSCeuDW6uDBkk6dlW2G6Z+dPbB+BUCY7HDdYOdeyElOxFcix4987xsd975i/6LIjRK", - "yNNJVLi+sjhKkCHUpsZ6Q+DSHzRa0OmU/fE5uNz+p6Dx8Hpfbk+8XtqsI79L66R4hV3z/5JJz9Q68KNX", - "AEMJ2Qjw8o5ImAE6JwoB//QQDsB0yDIALcs5GBhLcS9j7e7s7Dx/trfdiq/s6AobZwwOLs+hbEdQ2GLw", - "Jtp4d36OtgoMZ9p0adEAx8qsWenfZ8hWxhuUFdL+cLDj45KGgzvnGtv2Im4k+UdrmtlJWaJDImNmttV2", - "uZfaOzuDZ7t7z/fabWPreh2L69USxoX5G/JY1ODiym+ANvn+8AxBEt0UB2W/yXB7Z3dv/9nzG41K3WhU", - "gHhtkGpvMLDnz/b3dne2h+2QinyhDRaDq7Rhy7LLs+k8TOFZDQ8p6qK323Ra+NQpw2DvSBBhGh8GLiy5", - "cvoYRNqxMK/li9DmYLDe/trB1eLbVo6jSol8oxpwgVKW4aD3199r3u6asllMm/NgvRj3xa4zTS4LqWEK", - "n9yCdokgC8pTeQcNcWUSy6YR5+JG3zZZKO+ITCNl7hKpRB9P/wpCRDMXkookZaPJst8K4JFbTu5GG7jE", - "E36ubiJWq9Vos/SrJtxt2KbdVVnnpe3fiO8TalGVsvUxhUc4ClKA+sfZeupZAVIH5M0mSbQ00bdRxDlD", - "wRwzuHEQtrAImyGM5jwK+96ISP1kPPXGIvArFHGDTHpJSGJR8M0g9GdaZ6ELgjYKebvIsFKlWtlebKSK", - "xTkvc+Ne7C+7hKUvoyPLF9X0xIoXQDPNJyUfY8RnEqxABXHF/SpWc4KFCRfGzFR1WMTGeCwDHW3r094z", - "xIr09h2h5ujkU2vRWh0DsjENJXEguJSIRHQGFQQ+nlaS/FYkhmSpfuvjBMuDbcG65nbQc3bBmSZbF3/x", - "HYieiPtvORKBhyGxZkUEnvNGxpilgItfYGRynVBh2KNdlN2cSzXOwFtuOFipxoB5ngqSIzxlqamZA8i9", - "4z0XnWi7DblsOOutvq5xlb+ppgE2y1QvRf3U6mY86GPjOnzNSsScHIKnirdyE4ClHCSbSmiVFrB90Abj", - "qiSWCkDPm20iTvw2qu6nZp7aml6/7Q7O22IfrYY6OsNqfsKm3JMgf4NrSOd6tkGQCRExBdR/FBJGSeiM", - "x+w+0vq2IKsykgSFKbGUMwqpwJbg2GxvSHJnzilG2awi66sdtvEHmzGshkSHfu2LbWKHpD/l7b1IgVYm", - "2k8inCe/tQqdpHLsv7+qNyzILI2wQFV8rxVDlss4ouyyTetyGU94RAOkP6heMk95FPGrsX4kf4K5bLaa", - "nf5gnOduVC6NzeBs5o5ZkEq/+RR+0rPcrOQNgutly3y/BbAKbUKxvAHIr2hELATWB0avC4xexgze3R40", - "pZQ2NFpKJq3Dp91UcluW9e14h2x2mNWQ9dxSmlDiyl1p2RG59qYPYtVXJdDWXTFow0V3OUzmMl0L2Mit", - "PCHtwuWrcYxuNFuSBOXed5/vPdtvCU79Tb5OAxhw157NRbzCo9mwUqdt3GbP956/eLGzu/di+0YOKhfy", - "2rA+TWGvxfWplIquOM32IDxscKNBmaBX/5AaAl/LAyqVfb71gL6u2LpNwQX53my65IyKK+nuWcoe0HY+", - "xhXa0mFJ5cpLVaMNMp0SMCrHhm69fDCVtMdWYwhwggOqlh6HCb4ygXjZKxVwvTbetPJgPSS1bVsgJi25", - "ZDrJM0M2XOfob8a1XuGF560x7mU6aXLjv632apz4uQ+oeEXU4oYmL8NZdxdk87nCshRrpv8OICTUBb/X", - "A4HNG6uBvqpRmnAJaEs5FCIpfACRlfPPflRc/spyFty+JSW5SvFVR2jzFryRDe05kT0mdLA+vaciH+wB", - "eLuvxpNi9YmV5T1KpSryU/fm/bbIYKpDs2Yn2M37K+Ry3OTDKgwZ8KMdgyV53na3xBIN3FQI0/WYIzwi", - "vSzOwcbwIpka/6re8xbZ0pNcElzy6bQMr7XXDMcIOIgQh+56wUqROFFdRK7BTCdhDcvPVS3fk6MO4gKN", - "OsN41Kk4Ab2ZHTG+HtsOygnYg1X4iFndpeogpZvBJOLBpSmsoAQlso8GKCaYSZQy2PwVH+VwsNrX1u0k", - "hbXJ0AiJuSGuiS0Y04TM8YICCK71UM1KcSzkmioJ8TbQzgEKOdi35apSdob6NZN3cZBPGg4dzJa2Yd2g", - "fo8zFxCUvwvm0hRqWbHPRPCuzSjUEvvt29Ouuf+ByA0zsFJ4iJuoGYEWkFkXFUTX/Hd/+NUkImMYdxUh", - "NK7TsZgfB35qQSRR0kIG5uxQYQIU8JSpKnRo3C6EsxzxXj+SUgaxEvb2DHAobO+2FmVIAlvvt+5eKjH6", - "LZi7EnZpKe2Lu9zxsTBsCvDM+T3v76x7vToAibAgxYJwpp1iWJzxuY6l4raCQLarx+Q6ICSsYgz5X2kb", - "ami/9IYa/oZtIn9Wq82+DeFi9dn17y/2HMbaRO1iSCTjrAcpxG5JbbqvgUKxCeVlRiuhHRbSasc+RCff", - "C22Swcj1alq/IdcKIBnDNNLkbWJdK6rsYbSO4rdOumja0FysL216D5UqTLjerWpV2Ei/BylXYX++lxIV", - "teU4J8q9e275prkyaQlMuuQRdAGS7pXyFaXhnS6yJzoaxpsVntud+90gFnWsZb4IwzEZJ4JM6fUKbjEv", - "GEu4nGKd75xS9VqJNmJ8jXafoWCOhayMndHZXEXL8v3lrgfh4JsKtwiiCFM3qPObr6b7sB4sYJez2LpP", - "Gz4v4BH46w+TcLwKm+8oe81dxyZ4CW6bRh/rs53dwWBne3ArcL67KotcaKcpBaHwnb0nKUX1FFvIEr7q", - "tbOuBIW8sYxMUgmC4wMIVE5wQFBEpgBdk9UsXHtY1LpePXirQdnk+Iz/3ULZdXNXGGVY7qwri2voptFx", - "AVBlPITi8/qwV+DbZGImqAHdeHIUdnqD/ffDnYO9/YPh8D7Q/DIiNUXHPvs8vHoWbePpbvR8+eyP4fzZ", - "bDve8Rpe91CBu5zzWS3IbeeQEFEtilYtJihJRBnpySyifH1axwpZYII01u7/mzn2zQxWKgvn5UkWdQas", - "cuKUOOuBwD/s6FfeTlSHf3K8eti3CtGuDsTPYNWhAD+1GwzA3g6/Fco0ZS3PnQ+FF1ufPCvTBtadPb4s", - "T9ja3lVuoLiPn0uCsbTDVp3Y9VPN4x2dcUHVPF59PGSvZcCIEGf2WaqwDDbRRyczBiUQiz9nYQVFM0l/", - "3Ol2os+75T1jf28PO2KBADMGtEtdVANaXLtDhc3VVIBXctNCmMg/bY3rMf807A1fQPBb9Hn3p0HvRR/9", - "oxCE1zXUKpJv6N4u/TpoQ8NiXRGHRz98caMINUfPVRz0K/VVxcgPYgsRaHk8rz/nzgqXkVRa4PxxbY0r", - "MAKNGue3qnb2NBsXtaSQRHjpqxpecMXKin1YZDI0ITPKZBvP7M4gc83uxaNOHx1aYE2wVvPyoqXmobBk", - "gU9oHJOQaqXSGPfNEZ/bLb1tVePhZmjL7iuPetb362cv1ueQrgtQX3dM9r8hYembzN12Ju6q9GbwnDmb", - "FABM4MUuolOEWaXqka3xazMNIXMEYHMOHEpMzrJWBshc8XOekC6acYXyHMOWHrWUNXv+svGTa/Corkgq", - "NgyxfScZ4xl6CV0lvk6OUSJ4mAZ5gk0Eg85TokVawYdZodWvD2G6T4cGZK5NuUDrHRpNHox2Hsim9a54", - "HzXDNi/1cLB+qe/FC9LtpEm4XoaZl9pJsBsBqK5J2fD4ZMpkr2iChcl8aiHR3xUpWDdyjbc40CpRmrgr", - "FM1TdU7yXKjAJYIPS/CYREQfU/VGEI/CPKqUylyKrhepw/3n86ZLTLhzqg/kV0ISbasAQAT0F2O29A6s", - "WtIWbQxcnTJprrR6BoDdUqs8uGdrNbHGpWpfHbji1TYJ5cVizBmS6N2WBrZfri3cfh9+uMdU0t7ay4UK", - "ZpxDNMwwWF3/JmMGMCdZVDmvd30X8D62eG8t4yYcvGrWTNHNfNj7L+NWRuP+wdZPf/+/e5/+5nUvV+xm", - "SUQvJFMIJbokyx6A+CNto/fLKHAAQKyV6ZllFYJjcBoFl8Q4qWJ8XRzv3iATGss3OK5NAWKwYsqyf6+d", - "0N//0hzBVCDjB5CTa1n2m/G576Mqk+LuONqIiZi5utEu8H6zP2IQy39JlhIVKixYlcYx6l9l9olW0cFp", - "iSN0YdTAPmGLCzShUKhGjpi2anEQkERbExZpnprShxykjyA4KrZjKz24RDl75WgiBgj6eFqDEHz74f3P", - "bz+8OR6/PXv55vBk/OvL/4Qgjque6SHsad7b3du3BQ+LlBx6lvgb0Iy/CcXPx24GC8zDX5CUAlUkPQoz", - "lZBS6gIKCi+jDRInaukqKLncls2bYZMdZg16w9nuGFp+8OIuKul8WFk6Z8GjntaoG5CBvQ5MQwtvODY0", - "ZcLcO02O7Zmn6Pq59SbO6Ax7fNneeq93UfHGDWgtaFxt/RvrVfiD44+rsMlGGhhSVWB+K3apVL3m2PlY", - "K1LjvLJmOSIjZTa9hBYCtsq5JDFTW7YylS+lNeQA3rkqoSjfZQ6xqAcfrc+TWanKF2ZWGEnz2pw6jbWi", - "U68g0JkmzdWcCFJYCPggh6O9IclsskeLRGlTPyYhIg+EdJkiUGleUMgeyZwNjgRZQlDdA7sabvgUX2c9", - "gPcey9odF8wjB/4fvv4ZoGHfucKwdOqagGFU7Ak/EGqZi1bRxHFVfTGKXFWft3nfu/GsrFoh/Zr2VoU5", - "8z5KrOnjx39gql5xARZIc1ryveOpgnUTEgG4LFW01FZQozQm4Tgrj920/11FbJOTnBUfz2tTOWsLAxNr", - "Ibe+fpBLnM3HUKe0JgcJUkHV8hzK6JoIYYIFEYep2fCuGq/9Oe8YSj19/Qp+yqknC+E1YUTQAB2encB+", - "jDEDJR19PC2UZzGVemoYaqBevj06sRaug+EDi4UqYD0XzHd4dtLpdhZEGCuvM+hv9wewmRPCcEI7B52d", - "/rA/6Jj6yTDFLagMCX/aBMPMUjoJrR70s3lFfyVwTBQRsnPwuydRDwLZ4GXQd/GsYLEkmAprsiQRpA8a", - "VqH6WwDWdUfpgTmPbfnj1g46qZY2mYIkb+2yfgJ1EnYNTHF7MLAwo8oevJAKYuLPt/5pgxHzflvpc0Ae", - "D8pszaJwOqUl+dduZ3cwvNF4Vg0Ddqyv2w8Mp2rOBf1MYJh7NyTCrTo9YSbDCxmsMBtoU9xnwELFHfb7", - "J71eMo1jLJaOXDmtEi6blGEiEUaMXNkyp//kkz6y1w9QC0fOeRppaYJM+ppzNCgs+rPPCItgThdkxOw5", - "HaeRogkW4EaIkT6fjcFU3hqma7P6GbDAzzxcVqibNbelm+s5p3NO4GpagiRjwCEeNxVXzt3NlDEtJrEk", - "Fq89K+ZZj+bR4nIsA+7LJ3pPGGaqJxMS0CkNELysd6/1aHsbbAXNpwUeLAsRgJjlPDTbm/6cVChl4k/n", - "Ps6eIUvesjrB4BooiNIw17lcmhQWExxFXuymWcQnOBob+lwSj4r6Gt6wRClWfXHKDeMhMRU8kqWac2b+", - "TicpU6n5eyL4lSRCq0C2MpulNQlNLTbDuleA9RlDdTRT91X3uWWGuPXlkiy/9kfsMIxdTV9pPsGR5PrU", - "tJUUbV6A2dKGd/21ZhriSo5SqXhsWSorK5kPk6cqSZW9U5dE2XJy8DqVKEnlnIQjpjj6IsiMSiWWX7e+", - "5D1+BduF4FDzSeEVM6WtLzT82jRqOcZ69mN41WP9ESDAqKNPl1FH/z0TWNsuqZyDE0WC42RWXNKNDE9H", - "64WbVQoHmKGEJwaLCJhqjjXLldqAeHQcRUjBVnLfam0TVrJhPja9OJ405habZNDKNqIMnf5c2EyD3ef+", - "/SRJIIjPwfEf52/fIDiq9BqY13KHlbnUZvoURWEKmjz03h+xlziYI6M3Ad7sqEPDUSezLsJNGGsqbYR2", - "rwcq7k96aD+Zbro0/Knf100Z7fkA/f7FtHKg91ISjxW/JGzU+dpFhQczqubpJHv2yU/QphTN85IgQBtG", - "9m+6wsoAFZUfg+bcwCxE3MraaIkwyiVQ0Y8yoQyLlVWhPaS3FNSmPJ7JIjG+jMB3O+ocjJz3dtTpjjqE", - "LeA36+Iddb76KWCV6GZwU1MY2+naGRPtDwab67ETLH09KnTpRb39vta0r+07Uzys0lVXPMzkHDKzXkFT", - "4tyoWw+g+fyMQ1c084eKt0bFs56LgvIG3xfPAcO+ETEGbkUD0/Zs5DSwldaJYQsomAAWh0M6MQYHdRpc", - "zrxF86NqztfNit2mXRbAECPHf7sPwH/Qb1Zf0fT74qH6xRHgjDvg+ifGjrBYjhG7fov4NVHfA8cNHkqU", - "WlD0x+Tfp8I/r4nV+3KiVaTZFlm4+yY/nhMkm0jbinlZ26rnMKbeOWEKvYRf+/a/zuKBbOmLiM8uDpAh", - "YcRnKKLM3gMWbov0oWhpCR+ZfJPsO5t+4sA0N8z5+b///T8wKMpm//vf/6O1afMXbPctkzgJ4PsXc4KF", - "mhCsLg7Qr4QkPRzRBXGTAXhssiBiiXYGoGYmAh4VSypZ3USO2Ii9IyoVrHBfanAtpW0QTA8G86EsJdLm", - "6+gX6dSCbhkHs8eEd3vZkPJBd3TXk+gMMyhMQJ+KjgcgS9TWlLP2V8fvPTNzLvnPqr7ymsd0vXxR5FoZ", - "7u2ZAd5QwACJffsOHthJo43z85ebfQQ2huEKAFYDjTlvxirP/R8yab1MMhKlLFCAykY2mSS21f7fY/tO", - "OwewbfHP5AG2WJs3cAEblwfkrrsV+GErtHAH++nmXMM+/+yxy9JsdtDefr7FLlwcUytD+O7W2fFenebm", - "SYFkj2ECow0XDQ9uRC7Q2dGJK9S7+WhM/yCnhp6prdWXHR2IM8BeezCz7IizaUQDhXpuLFCYKSaZqVZm", - "kKciDt7ZUSPs5lWFMC6eb1slRL7Gky4D58uPvPs/PSqd3uQYyWGWc177cZKsY51jKgOuvy1wSy/ACRDS", - "qS/ZPi1y0TqHlAmuz46cleqSFc8nx25DPpxrynadsurZ8ABC8bgiEB9REFYq4heAyZ8SN3/IVtEhUqzw", - "XH1frDl4OC3oob1YPjZ/Sm6ssEI2LQVNUHfjAfqaKBPK3bnHhbY9eCZ+ToTb1a6OBMw6m5b51BRWNRMy", - "BepXagQn5pV2pq8teP8nsnyBPDfRWCzJf6goLYzdnFarDNwTW7L8/uxb6OFG5u3d3fNaBvMQGYJNJs5j", - "beruYrlkweaf6qr3QU4zQ+yCEStQVv0GJRFWECgH2TCZnaXHtv0Aet07Gz2EBFbERstkqJlPaMufpVHk", - "rmYWRCj09ujEiIDiYbX1BeKn1hshTiysPLc+vPutR1jAIWAuC/bya3v2yR2bIoazzFQejZ+flPFq8rep", - "O3ibVLFvWH8T14hMKGaf8n/bfhXRicBi+W/br3CUUEb+becwwopItXlvzDJ4qDPkoU2DJ8x82jKgZaKB", - "aGJQmH6dKp291VKbdu//qRRqM+kbqdQZXX9o1W206iK5VirWdinuVbU2fTzS3VHGbD5qwyMXSPknU6kf", - "1h1pOdIhWlNZvp+x1QC5AIc0PKIMpZI8wUhPmnFc8dho6VfPN+TK48Ox7slxFwgJBRwAXspmsjyQl92N", - "48GVW9vvw7vYD+MJnaU8lcUkmRirYE6kzaqKSFkAPzW1Oz+eGxXv75hLBw95dDy4Xv2D7+9J468uqBHe", - "5qpsnc7v3mqr89v3tc5vcr1tkp3FwOo6fMTNhuhHl+3dlo1LSfH1qEzfuHy2CPqgDZXcXEBgQRyM2P/R", - "9sfviuD4008umycdDLb34XfCFp9+cgk97NSxCmFQmgngbA/fHMP95AzS5AHxNs8drI7DlMgA1nMYP/9y", - "BlJ+RdveQnJc+MNCamUhFci12kKya3G/JlIZJ+zBbSTHbz6CW7SVP6eV9B1fPDy4BSfT6ZQGlDCVF46t", - "RdrZutM/bkZulyXI7E1fIUynpIm0NiMzqbVGQ88LPDx4iNZJjmj50NajqyXxNLMdeGLB2a29lmsLzQbb", - "98YPg4c9vR7eUHvKLGYsojrpEq10+4qiA1ponEKBT5RjvUD8LhK2kqdrsY/yWuQyTRIulDSIo2AhmJoE", - "c20h+NBJy4CjPoRRQNWkRHZHDGpO6McGlWHrkiwNnijlLIMOrVbW9WXRlfFcH3Ub3b0S6gerbaWEPvA2", - "tvDjj6eEPproeBB176RU1WEj2xhgcU9ItpN5lqZJP1M223xSscRGWGVzKyBTeVStLajZaBGat2RWF7jp", - "oC2ANNsiwv+CJ259kj7TwaEKFwiIQopnjEtFg2IN5SLQ648TuvUJvZqyXm6e2mL3fo/HKy4u2x5xngpx", - "T+CkK87wO3S26OEBrtvj+1zA4jengWaaBz8Fa2X/3C6FSySqpAvm4lAcXu+i3PPw4K6RyrEZRGmoz0l3", - "XjpNcyp4PLY/GmBiPRkL+wpulMC2+tiySPf+AA63N1whGicR0Uo+CVHPMJtebGsZuLoCVBZqaN5MVupd", - "Vcx8MqiD0tWgshIU+Mot2AbEKdSXyytUIz5bj3aSde6gPTxwJyNm6h4QVyThAmUyGKq0kYgECl3NaTAH", - "6BOo3AalewGVBCfJRYZ1tnmAXsNGLkK+QecbkghtJwWcSR4Rg2iyiOOLgzo078fTU/jIoJ4YEN6Lg6y2", - "fnZ+SP1WEcokK271xgK0bGhOEjyKzIpeaKOyML9NC3KSY9GNmA/whJEr2yCdoosC9slFA/iJk7e/8dmj", - "KWPdZgRRMxfFkQDCGd4kLOw0XWTRyA97Mhx4C/+0hGAxw7hnBJbaYH7jswy9tMTKOEnasq8dJnDxIo5X", - "8DDaKFThlSrkqfq7VCERAj623N3E3GgDB+YfCl9qRrUVpLI6xsB+3utaAyfoJZUWqoVySeZfizjudDt2", - "PAUYwhso92ugbKoN1q8V9coU8Gp+qOU3QaIpC/sCFE3l5DDFtEizRv7OvPCnd99aQj3mFQHoB5X6Rt+T", - "CloYL2V5TTABRf1dYbYnBX4BS17T2kytLN9ucrPsyUI92Xb3ZLVKtN+B9bvu+iwrK5rVPH3oe7T6CJ5y", - "upGszWbKRRUxYd0F23fPSHe3JLWptuGQH7x5cz9fK8ZM0hXlZaE6rgSHIZRcBajvYM65LLD9hMzxgnJh", - "Qfmt+zbjTHBuGDvTxileaFa9sI7gC6vIH1inFcLFR7aPPnxuoxv9X7hH+RevCnZ5JvG7TvkGYFCJMJoI", - "SqYowakkWq9KY4JM0RmL7U5wMHcF5Psj9n5OkC2ZWnA1ZBW2qUQXw/iiiyapQhEWM7CLzEMTsyhIwOOY", - "sNCUQR6xOcELqo06gSKsCAuWPUmgLPaC5DVttJFvrzpN9fWs8G4XuXrNoGpcFKoxX6BEEGAiY1izUunj", - "ERMp+3cDZqqbvXADvUBEKjyJqJxn5UMCHBIWeJFCz79vMXb33uBzouoFix/l8vNWsvQxb0OLXs+sZPx3", - "cVH6xCK+uHDFVluI+RVKr2w2Issxpud5keZ/wS1t5urm+EhXPBmJV+3i7+NuJ2O67+Z+5/EvcLhAYWq6", - "K+xKYPM/661MXlo8ZaWLGeu9ve3VTFYcIyPzjWTe1hf358ktvGnfiSTsNhr2TTDs+aS/B5FrqXormftI", - "bkTrSyp4xR5RBNtBPZ76xEVByj0Vd6cV2GZrZnK7KJ2UwGB9cfZDbFfFtg05uK3Ydr7Z2qV6QZBT1ksi", - "3CTBrRu3UVRb18G/aMBtZXYFkfnoIjK/O3gwsXiSCUIjGhO8jDgO/wwBtytucAIuhEmytbWsnw7IW8Fr", - "WLiK2gDfXDeTEF2XwPLx9HSzSUoItVJGCPWEJUS5Jm4Qe0p9vl0QIWjo6o4enR7bgFkqkUhZH72NKRQD", - "vSQkgSpDlKcSQbZ0X8/PpRvXKyiW8oq7HcKUWCacMrV2FPmr9zOYr7equ/jActLicf64kG59IQ2e/acn", - "zkDKaMXGTmC1ZaqwagwFdKFxlJmyqlovwxOe6ta1DHL1nGdwCk5pRORSKhKbuMBpGsF2A2xnW/rLfmdW", - "uQtRsXrndCE9MCEiplJSzuSITchUK2wJEbpv/TnUmM5DnLwXAgpn8vXMCMnvI3xOD8ZEjGHVRDUAhoBy", - "s52DzhZOki2oSu4P0bLD+4YhvYJ4OCSX8YRHNEARZZcSbUT00pgnaCFRpP/YXBlQN4bv7rqw2e13lqb0", - "CZtyb+0Xw7MZM/85MqTKYs1dTD45sfaaFDeLkz+w0H6xJtfKNUFw1FM0Jhk8AEoVjehnI+p0I1QqGpik", - "nzwvFGp929TQETslSuh3sCAo4FFEAuUcNluJ4MHWKB0MdoKEAsbMDoHBgcBrfhxDj0dnH+A9U4+8O2L6", - "H9Dw+8Mzc7s7xdabUBgoI+qKi0t0svV2TYjxOZDpXzhGz0xwZYKmd8F/XAnePO26cQ/Jhi3Kk1WmEk/+", - "9EGkVoP74Vd4mn4FwL3IZrMxEzgApVjOUxXyK+b3ISx4lMb6H+aPk3XoKQoH84/w6nej7ZrhrO3GTfBJ", - "bEo7p5CY2lSPculhCPZUY1Y14dwUQIkpRQN6T4FD9Wfk7rt33xfp+B1ed1qKurpv383eeuiTz47BIZMV", - "6fFUtrnhNDcTxVd7n64wbfY+/Rzx4FKilCkaldyG2m4DFFf9Y466aa8IQU2A3ExXsR6R64QKgNepOCAR", - "0TOWCCNFREwZjrZgzqYRwA91Xiy84BRSpIOIQpIaDQlKeBQBBNDVnDCkZwOOKtdA4UZX2vodxXeKl5GK", - "owkJeEwcpuqmz3T7B6bqFRdlgNTvRS6+L9Bfz0dPVc9zDSZsc4/fhBF7iq8hVDpM7YWyG9HGa57/aFxB", - "XQRrM+rsDOSo00WjznY86ugVOMLgQsUK7aGYslQR2UfHxr8FSbD7AyRJwFkoHbSr8+DtDGRTSqxhy4b8", - "yn347iHVHstVQMp3thOfeNDvIf09JO2gjeKGs3sy7MKmCxFPlXH3231l3wqJAvfI5oPf1Rb2yA/bvo0k", - "/4fdviUZBausxWVh6Y1kzwA213rdXKLGnMsclxMFOMEBVcsuwlHEg9x7kMrsdqCXDWUiCL7UNlR/xN5l", - "0J42uQIdnX3oOqcZCqm8NC1Yv1gfvV0QIdNJNjgE0sB48GAxSDhiiqMAR0Eaab4l0ykJIC8CEDtlg18t", - "G8p9VtvMO/HCixYijNInh2ru5wlYvZwtZIXjtsxSbwkSRJjGRadSlTig+sLlL7h9J7pRro/haWSvtwLB", - "pUS2qR6J6IxOIntZI/vovVY5cExGLIkwY0SgVJoIJT30XiKIlKlJttENQDljw1FdlMOsJIIr6yaOOBfS", - "eHY1h388RVKRZAWbvTMtn8Kc7wnk2TRue3okg6EyhuZjyb6C9IIYTjEE13ykj+lHCAsyA3psMOinsvHf", - "CzqbEaF3BTZC1lyNmm3tyGk2fSl7pLHCwXn2VrsKB1mrhQjxQvT0SpiMsXtxDAr0TW5gPZ1f0kYkFfvo", - "Zhkdv+qPWvZdzhzwD8I++sZZ/lkKx50XArbb1kXIOfyplSgojLy0VUtJD+shDlpnOdxn1kFrLINHgzB4", - "ysgFuJTK0ARR8P0xwuBhM+4eGgP8afNWCXmgVBapIf1qPbbod8GB9wMq+sgZp7cAFf2ucqAA9fHxclG9", - "G/WxcppKfkBXXuVPjwt6X6lMBhwUoDGaUpmM1LOBBCsNpY/2nXZmkm3xz6TB27vnG+jvjuw/rP4WJkOB", - "WH6Xncm3dlgwJE7U0l0u8mnlAlDSz5C24QOTyGII7g/D4RbX63fHHo5PGy/Xf9SMfrD7+7xk9Mmxp8zY", - "E8N7Ke650sGypU+dHhbBnC5Is9O9vIMtiRJBeglP4HIlNASz9HBnmcKiP/uMbPMW/8r+C1EHsExCFFJB", - "AhUtEWWKg0QwffxVIsG1JQDPuVj6nOnFnftK8PjQzmbNeWj3lHWG5Xe+8bIXYoV7CydtVrjQvuGm3d1t", - "a4GHKEOvf0Yb5FoJg/eLptryQXSakdTUl5PAk5vFAQ8HDZ5N+pmMZ5M2o1yB3PzWImOjIJWKx27tT47R", - "BlSCmBGm10Kr+lPQZBPBFzQ0ZWRzoi54ZKg6bCDoTf2uWqnIyng448IM7lF0mDYH0uwzTcpiwYQudA46", - "E8owDG4tRnJ5T5mEKt0fppDWkO8dxzmdH0eYtfw2nLGjOVEbOY6IinMDt7f545h7ysdcMTDVnWml065d", - "Hct2saotQ0jvA4Q3i2N+WLf1x+8nvJLKJxlZaV3ni8wgbXKbf18sOHi48+Gh3eUfn3A4/mvijO+Cqxwa", - "0C36GOY3HuAIhWRBIp5AiUvzbqfbSUXUOejMlUoOtrYi/d6cS3XwfPB80Pn66ev/HwAA//+bbynCdpUB", - "AA==", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/openapi.yaml b/openapi.yaml index 6168c18c..b464517a 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -123,11 +123,13 @@ components: properties: mode: type: string - enum: [all, http_https_only] + enum: [all, http_https_only, all_traffic] default: all description: | `all` (default) rejects direct non-mediated TCP egress from the VM, - while `http_https_only` rejects direct egress only on TCP ports 80 and 443. + `http_https_only` rejects direct egress only on TCP ports 80 and 443, + and `all_traffic` rejects direct TCP and UDP egress (including QUIC and + direct DNS; the guest must resolve names through its proxy). example: all CreateInstanceRequestNetworkEgress: @@ -145,6 +147,17 @@ components: host-managed credential rewriting is disabled. default: false example: true + proxy: + type: string + enum: [mitm, none] + default: mitm + description: | + Host-side proxy behavior for the mediated egress path. + `mitm` (default) runs the host MITM proxy, injects `HTTP_PROXY`/`HTTPS_PROXY` + into the guest, and installs the proxy CA in the guest trust store. + `none` applies host-side egress enforcement only: no proxy process, no guest + env injection, and no CA install. Use `none` when the guest is configured to + reach an external proxy at the network gateway. Credentials require `mitm`. enforcement: $ref: "#/components/schemas/CreateInstanceRequestNetworkEgressEnforcement" @@ -524,6 +537,16 @@ components: $ref: "#/components/schemas/HealthCheck" restart_policy: $ref: "#/components/schemas/RestartPolicy" + egress: + allOf: + - $ref: "#/components/schemas/CreateInstanceRequestNetworkEgress" + description: | + Replaces the instance's egress policy. Only enforcement-only policies + (`proxy: none`) can be set via update, and only on instances that were + not created with `proxy: mitm` egress — MITM mediation requires guest + env and CA configuration that cannot change on a running instance. + When the instance is running, host-side enforcement is applied or + removed immediately; otherwise the policy takes effect on next start. CreateInstanceRequest: type: object