diff --git a/proxy/README.md b/proxy/README.md index 6af7cadd2..47079ba0a 100644 --- a/proxy/README.md +++ b/proxy/README.md @@ -33,6 +33,43 @@ If any authentication methods are registered for the Host domain, then the Proxy If the user is successfully authenticated, their request will be forwarded through to the Proxy to be proxied to the relevant Peer. Successful authentication does not guarantee a successful forwarding of the request as there may be failures behind the Proxy, such as with Peer connectivity or the underlying resource. +### Target access control + +HTTP targets have an optional `access_action` setting, configured under +**Edit Target → Optional Settings → Access** in the dashboard: + +| Value | Behavior | +| --- | --- | +| `inherit` | Use the service's authentication settings. This is the default. A service without authentication remains public. | +| `bypass` | Forward anonymously without prompting for service authentication. | +| `block` | Return HTTP 403, including for authenticated users. | + +The longest matching Location prefix selects both the target and its access +action. Prefixes are literal and case-sensitive: `/api` also matches `/apiv2`, +while `/api/` only matches requests beginning with `/api/` and does not match +`/api` itself. Wildcards and regular expressions are not supported. A blocked +request or failed authentication never falls through to another target. Disabling +or deleting a target removes its rule, so a less-specific target may then match. + +Service IP, country, and reputation restrictions still apply to bypassed paths. +Bypass is unavailable for private services and Agent Network targets. Bypassed +requests have no NetBird user identity; configured header-auth credentials, +NetBird session cookies, and client-supplied NetBird identity headers are still +removed before forwarding. Custom upstream headers remain target settings, so +consider their effect when making a target public. + +Services using target access overrides reject ambiguous request paths, including +encoded separators and traversal segments, before forwarding. Path rewriting +continues to use the selected Location prefix after access checks. + +Upgrade all proxies before using target access actions. Older proxies ignore +these actions and continue applying the service's authentication settings. +API updates that omit an existing Location's +`access_action` preserve its setting; send `"access_action": "inherit"` explicitly +to restore service authentication. Requests omitting all access actions cannot +remove or rename a controlled Location; use an updated client and explicitly +include the intended actions when changing those targets. + ## TLS Due to the authentication provided, the Proxy uses HTTPS for its endpoint, even if the underlying service is HTTP. diff --git a/proxy/internal/auth/middleware.go b/proxy/internal/auth/middleware.go index 647741139..735fcbac3 100644 --- a/proxy/internal/auth/middleware.go +++ b/proxy/internal/auth/middleware.go @@ -64,6 +64,22 @@ type DomainConfig struct { // groups claim intersects this set. Empty means group membership does not // restrict access. AllowedGroups map[string]struct{} + // TargetResolver pins the target and access action used by this auth snapshot. + TargetResolver *proxy.TargetResolver +} + +// DomainOption configures a domain authentication snapshot. +type DomainOption func(*DomainConfig) error + +// WithTargetResolver attaches an immutable target-routing snapshot to a domain. +func WithTargetResolver(resolver *proxy.TargetResolver) DomainOption { + return func(config *DomainConfig) error { + if resolver == nil { + return errors.New("target resolver is nil") + } + config.TargetResolver = resolver + return nil + } } type validationResult struct { @@ -132,6 +148,12 @@ func (mw *Middleware) Protect(next http.Handler) http.Handler { return } + resolvedRequest, handled := mw.handleTargetAccess(w, r, config, next) + if handled { + return + } + r = resolvedRequest + // Private services bypass operator schemes and gate on tunnel peer. if config.Private { if mw.forwardWithTunnelPeer(w, r, host, config, next) { @@ -171,6 +193,66 @@ func (mw *Middleware) Protect(next http.Handler) http.Handler { }) } +func (mw *Middleware) handleTargetAccess(w http.ResponseWriter, r *http.Request, config DomainConfig, next http.Handler) (*http.Request, bool) { + if config.TargetResolver == nil { + return r, false + } + resolvedRequest, action, err := config.TargetResolver.ResolveRequest(r) + if err != nil { + mw.rejectUnsafePath(w, r, err) + return r, true + } + r = resolvedRequest + switch action { + case proxy.AccessActionInherit: + return r, false + case proxy.AccessActionBypass: + if config.Private { + mw.logger.Error("private domain reached a bypass target; denying") + denyPrivate(w) + return r, true + } + markAccessAction(r, action) + next.ServeHTTP(w, r) + case proxy.AccessActionBlock: + markAccessAction(r, action) + w.Header().Set("Cache-Control", "no-store") + denyForbidden(w, config) + default: + mw.logger.Errorf("resolved unknown access action %q; denying", action) + markAccessAction(r, action) + w.Header().Set("Cache-Control", "no-store") + denyForbidden(w, config) + } + return r, true +} + +func (mw *Middleware) rejectUnsafePath(w http.ResponseWriter, r *http.Request, err error) { + mw.logger.WithFields(log.Fields{ + "host": r.Host, + "path": r.URL.EscapedPath(), + }).Debugf("rejecting request path: %v", err) + if cd := proxy.CapturedDataFromContext(r.Context()); cd != nil { + cd.SetOrigin(proxy.OriginAuth) + cd.SetMetadata("access_action", "invalid_path") + } + w.Header().Set("Cache-Control", "no-store") + http.Error(w, "Bad Request", http.StatusBadRequest) +} + +func markAccessAction(r *http.Request, action proxy.AccessAction) { + if cd := proxy.CapturedDataFromContext(r.Context()); cd != nil { + cd.SetMetadata("access_action", string(action)) + switch action { + case proxy.AccessActionBypass: + cd.SetAuthMethod("path_bypass") + case proxy.AccessActionBlock: + cd.SetAuthMethod("path_block") + cd.SetOrigin(proxy.OriginAuth) + } + } +} + // requestIsPlainHTTP reports whether the request arrived without TLS. // Used to gate cookie-on-plain warnings and the OIDC plain-HTTP block. func requestIsPlainHTTP(r *http.Request) bool { @@ -666,36 +748,10 @@ func wasCredentialSubmitted(r *http.Request, method auth.Method) bool { return false } -// AddDomain registers authentication schemes for the given domain. With schemes a valid session public key is required. -// private=true forces ValidateTunnelPeer enforcement (403 on failure) regardless of the schemes list. -// allowedGroups restricts OIDC sessions to the given group ids; empty means unrestricted. -func (mw *Middleware) AddDomain(domain string, schemes []Scheme, publicKeyB64 string, expiration time.Duration, accountID types.AccountID, serviceID types.ServiceID, ipRestrictions *restrict.Filter, private bool, allowedGroups []string) error { - if len(schemes) == 0 { - mw.domainsMux.Lock() - defer mw.domainsMux.Unlock() - mw.domains[domain] = DomainConfig{ - AccountID: accountID, - ServiceID: serviceID, - IPRestrictions: ipRestrictions, - Private: private, - AllowedGroups: groupSet(allowedGroups), - } - return nil - } - - pubKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyB64) - if err != nil { - return fmt.Errorf("decode session public key for domain %s: %w", domain, err) - } - if len(pubKeyBytes) != ed25519.PublicKeySize { - return fmt.Errorf("invalid session public key size for domain %s: got %d, want %d", domain, len(pubKeyBytes), ed25519.PublicKeySize) - } - - mw.domainsMux.Lock() - defer mw.domainsMux.Unlock() - mw.domains[domain] = DomainConfig{ - Schemes: schemes, - SessionPublicKey: pubKeyBytes, +// NewDomainConfig validates and builds an immutable domain configuration. +func NewDomainConfig(domain string, schemes []Scheme, publicKeyB64 string, expiration time.Duration, accountID types.AccountID, serviceID types.ServiceID, ipRestrictions *restrict.Filter, private bool, allowedGroups []string, opts ...DomainOption) (DomainConfig, error) { + config := DomainConfig{ + Schemes: append([]Scheme(nil), schemes...), SessionExpiration: expiration, AccountID: accountID, ServiceID: serviceID, @@ -703,9 +759,63 @@ func (mw *Middleware) AddDomain(domain string, schemes []Scheme, publicKeyB64 st Private: private, AllowedGroups: groupSet(allowedGroups), } + if len(schemes) > 0 { + pubKeyBytes, err := base64.StdEncoding.DecodeString(publicKeyB64) + if err != nil { + return DomainConfig{}, fmt.Errorf("decode session public key for domain %s: %w", domain, err) + } + if len(pubKeyBytes) != ed25519.PublicKeySize { + return DomainConfig{}, fmt.Errorf("invalid session public key size for domain %s: got %d, want %d", domain, len(pubKeyBytes), ed25519.PublicKeySize) + } + config.SessionPublicKey = append(ed25519.PublicKey(nil), pubKeyBytes...) + } + for _, option := range opts { + if option == nil { + return DomainConfig{}, fmt.Errorf("nil domain option for domain %s", domain) + } + if err := option(&config); err != nil { + return DomainConfig{}, fmt.Errorf("configure domain %s: %w", domain, err) + } + } + if config.Private && config.TargetResolver != nil && config.TargetResolver.HasBypass() { + return DomainConfig{}, fmt.Errorf("authentication bypass is not allowed for private domain %s", domain) + } + return config, nil +} + +// AddDomainConfig atomically publishes a validated domain configuration. +func (mw *Middleware) AddDomainConfig(domain string, config DomainConfig) { + config.Schemes = append([]Scheme(nil), config.Schemes...) + config.SessionPublicKey = append(ed25519.PublicKey(nil), config.SessionPublicKey...) + config.AllowedGroups = cloneGroupSet(config.AllowedGroups) + mw.domainsMux.Lock() + defer mw.domainsMux.Unlock() + mw.domains[domain] = config +} + +// AddDomain registers authentication schemes for the given domain. With schemes a valid session public key is required. +// private=true forces ValidateTunnelPeer enforcement (403 on failure) regardless of the schemes list. +// allowedGroups restricts OIDC sessions to the given group ids; empty means unrestricted. +func (mw *Middleware) AddDomain(domain string, schemes []Scheme, publicKeyB64 string, expiration time.Duration, accountID types.AccountID, serviceID types.ServiceID, ipRestrictions *restrict.Filter, private bool, allowedGroups []string, opts ...DomainOption) error { + config, err := NewDomainConfig(domain, schemes, publicKeyB64, expiration, accountID, serviceID, ipRestrictions, private, allowedGroups, opts...) + if err != nil { + return err + } + mw.AddDomainConfig(domain, config) return nil } +func cloneGroupSet(groups map[string]struct{}) map[string]struct{} { + if groups == nil { + return nil + } + cloned := make(map[string]struct{}, len(groups)) + for group := range groups { + cloned[group] = struct{}{} + } + return cloned +} + // RemoveDomain unregisters authentication for the given domain. func (mw *Middleware) RemoveDomain(domain string) { mw.domainsMux.Lock() diff --git a/proxy/internal/auth/target_access_test.go b/proxy/internal/auth/target_access_test.go new file mode 100644 index 000000000..56f924b60 --- /dev/null +++ b/proxy/internal/auth/target_access_test.go @@ -0,0 +1,278 @@ +package auth + +import ( + "errors" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + proxyauth "github.com/netbirdio/netbird/proxy/auth" + "github.com/netbirdio/netbird/proxy/internal/proxy" + "github.com/netbirdio/netbird/proxy/internal/restrict" +) + +type targetAccessRoundTripFunc func(*http.Request) (*http.Response, error) + +func (f targetAccessRoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { + return f(req) +} + +func targetAccessResolver(t *testing.T, action proxy.AccessAction) *proxy.TargetResolver { + t.Helper() + targetURL, err := url.Parse("http://backend.internal") + require.NoError(t, err) + resolver, err := proxy.NewTargetResolver(proxy.Mapping{ + ID: "service-1", + AccountID: "account-1", + Host: "example.com", + Paths: map[string]*proxy.PathTarget{ + "/selected": { + URL: targetURL, + AccessAction: action, + }, + }, + }) + require.NoError(t, err) + return resolver +} + +func TestProtect_TargetAccessActions(t *testing.T) { + tests := []struct { + name string + action proxy.AccessAction + wantStatus int + wantBackendCalls int + wantSchemeCalls int + wantCacheControl string + }{ + { + name: "bypass forwards without authentication", + action: proxy.AccessActionBypass, + wantStatus: http.StatusNoContent, + wantBackendCalls: 1, + }, + { + name: "block rejects before authentication", + action: proxy.AccessActionBlock, + wantStatus: http.StatusForbidden, + wantSchemeCalls: 0, + wantCacheControl: "no-store", + }, + { + name: "inherit runs configured authentication", + action: proxy.AccessActionInherit, + wantStatus: http.StatusUnauthorized, + wantSchemeCalls: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mw := NewMiddleware(log.StandardLogger(), nil, nil) + kp := generateTestKeyPair(t) + schemeCalls := 0 + scheme := &stubScheme{ + method: proxyauth.MethodPIN, + authFn: func(*http.Request) (string, string, error) { + schemeCalls++ + return "", "pin", nil + }, + } + err := mw.AddDomain( + "example.com", + []Scheme{scheme}, + kp.PublicKey, + time.Hour, + "account-1", + "service-1", + nil, + false, + nil, + WithTargetResolver(targetAccessResolver(t, tt.action)), + ) + require.NoError(t, err) + + backendCalls := 0 + handler := mw.Protect(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + backendCalls++ + w.WriteHeader(http.StatusNoContent) + })) + req := httptest.NewRequest(http.MethodGet, "http://example.com/selected/status", nil) + rec := httptest.NewRecorder() + + handler.ServeHTTP(rec, req) + + assert.Equal(t, tt.wantStatus, rec.Code) + assert.Equal(t, tt.wantBackendCalls, backendCalls, "backend call count") + assert.Equal(t, tt.wantSchemeCalls, schemeCalls, "authentication scheme call count") + assert.Equal(t, tt.wantCacheControl, rec.Header().Get("Cache-Control"), "cache policy") + }) + } +} + +func TestProtect_BypassRecordsActionWithoutIdentity(t *testing.T) { + mw := NewMiddleware(log.StandardLogger(), nil, nil) + require.NoError(t, mw.AddDomain( + "example.com", nil, "", 0, "account-1", "service-1", nil, false, nil, + WithTargetResolver(targetAccessResolver(t, proxy.AccessActionBypass)), + )) + + handler := mw.Protect(newPassthroughHandler()) + req := httptest.NewRequest(http.MethodGet, "http://example.com/selected", nil) + captured := proxy.NewCapturedData("request-1") + req = req.WithContext(proxy.WithCapturedData(req.Context(), captured)) + rec := httptest.NewRecorder() + + handler.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + assert.Empty(t, captured.GetUserID()) + assert.Empty(t, captured.GetUserEmail()) + assert.Empty(t, captured.GetUserGroups()) + assert.Equal(t, "path_bypass", captured.GetAuthMethod()) + assert.Equal(t, "bypass", captured.GetMetadata()["access_action"]) +} + +func TestProtect_TargetResolverPinsMissThroughAuthentication(t *testing.T) { + mw := NewMiddleware(log.StandardLogger(), nil, nil) + resolver := targetAccessResolver(t, proxy.AccessActionBypass) + require.NoError(t, mw.AddDomain( + "example.com", nil, "", 0, "account-1", "service-1", nil, false, nil, + WithTargetResolver(resolver), + )) + + forwarded := false + reverseProxy := proxy.NewReverseProxy(targetAccessRoundTripFunc(func(*http.Request) (*http.Response, error) { + forwarded = true + return nil, errors.New("unexpected forwarding") + }), "auto", nil, nil) + replacementURL, err := url.Parse("http://replacement.internal") + require.NoError(t, err) + reverseProxy.AddMapping(proxy.Mapping{ + ID: "service-1", + AccountID: "account-1", + Host: "example.com", + Paths: map[string]*proxy.PathTarget{ + "/": {URL: replacementURL}, + }, + }) + handler := mw.Protect(reverseProxy) + req := httptest.NewRequest(http.MethodGet, "http://example.com/not-selected", nil) + rec := httptest.NewRecorder() + + handler.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusNotFound, rec.Code, "a miss pinned before auth must reach the proxy unchanged") + assert.False(t, forwarded, "the proxy must not fall back to a newly published mapping after a pinned miss") +} + +func TestProtect_UnsafePathFailsBeforeAuthentication(t *testing.T) { + mw := NewMiddleware(log.StandardLogger(), nil, nil) + kp := generateTestKeyPair(t) + schemeCalls := 0 + scheme := &stubScheme{ + method: proxyauth.MethodPIN, + authFn: func(*http.Request) (string, string, error) { + schemeCalls++ + return "", "pin", nil + }, + } + require.NoError(t, mw.AddDomain( + "example.com", + []Scheme{scheme}, + kp.PublicKey, + time.Hour, + "account-1", + "service-1", + nil, + false, + nil, + WithTargetResolver(targetAccessResolver(t, proxy.AccessActionBlock)), + )) + + backendCalled := false + handler := mw.Protect(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { + backendCalled = true + })) + req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil) + req.URL.Path = "/selected/../other" + rec := httptest.NewRecorder() + + handler.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusBadRequest, rec.Code) + assert.Equal(t, "no-store", rec.Header().Get("Cache-Control")) + assert.Zero(t, schemeCalls, "unsafe paths must be rejected before credentials are evaluated") + assert.False(t, backendCalled) +} + +func TestProtect_IPRestrictionsRunBeforeTargetAccessAction(t *testing.T) { + mw := NewMiddleware(log.StandardLogger(), nil, nil) + filter := restrict.ParseFilter(restrict.FilterConfig{AllowedCIDRs: []string{"10.0.0.0/8"}}) + require.NoError(t, mw.AddDomain( + "example.com", nil, "", 0, "account-1", "service-1", filter, false, nil, + WithTargetResolver(targetAccessResolver(t, proxy.AccessActionBypass)), + )) + + backendCalled := false + handler := mw.Protect(http.HandlerFunc(func(http.ResponseWriter, *http.Request) { + backendCalled = true + })) + req := httptest.NewRequest(http.MethodGet, "http://example.com/selected", nil) + req.RemoteAddr = "192.0.2.10:1234" + rec := httptest.NewRecorder() + + handler.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusForbidden, rec.Code) + assert.False(t, backendCalled, "bypass must not bypass service IP restrictions") +} + +func TestNewDomainConfig_RejectsPrivateBypass(t *testing.T) { + _, err := NewDomainConfig( + "private.example.com", + nil, + "", + 0, + "account-1", + "service-1", + nil, + true, + nil, + WithTargetResolver(targetAccessResolver(t, proxy.AccessActionBypass)), + ) + + require.Error(t, err) + assert.Contains(t, err.Error(), "not allowed for private domain") +} + +func TestAddDomainConfig_PublishesPreparedSnapshot(t *testing.T) { + mw := NewMiddleware(log.StandardLogger(), nil, nil) + config, err := NewDomainConfig( + "example.com", + nil, + "", + 0, + "account-1", + "service-1", + nil, + false, + []string{"group-1"}, + WithTargetResolver(targetAccessResolver(t, proxy.AccessActionBlock)), + ) + require.NoError(t, err) + + mw.AddDomainConfig("example.com", config) + delete(config.AllowedGroups, "group-1") + + published, exists := mw.getDomainConfig("example.com") + require.True(t, exists) + assert.Contains(t, published.AllowedGroups, "group-1", "published mutable fields should be copied") + assert.NotNil(t, published.TargetResolver) +} diff --git a/proxy/internal/proxy/reverseproxy.go b/proxy/internal/proxy/reverseproxy.go index a3987fe5a..bf3ccaeb1 100644 --- a/proxy/internal/proxy/reverseproxy.go +++ b/proxy/internal/proxy/reverseproxy.go @@ -82,7 +82,15 @@ func NewReverseProxy(transport http.RoundTripper, forwardedProto string, trusted } func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { - result, exists := p.findTargetForRequest(r) + resolution, pinned := targetResolutionFromContext(r.Context()) + result, exists := resolution.result, resolution.matched + if !pinned { + result, exists = p.findTargetForRequest(r) + if result.requirePinned { + p.serveProxyUnavailable(w, r) + return + } + } if !exists { p.serveRouteError(w, r, http.StatusNotFound, "Service Not Found", "The requested service could not be found. Please check the URL, try refreshing, or check if the peer is running. If that doesn't work, see our documentation for help.") @@ -119,7 +127,11 @@ func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { rewriteMatchedPath = "" } - chain := p.resolveChain(result) + chain, current := p.resolveChain(result) + if !current || (targetRequiresMiddleware(pt) && (chain == nil || chain.Empty())) { + p.serveProxyUnavailable(w, r) + return + } if chain == nil || chain.Empty() { p.serveDirect(w, r, ctx, result, rewriteMatchedPath) return @@ -127,6 +139,28 @@ func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { p.serveWithChain(w, r, ctx, result, chain, rewriteMatchedPath, capturedData) } +func (p *ReverseProxy) serveProxyUnavailable(w http.ResponseWriter, r *http.Request) { + if cd := CapturedDataFromContext(r.Context()); cd != nil { + cd.SetOrigin(OriginProxyError) + } + w.Header().Set("Cache-Control", "no-store") + web.ServeErrorPage(w, r, http.StatusServiceUnavailable, "Service Unavailable", + "The requested service is being updated. Please try again shortly.", getRequestID(r), + web.ErrorStatus{Proxy: true, Destination: false}) +} + +func targetRequiresMiddleware(target *PathTarget) bool { + if target == nil { + return false + } + for _, spec := range target.Middlewares { + if spec.Enabled { + return true + } + } + return false +} + // serveRouteError marks the request as un-routed on any captured-data // context and renders the proxy error page. func (p *ReverseProxy) serveRouteError(w http.ResponseWriter, r *http.Request, status int, title, message string) { @@ -455,14 +489,15 @@ func applyUpstreamHeaders(r *http.Request, rewrite *middleware.UpstreamRewrite) } } -// resolveChain returns the middleware chain registered for the -// resolved target, or nil when middleware is disabled for the proxy -// or the target. -func (p *ReverseProxy) resolveChain(result targetResult) *middleware.Chain { +// resolveChain returns a chain only from the target's routing revision. +func (p *ReverseProxy) resolveChain(result targetResult) (*middleware.Chain, bool) { if p.middlewareManager == nil { - return nil + return nil, result.middlewareRevision == 0 } - return p.middlewareManager.ChainFor(string(result.serviceID), result.matchedPath) + if result.middlewareRevision == 0 { + return p.middlewareManager.ChainFor(string(result.serviceID), result.matchedPath), true + } + return p.middlewareManager.ChainForRevision(string(result.serviceID), result.matchedPath, result.middlewareRevision) } // buildRequestInput gathers the per-request fields the middleware diff --git a/proxy/internal/proxy/servicemapping.go b/proxy/internal/proxy/servicemapping.go index 64fccc42a..40115fd9d 100644 --- a/proxy/internal/proxy/servicemapping.go +++ b/proxy/internal/proxy/servicemapping.go @@ -23,9 +23,22 @@ const ( PathRewritePreserve ) +// AccessAction controls how authentication handles requests for a target. +type AccessAction string + +const ( + // AccessActionInherit applies the service's configured authentication. + AccessActionInherit AccessAction = "inherit" + // AccessActionBypass skips the service's configured authentication. + AccessActionBypass AccessAction = "bypass" + // AccessActionBlock rejects the request before authentication. + AccessActionBlock AccessAction = "block" +) + // PathTarget holds a backend URL and per-target behavioral options. type PathTarget struct { URL *url.URL + AccessAction AccessAction SkipTLSVerify bool RequestTimeout time.Duration PathRewrite PathRewriteMode @@ -61,18 +74,25 @@ type Mapping struct { // StripAuthHeaders are header names used for header-based auth. // These headers are stripped from requests before forwarding. StripAuthHeaders []string + // MiddlewareRevision binds this routing snapshot to its middleware policies. + MiddlewareRevision middleware.Revision // sortedPaths caches the paths sorted by length (longest first). sortedPaths []string + // requirePinnedResolution prevents a mapping with target access actions + // from being forwarded outside the auth-owned resolver snapshot. + requirePinnedResolution bool } type targetResult struct { - target *PathTarget - matchedPath string - serviceID types.ServiceID - accountID types.AccountID - passHostHeader bool - rewriteRedirects bool - stripAuthHeaders []string + target *PathTarget + matchedPath string + serviceID types.ServiceID + accountID types.AccountID + passHostHeader bool + rewriteRedirects bool + stripAuthHeaders []string + middlewareRevision middleware.Revision + requirePinned bool } func (p *ReverseProxy) findTargetForRequest(req *http.Request) (targetResult, bool) { @@ -90,6 +110,7 @@ func (p *ReverseProxy) findTargetForRequest(req *http.Request) (targetResult, bo p.logger.Debugf("no mapping found for host: %s", host) return targetResult{}, false } + baseResult := targetResult{requirePinned: m.requirePinnedResolution} for _, path := range m.sortedPaths { if strings.HasPrefix(req.URL.Path, path) { @@ -100,18 +121,20 @@ func (p *ReverseProxy) findTargetForRequest(req *http.Request) (targetResult, bo } p.logger.Debugf("matched host: %s, path: %s -> %s", host, path, pt.URL) return targetResult{ - target: pt, - matchedPath: path, - serviceID: m.ID, - accountID: m.AccountID, - passHostHeader: m.PassHostHeader, - rewriteRedirects: m.RewriteRedirects, - stripAuthHeaders: m.StripAuthHeaders, + target: pt, + matchedPath: path, + serviceID: m.ID, + accountID: m.AccountID, + passHostHeader: m.PassHostHeader, + rewriteRedirects: m.RewriteRedirects, + stripAuthHeaders: m.StripAuthHeaders, + middlewareRevision: m.MiddlewareRevision, + requirePinned: m.requirePinnedResolution, }, true } } p.logger.Debugf("no path match for host: %s, path: %s", host, req.URL.Path) - return targetResult{}, false + return baseResult, false } // AddMapping registers a host-to-backend mapping for the reverse proxy. @@ -125,6 +148,7 @@ func (p *ReverseProxy) AddMapping(m Mapping) { return len(paths[i]) > len(paths[j]) }) m.sortedPaths = paths + m.requirePinnedResolution = mappingHasNonDefaultAccessAction(m) p.mappingsMux.Lock() defer p.mappingsMux.Unlock() diff --git a/proxy/internal/proxy/target_middleware_snapshot_test.go b/proxy/internal/proxy/target_middleware_snapshot_test.go new file mode 100644 index 000000000..dbbff55c7 --- /dev/null +++ b/proxy/internal/proxy/target_middleware_snapshot_test.go @@ -0,0 +1,150 @@ +package proxy + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/netbirdio/netbird/proxy/internal/middleware" + guardrail "github.com/netbirdio/netbird/proxy/internal/middleware/builtin/llm_guardrail" +) + +func TestReverseProxy_PinnedTargetFailsClosedAcrossMiddlewareRevisions(t *testing.T) { + restrictiveGuardrail := []middleware.Spec{{ + ID: guardrail.ID, + Slot: middleware.SlotOnRequest, + Enabled: true, + RawConfig: []byte(`{"provider_allowlists":{"provider-1":["allowed-model"]}}`), + }} + unrestrictedGuardrail := []middleware.Spec{{ + ID: guardrail.ID, + Slot: middleware.SlotOnRequest, + Enabled: true, + RawConfig: []byte(`{}`), + }} + + tests := []struct { + name string + oldMiddlewares []middleware.Spec + newMiddlewares []middleware.Spec + oldStatus int + }{ + { + name: "replacement policy cannot authorize old target", + oldMiddlewares: restrictiveGuardrail, + newMiddlewares: unrestrictedGuardrail, + oldStatus: http.StatusForbidden, + }, + { + name: "new policy cannot run on old no-policy target", + newMiddlewares: unrestrictedGuardrail, + oldStatus: http.StatusNoContent, + }, + { + name: "removed policy cannot disappear from old target", + oldMiddlewares: unrestrictedGuardrail, + oldStatus: http.StatusNoContent, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + registry := middleware.NewRegistry() + require.NoError(t, registry.Register(guardrail.Factory{})) + manager := middleware.NewManager(0, nil, nil) + manager.SetResolver(middleware.NewResolver(registry)) + + oldTarget := &PathTarget{ + URL: mustTargetURL(t, "http://old-agent-backend.internal"), + AgentNetwork: true, + Middlewares: tt.oldMiddlewares, + } + oldMapping := resolverMapping(map[string]*PathTarget{"/": oldTarget}) + oldRevision := rebuildMiddlewareSnapshot(t, manager, &oldMapping) + oldResolver, err := NewTargetResolver(oldMapping) + require.NoError(t, err) + + // Auth resolves and pins the old route and middleware revision before + // the replacement snapshot is installed. + req := httptest.NewRequest(http.MethodPost, "http://example.com/v1/chat/completions", nil) + oldPinnedReq, _, err := oldResolver.ResolveRequest(req) + require.NoError(t, err) + + var upstreamHosts []string + rp := NewReverseProxy(resolverRoundTripFunc(func(req *http.Request) (*http.Response, error) { + upstreamHosts = append(upstreamHosts, req.URL.Host) + return &http.Response{ + StatusCode: http.StatusNoContent, + Header: make(http.Header), + Body: http.NoBody, + }, nil + }), "auto", nil, nil, WithMiddlewareManager(manager)) + + before := httptest.NewRecorder() + rp.ServeHTTP(before, oldPinnedReq.Clone(oldPinnedReq.Context())) + require.Equal(t, tt.oldStatus, before.Code, "the original snapshot must work before replacement") + if tt.oldStatus == http.StatusNoContent { + require.Equal(t, []string{"old-agent-backend.internal"}, upstreamHosts) + } else { + require.Empty(t, upstreamHosts) + } + upstreamHosts = nil + + newTarget := &PathTarget{ + URL: mustTargetURL(t, "http://new-agent-backend.internal"), + AgentNetwork: true, + Middlewares: tt.newMiddlewares, + } + newMapping := resolverMapping(map[string]*PathTarget{"/": newTarget}) + newRevision := rebuildMiddlewareSnapshot(t, manager, &newMapping) + require.NotEqual(t, oldRevision, newRevision) + + stale := httptest.NewRecorder() + rp.ServeHTTP(stale, oldPinnedReq) + + assert.Equal(t, http.StatusServiceUnavailable, stale.Code, + "a pinned target must not use a replacement middleware revision") + assert.Equal(t, "no-store", stale.Header().Get("Cache-Control")) + assert.Empty(t, upstreamHosts, + "a mixed target/policy generation must never reach an upstream") + + // A request resolved from the replacement route snapshot is coherent + // with the replacement chain and reaches only the new backend. + newResolver, err := NewTargetResolver(newMapping) + require.NoError(t, err) + freshReq, _, err := newResolver.ResolveRequest( + httptest.NewRequest(http.MethodPost, "http://example.com/v1/chat/completions", nil), + ) + require.NoError(t, err) + + fresh := httptest.NewRecorder() + rp.ServeHTTP(fresh, freshReq) + + assert.Equal(t, http.StatusNoContent, fresh.Code) + assert.Equal(t, []string{"new-agent-backend.internal"}, upstreamHosts) + }) + } +} + +func rebuildMiddlewareSnapshot(t *testing.T, manager *middleware.Manager, mapping *Mapping) middleware.Revision { + t.Helper() + bindings := make([]middleware.PathTargetBinding, 0, len(mapping.Paths)) + for path, target := range mapping.Paths { + if target == nil || len(target.Middlewares) == 0 { + continue + } + bindings = append(bindings, middleware.PathTargetBinding{ + ServiceID: string(mapping.ID), + PathID: path, + Specs: target.Middlewares, + }) + } + revision, err := manager.RebuildSnapshot(string(mapping.ID), bindings) + require.NoError(t, err) + require.NotZero(t, revision) + mapping.MiddlewareRevision = revision + return revision +} diff --git a/proxy/internal/proxy/target_resolver.go b/proxy/internal/proxy/target_resolver.go new file mode 100644 index 000000000..67ce51a24 --- /dev/null +++ b/proxy/internal/proxy/target_resolver.go @@ -0,0 +1,300 @@ +package proxy + +import ( + "context" + "errors" + "fmt" + "maps" + "net/http" + "net/url" + "sort" + "strings" + "unicode/utf8" + + "github.com/netbirdio/netbird/proxy/internal/middleware" + "github.com/netbirdio/netbird/proxy/internal/middleware/bodytap" +) + +// ErrUnsafeRequestPath indicates that a request path cannot be matched safely. +var ErrUnsafeRequestPath = errors.New("unsafe request path") + +// TargetResolver is an immutable routing snapshot shared by auth and forwarding. +type TargetResolver struct { + mapping Mapping + strictPaths bool + hasBypass bool +} + +type targetResolution struct { + result targetResult + matched bool +} + +type targetResolutionContextKey struct{} + +// NewTargetResolver builds an immutable target-routing snapshot. +func NewTargetResolver(mapping Mapping) (*TargetResolver, error) { + snapshot, err := cloneResolverMapping(mapping) + if err != nil { + return nil, err + } + + resolver := &TargetResolver{mapping: snapshot} + for _, target := range snapshot.Paths { + action := effectiveAccessAction(target.AccessAction) + if action != AccessActionInherit { + resolver.strictPaths = true + } + if action == AccessActionBypass { + resolver.hasBypass = true + } + } + return resolver, nil +} + +// ResolveRequest selects and pins a target for both auth and forwarding. +func (r *TargetResolver) ResolveRequest(req *http.Request) (*http.Request, AccessAction, error) { + if r == nil { + return req, AccessActionInherit, nil + } + if req == nil || req.URL == nil { + return req, AccessActionInherit, fmt.Errorf("%w: missing URL", ErrUnsafeRequestPath) + } + if r.strictPaths { + if err := validateAccessPath(req.URL); err != nil { + return req, AccessActionInherit, err + } + } + + result, matched := findTargetInMapping(req.URL.Path, r.mapping) + resolution := targetResolution{result: result, matched: matched} + ctx := context.WithValue(req.Context(), targetResolutionContextKey{}, resolution) + resolvedReq := req.WithContext(ctx) + if !matched { + return resolvedReq, AccessActionInherit, nil + } + + if cd := CapturedDataFromContext(ctx); cd != nil { + cd.SetServiceID(result.serviceID) + cd.SetAccountID(result.accountID) + cd.SetAgentNetwork(result.target.AgentNetwork) + cd.SetSuppressAccessLog(result.target.DisableAccessLog) + } + return resolvedReq, effectiveAccessAction(result.target.AccessAction), nil +} + +// HasBypass reports whether the snapshot contains an authentication bypass. +func (r *TargetResolver) HasBypass() bool { + return r != nil && r.hasBypass +} + +// WithMiddlewareRevision returns a routing snapshot bound to the given policy revision. +func (r *TargetResolver) WithMiddlewareRevision(revision middleware.Revision) *TargetResolver { + bound := *r + bound.mapping.MiddlewareRevision = revision + return &bound +} + +func targetResolutionFromContext(ctx context.Context) (targetResolution, bool) { + resolution, ok := ctx.Value(targetResolutionContextKey{}).(targetResolution) + return resolution, ok +} + +func effectiveAccessAction(action AccessAction) AccessAction { + if action == "" { + return AccessActionInherit + } + return action +} + +func mappingHasNonDefaultAccessAction(mapping Mapping) bool { + for _, target := range mapping.Paths { + if target != nil && effectiveAccessAction(target.AccessAction) != AccessActionInherit { + return true + } + } + return false +} + +func cloneResolverMapping(mapping Mapping) (Mapping, error) { + snapshot := mapping + snapshot.Paths = make(map[string]*PathTarget, len(mapping.Paths)) + snapshot.StripAuthHeaders = append([]string(nil), mapping.StripAuthHeaders...) + snapshot.sortedPaths = make([]string, 0, len(mapping.Paths)) + + for path, target := range mapping.Paths { + if target == nil || target.URL == nil { + return Mapping{}, fmt.Errorf("target for path %q is incomplete", path) + } + action := effectiveAccessAction(target.AccessAction) + switch action { + case AccessActionInherit, AccessActionBypass, AccessActionBlock: + default: + return Mapping{}, fmt.Errorf("unknown access action %q for path %q", target.AccessAction, path) + } + if action == AccessActionBypass && target.AgentNetwork { + return Mapping{}, fmt.Errorf("authentication bypass is not allowed for Agent Network target %q", path) + } + if action != AccessActionInherit { + if err := validateAccessPrefix(path); err != nil { + return Mapping{}, fmt.Errorf("invalid access-controlled path %q: %w", path, err) + } + } + + cloned := clonePathTarget(target) + cloned.AccessAction = action + snapshot.Paths[path] = cloned + snapshot.sortedPaths = append(snapshot.sortedPaths, path) + } + sort.Slice(snapshot.sortedPaths, func(i, j int) bool { + return len(snapshot.sortedPaths[i]) > len(snapshot.sortedPaths[j]) + }) + snapshot.requirePinnedResolution = mappingHasNonDefaultAccessAction(snapshot) + return snapshot, nil +} + +func validateAccessPrefix(prefix string) error { + if strings.ContainsAny(prefix, "?#") { + return fmt.Errorf("%w: query or fragment delimiter", ErrUnsafeRequestPath) + } + return validateAccessPath(&url.URL{Path: prefix}) +} + +func clonePathTarget(target *PathTarget) *PathTarget { + cloned := *target + clonedURL := *target.URL + cloned.URL = &clonedURL + cloned.CustomHeaders = maps.Clone(target.CustomHeaders) + cloned.Middlewares = cloneMiddlewareSpecs(target.Middlewares) + cloned.CaptureConfig = cloneCaptureConfig(target.CaptureConfig) + return &cloned +} + +func cloneMiddlewareSpecs(specs []middleware.Spec) []middleware.Spec { + if specs == nil { + return nil + } + cloned := make([]middleware.Spec, len(specs)) + for i, spec := range specs { + cloned[i] = spec.Clone() + } + return cloned +} + +func cloneCaptureConfig(config *bodytap.Config) *bodytap.Config { + if config == nil { + return nil + } + cloned := *config + cloned.ContentTypes = append([]string(nil), config.ContentTypes...) + return &cloned +} + +func findTargetInMapping(path string, mapping Mapping) (targetResult, bool) { + for _, prefix := range mapping.sortedPaths { + if !strings.HasPrefix(path, prefix) { + continue + } + target := mapping.Paths[prefix] + if target == nil || target.URL == nil { + continue + } + return targetResult{ + target: target, + matchedPath: prefix, + serviceID: mapping.ID, + accountID: mapping.AccountID, + passHostHeader: mapping.PassHostHeader, + rewriteRedirects: mapping.RewriteRedirects, + stripAuthHeaders: mapping.StripAuthHeaders, + middlewareRevision: mapping.MiddlewareRevision, + requirePinned: mapping.requirePinnedResolution, + }, true + } + return targetResult{requirePinned: mapping.requirePinnedResolution}, false +} + +func validateAccessPath(requestURL *url.URL) error { + if requestURL.Opaque != "" || requestURL.Path == "" || requestURL.Path[0] != '/' { + return fmt.Errorf("%w: path must be absolute", ErrUnsafeRequestPath) + } + if !utf8.ValidString(requestURL.Path) { + return fmt.Errorf("%w: path is not valid UTF-8", ErrUnsafeRequestPath) + } + if requestURL.RawPath != "" { + decoded, err := url.PathUnescape(requestURL.RawPath) + if err != nil || decoded != requestURL.Path { + return fmt.Errorf("%w: invalid escaped path", ErrUnsafeRequestPath) + } + } + if err := validateDecodedAccessPath(requestURL.Path); err != nil { + return err + } + return validateEscapedAccessPath(requestURL.EscapedPath()) +} + +func validateDecodedAccessPath(path string) error { + if strings.Contains(path, "//") || strings.ContainsAny(path, "\\;?#") { + return fmt.Errorf("%w: ambiguous path separator", ErrUnsafeRequestPath) + } + for _, segment := range strings.Split(path, "/") { + if segment == "." || segment == ".." { + return fmt.Errorf("%w: dot segment", ErrUnsafeRequestPath) + } + } + for _, char := range path { + if char < 0x20 || char == 0x7f { + return fmt.Errorf("%w: control character", ErrUnsafeRequestPath) + } + } + return nil +} + +func validateEscapedAccessPath(escapedPath string) error { + for i := 0; i < len(escapedPath); i++ { + if escapedPath[i] != '%' { + continue + } + if i+2 >= len(escapedPath) { + return fmt.Errorf("%w: truncated escape", ErrUnsafeRequestPath) + } + value, ok := decodeHexByte(escapedPath[i+1], escapedPath[i+2]) + if !ok { + return fmt.Errorf("%w: invalid escape", ErrUnsafeRequestPath) + } + switch value { + case '/', '\\', '.', '%', 0: + return fmt.Errorf("%w: ambiguous escaped character", ErrUnsafeRequestPath) + } + if value < 0x20 || value == 0x7f { + return fmt.Errorf("%w: escaped control character", ErrUnsafeRequestPath) + } + i += 2 + } + return nil +} + +func decodeHexByte(high, low byte) (byte, bool) { + hi, ok := hexNibble(high) + if !ok { + return 0, false + } + lo, ok := hexNibble(low) + if !ok { + return 0, false + } + return hi<<4 | lo, true +} + +func hexNibble(value byte) (byte, bool) { + switch { + case value >= '0' && value <= '9': + return value - '0', true + case value >= 'a' && value <= 'f': + return value - 'a' + 10, true + case value >= 'A' && value <= 'F': + return value - 'A' + 10, true + default: + return 0, false + } +} diff --git a/proxy/internal/proxy/target_resolver_path_test.go b/proxy/internal/proxy/target_resolver_path_test.go new file mode 100644 index 000000000..d2c5efa35 --- /dev/null +++ b/proxy/internal/proxy/target_resolver_path_test.go @@ -0,0 +1,202 @@ +package proxy + +import ( + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestValidateAccessPath(t *testing.T) { + tests := []struct { + name string + url url.URL + wantErr bool + }{ + {name: "root", url: url.URL{Path: "/"}}, + {name: "nested path", url: url.URL{Path: "/public/status"}}, + {name: "trailing slash", url: url.URL{Path: "/public/"}}, + {name: "dots within segments", url: url.URL{Path: "/.well-known/file..json"}}, + {name: "space", url: url.URL{Path: "/file name"}}, + {name: "unicode", url: url.URL{Path: "/unicode/é"}}, + {name: "encoded space", url: url.URL{Path: "/file name", RawPath: "/file%20name"}}, + {name: "encoded letter", url: url.URL{Path: "/public", RawPath: "/%70ublic"}}, + {name: "encoded unicode", url: url.URL{Path: "/unicode/é", RawPath: "/unicode/%C3%a9"}}, + { + name: "query and fragment are separate from the path", + url: url.URL{Path: "/public", RawQuery: "next=/../private", Fragment: "section"}, + }, + {name: "empty path", url: url.URL{}, wantErr: true}, + {name: "relative path", url: url.URL{Path: "public"}, wantErr: true}, + {name: "opaque URL", url: url.URL{Path: "/public", Opaque: "//example.com/private"}, wantErr: true}, + {name: "invalid UTF-8", url: url.URL{Path: "/public/\xff"}, wantErr: true}, + { + name: "encoded invalid UTF-8", + url: url.URL{Path: "/public/\xff", RawPath: "/public/%ff"}, + wantErr: true, + }, + {name: "bare escape", url: url.URL{Path: "/public", RawPath: "/public%"}, wantErr: true}, + {name: "short escape", url: url.URL{Path: "/public", RawPath: "/public%2"}, wantErr: true}, + {name: "invalid escape", url: url.URL{Path: "/public", RawPath: "/public%GG"}, wantErr: true}, + {name: "mismatched raw path", url: url.URL{Path: "/public", RawPath: "/private"}, wantErr: true}, + {name: "duplicate separator", url: url.URL{Path: "/public//private"}, wantErr: true}, + {name: "backslash", url: url.URL{Path: "/public\\private"}, wantErr: true}, + {name: "path parameter", url: url.URL{Path: "/public;private"}, wantErr: true}, + {name: "query delimiter in path", url: url.URL{Path: "/public?private"}, wantErr: true}, + {name: "fragment delimiter in path", url: url.URL{Path: "/public#private"}, wantErr: true}, + {name: "dot segment", url: url.URL{Path: "/public/./private"}, wantErr: true}, + {name: "parent segment", url: url.URL{Path: "/public/../private"}, wantErr: true}, + {name: "NUL", url: url.URL{Path: "/public/\x00private"}, wantErr: true}, + {name: "control character", url: url.URL{Path: "/public/\x1fprivate"}, wantErr: true}, + {name: "DEL", url: url.URL{Path: "/public/\x7fprivate"}, wantErr: true}, + { + name: "encoded separator", + url: url.URL{Path: "/public/private", RawPath: "/public%2Fprivate"}, + wantErr: true, + }, + { + // The decoded dot is within a segment, so only escaped validation rejects it. + name: "encoded dot within segment", + url: url.URL{Path: "/file.txt", RawPath: "/file%2Etxt"}, + wantErr: true, + }, + { + name: "double encoding", + url: url.URL{Path: "/public/%2e%2e/private", RawPath: "/public/%252e%252e/private"}, + wantErr: true, + }, + {name: "percent without raw path", url: url.URL{Path: "/public/%2fprivate"}, wantErr: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateAccessPath(&tt.url) + if tt.wantErr { + assert.ErrorIs(t, err, ErrUnsafeRequestPath, "unsafe URL path must be rejected") + return + } + assert.NoError(t, err, "ordinary URL path must remain supported") + }) + } +} + +func TestValidateAccessPath_RejectsEncodedTraversal(t *testing.T) { + tests := []struct { + name string + rawPath string + }{ + {name: "encoded parent segment", rawPath: "/public/%2E%2E/private"}, + { + name: "UTF-16LE encoded traversal", + rawPath: "%2f%00%70%00%75%00%62%00%6c%00%69%00%63%00%2f%00" + + "%2e%00%2e%00%2f%00%70%00%72%00%69%00%76%00%61%00%74%00%65%00", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Preserve percent-decoded bytes, including NULs, without transcoding UTF-16. + path, err := url.PathUnescape(tt.rawPath) + require.NoError(t, err) + + err = validateAccessPath(&url.URL{Path: path, RawPath: tt.rawPath}) + assert.ErrorIs(t, err, ErrUnsafeRequestPath, "encoded traversal must be rejected") + }) + } +} + +func TestValidateDecodedAccessPath(t *testing.T) { + tests := []struct { + name string + path string + wantErr bool + }{ + {name: "root", path: "/"}, + {name: "nested path", path: "/public/status"}, + {name: "trailing slash", path: "/public/"}, + {name: "dots within segments", path: "/.well-known/file..json/..."}, + {name: "space boundary", path: "/file\x20name"}, + {name: "last printable ASCII", path: "/file\x7e"}, + {name: "unicode", path: "/unicode/é"}, + {name: "duplicate leading separator", path: "//public", wantErr: true}, + {name: "duplicate nested separator", path: "/public//private", wantErr: true}, + {name: "backslash", path: "/public\\private", wantErr: true}, + {name: "path parameter", path: "/public;private", wantErr: true}, + {name: "query delimiter", path: "/public?private", wantErr: true}, + {name: "fragment delimiter", path: "/public#private", wantErr: true}, + {name: "dot segment", path: "/public/./private", wantErr: true}, + {name: "parent segment", path: "/public/../private", wantErr: true}, + {name: "terminal dot segment", path: "/public/.", wantErr: true}, + {name: "terminal parent segment", path: "/public/..", wantErr: true}, + {name: "NUL", path: "/public/\x00private", wantErr: true}, + {name: "tab", path: "/public/\tprivate", wantErr: true}, + {name: "newline", path: "/public/\nprivate", wantErr: true}, + {name: "carriage return", path: "/public/\rprivate", wantErr: true}, + {name: "last C0 control", path: "/public/\x1fprivate", wantErr: true}, + {name: "DEL", path: "/public/\x7fprivate", wantErr: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateDecodedAccessPath(tt.path) + if tt.wantErr { + assert.ErrorIs(t, err, ErrUnsafeRequestPath, "unsafe decoded path %q must be rejected", tt.path) + return + } + assert.NoError(t, err, "ordinary decoded path %q must remain supported", tt.path) + }) + } +} + +func TestValidateEscapedAccessPath(t *testing.T) { + // Malformed escapes and decoded controls normally fail earlier in validateAccessPath. + // Exercise these guards directly so changes to that ordering cannot silently weaken them. + tests := []struct { + name string + path string + wantErr bool + }{ + {name: "root", path: "/"}, + {name: "unescaped path", path: "/.well-known/file.txt"}, + {name: "encoded space boundary", path: "/file%20name"}, + {name: "encoded last printable ASCII lowercase", path: "/file%7e"}, + {name: "encoded last printable ASCII uppercase", path: "/file%7E"}, + {name: "adjacent escapes", path: "/%41%42%39"}, + {name: "mixed case unicode escapes", path: "/unicode/%C3%a9"}, + {name: "bare escape", path: "/public/%", wantErr: true}, + {name: "short escape", path: "/public/%2", wantErr: true}, + {name: "invalid high nibble", path: "/public/%G0", wantErr: true}, + {name: "invalid low nibble", path: "/public/%0g", wantErr: true}, + {name: "encoded slash lowercase", path: "/public%2fprivate", wantErr: true}, + {name: "encoded slash uppercase", path: "/public%2Fprivate", wantErr: true}, + {name: "encoded backslash lowercase", path: "/public%5cprivate", wantErr: true}, + {name: "encoded backslash uppercase", path: "/public%5Cprivate", wantErr: true}, + {name: "encoded dot lowercase", path: "/file%2etxt", wantErr: true}, + {name: "encoded dot uppercase", path: "/file%2Etxt", wantErr: true}, + {name: "encoded percent", path: "/public/%25", wantErr: true}, + {name: "double encoding", path: "/public/%252e%252e/private", wantErr: true}, + {name: "encoded NUL", path: "/public/%00private", wantErr: true}, + {name: "encoded first non-NUL control", path: "/public/%01private", wantErr: true}, + {name: "encoded tab", path: "/public/%09private", wantErr: true}, + {name: "encoded newline", path: "/public/%0aprivate", wantErr: true}, + {name: "encoded carriage return", path: "/public/%0Dprivate", wantErr: true}, + {name: "encoded last C0 control lowercase", path: "/public/%1fprivate", wantErr: true}, + {name: "encoded last C0 control uppercase", path: "/public/%1Fprivate", wantErr: true}, + {name: "encoded DEL lowercase", path: "/public/%7fprivate", wantErr: true}, + {name: "encoded DEL uppercase", path: "/public/%7Fprivate", wantErr: true}, + {name: "unsafe escape after valid escape", path: "/%41%2fprivate", wantErr: true}, + {name: "malformed escape after valid escape", path: "/%41%", wantErr: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateEscapedAccessPath(tt.path) + if tt.wantErr { + assert.ErrorIs(t, err, ErrUnsafeRequestPath, "unsafe escaped path %q must be rejected", tt.path) + return + } + assert.NoError(t, err, "ordinary escaped path %q must remain supported", tt.path) + }) + } +} diff --git a/proxy/internal/proxy/target_resolver_test.go b/proxy/internal/proxy/target_resolver_test.go new file mode 100644 index 000000000..bf48d812c --- /dev/null +++ b/proxy/internal/proxy/target_resolver_test.go @@ -0,0 +1,336 @@ +package proxy + +import ( + "errors" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/netbirdio/netbird/proxy/internal/middleware" + guardrail "github.com/netbirdio/netbird/proxy/internal/middleware/builtin/llm_guardrail" +) + +type resolverRoundTripFunc func(*http.Request) (*http.Response, error) + +func (f resolverRoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { + return f(req) +} + +func resolverMapping(targets map[string]*PathTarget) Mapping { + return Mapping{ + ID: "service-1", + AccountID: "account-1", + Host: "example.com", + Paths: targets, + } +} + +func mustTargetURL(t *testing.T, rawURL string) *url.URL { + t.Helper() + targetURL, err := url.Parse(rawURL) + require.NoError(t, err) + return targetURL +} + +func TestTargetResolver_LongestPrefixPinsTargetAndAction(t *testing.T) { + mapping := resolverMapping(map[string]*PathTarget{ + "/": { + URL: mustTargetURL(t, "http://root.internal"), + AccessAction: AccessActionInherit, + }, + "/public": { + URL: mustTargetURL(t, "http://public.internal"), + AccessAction: AccessActionBypass, + CustomHeaders: map[string]string{"X-Target": "original"}, + }, + }) + resolver, err := NewTargetResolver(mapping) + require.NoError(t, err) + + req := httptest.NewRequest(http.MethodGet, "http://example.com/public/status", nil) + resolvedReq, action, err := resolver.ResolveRequest(req) + require.NoError(t, err) + assert.Equal(t, AccessActionBypass, action, "the longest matching target action should be selected") + + // Mutating both the source mapping and the live proxy mapping must not + // change the target already selected by the auth snapshot. + mapping.Paths["/public"].URL.Host = "mutated.internal" + mapping.Paths["/public"].CustomHeaders["X-Target"] = "mutated" + liveMapping := resolverMapping(map[string]*PathTarget{ + "/": {URL: mustTargetURL(t, "http://replacement.internal")}, + }) + + var gotHost, gotHeader string + rp := NewReverseProxy(resolverRoundTripFunc(func(req *http.Request) (*http.Response, error) { + gotHost = req.URL.Host + gotHeader = req.Header.Get("X-Target") + return &http.Response{ + StatusCode: http.StatusNoContent, + Header: make(http.Header), + Body: http.NoBody, + }, nil + }), "auto", nil, nil) + rp.AddMapping(liveMapping) + rec := httptest.NewRecorder() + rp.ServeHTTP(rec, resolvedReq) + + assert.Equal(t, http.StatusNoContent, rec.Code) + assert.Equal(t, "public.internal", gotHost, "forwarding must reuse the target pinned before auth") + assert.Equal(t, "original", gotHeader, "resolver-owned target options must be immutable") +} + +func TestTargetResolver_PinnedMissDoesNotFallBackToNewMapping(t *testing.T) { + resolver, err := NewTargetResolver(resolverMapping(map[string]*PathTarget{ + "/api": {URL: mustTargetURL(t, "http://api.internal")}, + })) + require.NoError(t, err) + + req := httptest.NewRequest(http.MethodGet, "http://example.com/other", nil) + resolvedReq, action, err := resolver.ResolveRequest(req) + require.NoError(t, err) + assert.Equal(t, AccessActionInherit, action) + + called := false + rp := NewReverseProxy(resolverRoundTripFunc(func(*http.Request) (*http.Response, error) { + called = true + return nil, errors.New("unexpected forwarding") + }), "auto", nil, nil) + rp.AddMapping(resolverMapping(map[string]*PathTarget{ + "/": {URL: mustTargetURL(t, "http://new.internal")}, + })) + rec := httptest.NewRecorder() + rp.ServeHTTP(rec, resolvedReq) + + assert.Equal(t, http.StatusNotFound, rec.Code) + assert.False(t, called, "a miss pinned before auth must not be resolved again after an update") +} + +func TestReverseProxy_UnpinnedNonDefaultMappingFailsClosed(t *testing.T) { + for _, requestPath := range []string{"/public", "/missing"} { + t.Run(requestPath, func(t *testing.T) { + called := false + rp := NewReverseProxy(resolverRoundTripFunc(func(*http.Request) (*http.Response, error) { + called = true + return nil, errors.New("unexpected forwarding") + }), "auto", nil, nil) + rp.AddMapping(resolverMapping(map[string]*PathTarget{ + "/public": { + URL: mustTargetURL(t, "http://public.internal"), + AccessAction: AccessActionBypass, + }, + })) + + req := httptest.NewRequest(http.MethodGet, "http://example.com"+requestPath, nil) + rec := httptest.NewRecorder() + rp.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusServiceUnavailable, rec.Code, + "both target hits and misses must require an auth-owned resolution") + assert.Equal(t, "no-store", rec.Header().Get("Cache-Control"), + "an unavailable routing snapshot must not be cached") + assert.False(t, called, "a target access action must never forward without an auth-owned resolution") + }) + } +} + +func TestReverseProxy_ConfiguredMiddlewareMissingChainFailsClosed(t *testing.T) { + called := false + manager := middleware.NewManager(0, nil, nil) + rp := NewReverseProxy(resolverRoundTripFunc(func(*http.Request) (*http.Response, error) { + called = true + return nil, errors.New("unexpected forwarding") + }), "auto", nil, nil, WithMiddlewareManager(manager)) + rp.AddMapping(resolverMapping(map[string]*PathTarget{ + "/": { + URL: mustTargetURL(t, "http://protected.internal"), + Middlewares: []middleware.Spec{{ + ID: "required-policy", + Slot: middleware.SlotOnRequest, + Enabled: true, + }}, + }, + })) + + req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil) + rec := httptest.NewRecorder() + rp.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusServiceUnavailable, rec.Code) + assert.Equal(t, "no-store", rec.Header().Get("Cache-Control")) + assert.False(t, called, "a missing configured policy chain must never become pass-through") +} + +func TestReverseProxy_PinnedTargetFailsClosedAfterMiddlewareRemoval(t *testing.T) { + target := &PathTarget{ + URL: mustTargetURL(t, "http://protected.internal"), + AccessAction: AccessActionInherit, + Middlewares: []middleware.Spec{{ + ID: guardrail.ID, + Slot: middleware.SlotOnRequest, + Enabled: true, + }}, + } + mapping := resolverMapping(map[string]*PathTarget{"/": target}) + resolver, err := NewTargetResolver(mapping) + require.NoError(t, err) + req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil) + resolvedReq, _, err := resolver.ResolveRequest(req) + require.NoError(t, err) + + upstreamCalls := 0 + registry := middleware.NewRegistry() + require.NoError(t, registry.Register(guardrail.Factory{})) + manager := middleware.NewManager(0, nil, nil) + manager.SetResolver(middleware.NewResolver(registry)) + require.NoError(t, manager.Rebuild(string(mapping.ID), []middleware.PathTargetBinding{{ + ServiceID: string(mapping.ID), + PathID: "/", + Specs: target.Middlewares, + }})) + require.NotNil(t, manager.ChainFor(string(mapping.ID), "/"), "the policy chain must exist before invalidation") + rp := NewReverseProxy(resolverRoundTripFunc(func(*http.Request) (*http.Response, error) { + upstreamCalls++ + return &http.Response{ + StatusCode: http.StatusNoContent, + Header: make(http.Header), + Body: http.NoBody, + }, nil + }), "auto", nil, nil, WithMiddlewareManager(manager)) + before := httptest.NewRecorder() + rp.ServeHTTP(before, resolvedReq.Clone(resolvedReq.Context())) + require.Equal(t, http.StatusNoContent, before.Code, "the installed policy chain must allow forwarding") + require.Equal(t, 1, upstreamCalls, "the upstream must receive the request before invalidation") + + manager.Invalidate(string(mapping.ID)) + require.Nil(t, manager.ChainFor(string(mapping.ID), "/"), "invalidation must remove the installed chain") + rec := httptest.NewRecorder() + rp.ServeHTTP(rec, resolvedReq) + + assert.Equal(t, http.StatusServiceUnavailable, rec.Code, "a removed policy chain must fail closed") + assert.Equal(t, "no-store", rec.Header().Get("Cache-Control"), "a removed policy chain must not be cached") + assert.Equal(t, 1, upstreamCalls, "a stale pinned target must not bypass a removed policy chain") +} + +func TestTargetResolver_RejectsUnsafePathsWhenActionsAreConfigured(t *testing.T) { + resolver, err := NewTargetResolver(resolverMapping(map[string]*PathTarget{ + "/public": { + URL: mustTargetURL(t, "http://public.internal"), + AccessAction: AccessActionBypass, + }, + "/": {URL: mustTargetURL(t, "http://root.internal")}, + })) + require.NoError(t, err) + + tests := []struct { + name string + path string + rawPath string + }{ + {name: "dot segment", path: "/public/../private"}, + {name: "duplicate separator", path: "/public//private"}, + {name: "backslash", path: `/public\private`}, + {name: "path parameter", path: "/public;ignored/private"}, + {name: "encoded query delimiter", path: "/public?ignored", rawPath: "/public%3fignored"}, + {name: "encoded fragment delimiter", path: "/public#ignored", rawPath: "/public%23ignored"}, + {name: "encoded slash", path: "/public/private", rawPath: "/public%2fprivate"}, + {name: "encoded dot", path: "/public/../private", rawPath: "/public/%2e%2e/private"}, + {name: "double encoding", path: "/public/%2e%2e/private", rawPath: "/public/%252e%252e/private"}, + {name: "escaped NUL", path: "/public/\x00private", rawPath: "/public/%00private"}, + {name: "mismatched raw path", path: "/public/safe", rawPath: "/different"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil) + req.URL.Path = tt.path + req.URL.RawPath = tt.rawPath + + _, _, err := resolver.ResolveRequest(req) + + require.ErrorIs(t, err, ErrUnsafeRequestPath) + }) + } + + req := httptest.NewRequest(http.MethodGet, "http://example.com/public/file%20name", nil) + _, action, err := resolver.ResolveRequest(req) + require.NoError(t, err) + assert.Equal(t, AccessActionBypass, action, "ordinary percent-encoded path data should remain supported") +} + +func TestTargetResolver_InheritOnlyPreservesLegacyPathHandling(t *testing.T) { + resolver, err := NewTargetResolver(resolverMapping(map[string]*PathTarget{ + "/": {URL: mustTargetURL(t, "http://root.internal")}, + })) + require.NoError(t, err) + + req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil) + req.URL.Path = "/legacy/../path" + resolvedReq, action, err := resolver.ResolveRequest(req) + + require.NoError(t, err) + assert.Equal(t, AccessActionInherit, action) + _, pinned := targetResolutionFromContext(resolvedReq.Context()) + assert.True(t, pinned, "inherit-only mappings should still pin their target snapshot") +} + +func TestNewTargetResolver_RejectsInvalidAccessActions(t *testing.T) { + tests := []struct { + name string + target *PathTarget + }{ + { + name: "unknown action", + target: &PathTarget{ + URL: mustTargetURL(t, "http://target.internal"), + AccessAction: AccessAction("unknown"), + }, + }, + { + name: "Agent Network bypass", + target: &PathTarget{ + URL: mustTargetURL(t, "http://target.internal"), + AccessAction: AccessActionBypass, + AgentNetwork: true, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := NewTargetResolver(resolverMapping(map[string]*PathTarget{"/": tt.target})) + require.Error(t, err) + }) + } +} + +func TestNewTargetResolver_RejectsNoncanonicalAccessPrefixes(t *testing.T) { + prefixes := []string{ + "public", + "/public/../private", + "/public//nested", + `/public\nested`, + "/public;parameter", + "/public%2fprivate", + "/public?query", + "/public#fragment", + "/public\x00nested", + } + + for _, prefix := range prefixes { + t.Run(prefix, func(t *testing.T) { + _, err := NewTargetResolver(resolverMapping(map[string]*PathTarget{ + prefix: { + URL: mustTargetURL(t, "http://target.internal"), + AccessAction: AccessActionBlock, + }, + "/": {URL: mustTargetURL(t, "http://root.internal")}, + })) + + require.ErrorIs(t, err, ErrUnsafeRequestPath) + }) + } +} diff --git a/proxy/server.go b/proxy/server.go index 762ead9b8..083449962 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -1644,6 +1644,22 @@ func (s *Server) addMapping(ctx context.Context, mapping *proto.ProxyMapping) er // state and re-applies them from the new mapping. func (s *Server) modifyMapping(ctx context.Context, mapping *proto.ProxyMapping) error { if old := s.loadMapping(types.ServiceID(mapping.GetId())); old != nil { + if !types.ServiceMode(old.GetMode()).IsL4() && + !types.ServiceMode(mapping.GetMode()).IsL4() && + old.GetDomain() == mapping.GetDomain() && + len(old.GetPath()) > 0 && len(mapping.GetPath()) > 0 { + if err := s.updateMapping(ctx, mapping); err != nil { + // Fail the whole service closed until a valid update arrives: the old + // policy could retain access this replacement intended to revoke. + // Keep the peer, but withdraw routes and the cached mapping for recovery. + s.cleanupMappingRoutes(old) + s.deleteMapping(types.ServiceID(mapping.GetId())) + return err + } + s.storeMapping(mapping) + s.hijackTracker.CloseByHost(mapping.GetDomain()) + return nil + } s.cleanupMappingRoutes(old) if mode := types.ServiceMode(old.GetMode()); mode.IsL4() { s.meter.L4ServiceRemoved(mode) @@ -2060,13 +2076,16 @@ func (s *Server) addUDPRelay(ctx context.Context, mapping *proto.ProxyMapping, t } func (s *Server) updateMapping(ctx context.Context, mapping *proto.ProxyMapping) error { - // Very simple implementation here, we don't touch the existing peer - // connection or any existing TLS configuration, we simply overwrite - // the auth and proxy mappings. - // Note: this does require the management server to always send a - // full mapping rather than deltas during a modification. accountID := types.AccountID(mapping.GetAccountId()) svcID := types.ServiceID(mapping.GetId()) + m, err := s.protoToMapping(ctx, mapping) + if err != nil { + return err + } + resolver, err := proxy.NewTargetResolver(m) + if err != nil { + return fmt.Errorf("prepare target routes: %w", err) + } var schemes []auth.Scheme if mapping.GetAuth().GetPassword() { @@ -2084,19 +2103,19 @@ func (s *Server) updateMapping(ctx context.Context, mapping *proto.ProxyMapping) s.warnIfGeoUnavailable(mapping.GetDomain(), mapping.GetAccessRestrictions()) maxSessionAge := time.Duration(mapping.GetAuth().GetMaxSessionAgeSeconds()) * time.Second - if err := s.auth.AddDomain(mapping.GetDomain(), schemes, mapping.GetAuth().GetSessionKey(), maxSessionAge, accountID, svcID, ipRestrictions, mapping.GetPrivate(), mapping.GetAuth().GetAllowedGroupIds()); err != nil { + config, err := auth.NewDomainConfig(mapping.GetDomain(), schemes, mapping.GetAuth().GetSessionKey(), maxSessionAge, accountID, svcID, ipRestrictions, mapping.GetPrivate(), mapping.GetAuth().GetAllowedGroupIds(), auth.WithTargetResolver(resolver)) + if err != nil { return fmt.Errorf("auth setup for domain %s: %w", mapping.GetDomain(), err) } - m := s.protoToMapping(ctx, mapping) - // The chain is published before the route that leads to it. A request - // arriving at a target whose chain has not been rebuilt yet is served - // straight through, so a provider update that added the route first left a - // window in which an inference could complete unrouted and unmetered. - // Rebuilding first inverts that: the worst a request in the window meets is - // the new chain in front of the previous target, which is still counted. - if err := s.rebuildMiddlewareChains(svcID, m); err != nil { + revision, err := s.rebuildMiddlewareChains(svcID, m) + if err != nil { return err } + m.MiddlewareRevision = revision + config.TargetResolver = resolver.WithMiddlewareRevision(revision) + // The auth snapshot owns its resolver, so an in-flight request cannot + // authorize an old target and then forward to a replacement target. + s.auth.AddDomainConfig(mapping.GetDomain(), config) s.meter.AddMapping(m) s.proxy.AddMapping(m) return nil @@ -2161,22 +2180,17 @@ func (s *Server) initMiddlewareManager(ctx context.Context) error { return nil } -// rebuildMiddlewareChains converts m into per-path bindings and calls -// Manager.Rebuild. Short-circuits when the middleware manager is unset, which -// is a deployment without middleware rather than a failure to install it. -// -// A rebuild that fails is reported rather than logged: the caller publishes -// the route once this returns, and a route published over chains that were -// not installed serves requests with no policy enforcement and no metering. -func (s *Server) rebuildMiddlewareChains(svcID types.ServiceID, m proxy.Mapping) error { +// rebuildMiddlewareChains installs the service's policies before its routing snapshot. +func (s *Server) rebuildMiddlewareChains(svcID types.ServiceID, m proxy.Mapping) (middleware.Revision, error) { if s.middlewareManager == nil { - return nil + return 0, nil } bindings := buildMiddlewareBindings(svcID, m) - if err := s.middlewareManager.Rebuild(string(svcID), bindings); err != nil { - return fmt.Errorf("rebuild middleware chains for service %s: %w", svcID, err) + revision, err := s.middlewareManager.RebuildSnapshot(string(svcID), bindings) + if err != nil { + return 0, fmt.Errorf("rebuild middleware chains for service %s: %w", svcID, err) } - return nil + return revision, nil } // isLiveService reports whether svcID is currently present in the live @@ -2261,10 +2275,10 @@ func (s *Server) cleanupMappingRoutes(mapping *proto.ProxyMapping) { if s.acme != nil { s.acme.RemoveDomain(d) } - s.auth.RemoveDomain(host) if s.proxy.RemoveMapping(proxy.Mapping{Host: host}) { s.meter.RemoveMapping(proxy.Mapping{Host: host}) } + s.auth.RemoveDomain(host) // Close hijacked connections (WebSocket) for this domain. if n := s.hijackTracker.CloseByHost(host); n > 0 { s.Logger.Debugf("closed %d hijacked connection(s) for %s", n, host) @@ -2337,23 +2351,33 @@ func (s *Server) deleteMapping(svcID types.ServiceID) *proto.ProxyMapping { return m } -func (s *Server) protoToMapping(ctx context.Context, mapping *proto.ProxyMapping) proxy.Mapping { +func (s *Server) protoToMapping(ctx context.Context, mapping *proto.ProxyMapping) (proxy.Mapping, error) { + pathMappings := mapping.GetPath() + actions, strictPaths, err := parseTargetAccessActions(pathMappings) + if err != nil { + return proxy.Mapping{}, err + } + paths := make(map[string]*proxy.PathTarget) - for _, pathMapping := range mapping.GetPath() { + for i, pathMapping := range pathMappings { + path := pathMapping.GetPath() + if strictPaths && path == "" { + path = "/" + } + if strictPaths { + if _, exists := paths[path]; exists { + return proxy.Mapping{}, fmt.Errorf("duplicate target location %q", path) + } + } targetURL, err := url.Parse(pathMapping.GetTarget()) if err != nil { - s.Logger.WithFields(log.Fields{ - "service_id": mapping.GetId(), - "account_id": mapping.GetAccountId(), - "domain": mapping.GetDomain(), - "path": pathMapping.GetPath(), - "target": pathMapping.GetTarget(), - }).WithError(err).Error("failed to parse target URL for path, skipping") - s.notifyError(ctx, mapping, fmt.Errorf("invalid target URL %q for path %q: %w", pathMapping.GetTarget(), pathMapping.GetPath(), err)) - continue + return proxy.Mapping{}, fmt.Errorf("parse target URL for location %q: %w", path, err) + } + if targetURL.Host == "" || (targetURL.Scheme != "http" && targetURL.Scheme != "https") { + return proxy.Mapping{}, fmt.Errorf("invalid HTTP target URL for location %q", path) } - pt := &proxy.PathTarget{URL: targetURL} + pt := &proxy.PathTarget{URL: targetURL, AccessAction: actions[i]} if opts := pathMapping.GetOptions(); opts != nil { pt.SkipTLSVerify = opts.GetSkipTlsVerify() pt.PathRewrite = protoToPathRewrite(opts.GetPathRewrite()) @@ -2370,7 +2394,7 @@ func (s *Server) protoToMapping(ctx context.Context, mapping *proto.ProxyMapping pt.DisableAccessLog = opts.GetDisableAccessLog() } pt.RequestTimeout = s.clampDialTimeout(pt.RequestTimeout) - paths[pathMapping.GetPath()] = pt + paths[path] = pt } m := proxy.Mapping{ ID: types.ServiceID(mapping.GetId()), @@ -2383,7 +2407,7 @@ func (s *Server) protoToMapping(ctx context.Context, mapping *proto.ProxyMapping for _, ha := range mapping.GetAuth().GetHeaderAuths() { m.StripAuthHeaders = append(m.StripAuthHeaders, ha.GetHeader()) } - return m + return m, nil } func protoToPathRewrite(mode proto.PathRewriteMode) proxy.PathRewriteMode { diff --git a/proxy/target_access_control.go b/proxy/target_access_control.go new file mode 100644 index 000000000..9460375c9 --- /dev/null +++ b/proxy/target_access_control.go @@ -0,0 +1,40 @@ +package proxy + +import ( + "fmt" + + "github.com/netbirdio/netbird/proxy/internal/proxy" + "github.com/netbirdio/netbird/shared/management/proto" +) + +func parseTargetAccessActions(pathMappings []*proto.PathMapping) ([]proxy.AccessAction, bool, error) { + actions := make([]proxy.AccessAction, len(pathMappings)) + strictPaths := false + for i, pathMapping := range pathMappings { + if pathMapping == nil { + return nil, false, fmt.Errorf("nil target mapping") + } + action, err := targetAccessActionFromProto(pathMapping.GetAccessAction()) + if err != nil { + return nil, false, err + } + actions[i] = action + if action != proxy.AccessActionInherit { + strictPaths = true + } + } + return actions, strictPaths, nil +} + +func targetAccessActionFromProto(action proto.TargetAccessAction) (proxy.AccessAction, error) { + switch action { + case proto.TargetAccessAction_TARGET_ACCESS_ACTION_INHERIT: + return proxy.AccessActionInherit, nil + case proto.TargetAccessAction_TARGET_ACCESS_ACTION_BYPASS: + return proxy.AccessActionBypass, nil + case proto.TargetAccessAction_TARGET_ACCESS_ACTION_BLOCK: + return proxy.AccessActionBlock, nil + default: + return "", fmt.Errorf("unknown target access action %d", action) + } +} diff --git a/proxy/target_access_control_runtime_test.go b/proxy/target_access_control_runtime_test.go new file mode 100644 index 000000000..4ff3104e4 --- /dev/null +++ b/proxy/target_access_control_runtime_test.go @@ -0,0 +1,172 @@ +package proxy + +import ( + "context" + "crypto/tls" + "net" + "net/http" + "net/http/httptest" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/metric/noop" + + "github.com/netbirdio/netbird/proxy/internal/auth" + proxymetrics "github.com/netbirdio/netbird/proxy/internal/metrics" + internalproxy "github.com/netbirdio/netbird/proxy/internal/proxy" + "github.com/netbirdio/netbird/proxy/internal/roundtrip" + nbtcp "github.com/netbirdio/netbird/proxy/internal/tcp" + proxytypes "github.com/netbirdio/netbird/proxy/internal/types" + "github.com/netbirdio/netbird/shared/management/proto" +) + +const ( + targetAccessAccountID = "test-account-1" + targetAccessCluster = "test.proxy.io" + targetAccessDomain = "target-access.test.proxy.io" + targetAccessHeader = "X-Test-Key" + targetAccessSecret = "valid-secret" +) + +type targetAccessRequest struct { + path string + headers http.Header +} + +type targetAccessUpstream struct { + mu sync.Mutex + requests []targetAccessRequest +} + +func (u *targetAccessUpstream) ServeHTTP(w http.ResponseWriter, r *http.Request) { + u.mu.Lock() + u.requests = append(u.requests, targetAccessRequest{ + path: r.URL.Path, + headers: r.Header.Clone(), + }) + u.mu.Unlock() + w.WriteHeader(http.StatusOK) +} + +func (u *targetAccessUpstream) snapshot() []targetAccessRequest { + u.mu.Lock() + defer u.mu.Unlock() + return append([]targetAccessRequest(nil), u.requests...) +} + +func newTargetAccessRuntime(t *testing.T) (*Server, http.Handler) { + t.Helper() + + logger := quietLifecycleLogger() + meter, err := proxymetrics.New(t.Context(), noop.Meter{}) + require.NoError(t, err) + + reverseProxy := internalproxy.NewReverseProxy(http.DefaultTransport, "auto", nil, logger) + authMiddleware := auth.NewMiddleware(logger, nil, nil) + srv := &Server{ + ctx: t.Context(), + Logger: logger, + proxy: reverseProxy, + auth: authMiddleware, + meter: meter, + mainRouter: nbtcp.NewRouter(logger, nil, &net.TCPAddr{}), + portRouters: make(map[uint16]*portRouter), + svcPorts: make(map[proxytypes.ServiceID][]uint16), + lastMappings: make(map[proxytypes.ServiceID]*proto.ProxyMapping), + crowdsecServices: make(map[proxytypes.ServiceID]bool), + removePeer: func(context.Context, proxytypes.AccountID, roundtrip.ServiceKey) error { + return nil + }, + } + + return srv, authMiddleware.Protect(reverseProxy) +} + +func applyTargetAccessMapping(t *testing.T, srv *Server, mapping *proto.ProxyMapping) { + t.Helper() + require.NoError(t, srv.updateMapping(t.Context(), mapping)) + srv.storeMapping(mapping) +} + +func targetAccessRequestTo(handler http.Handler, path, remoteAddr string, headers http.Header) *httptest.ResponseRecorder { + req := httptest.NewRequest(http.MethodGet, "http://"+targetAccessDomain+path, nil) + req.Host = targetAccessDomain + req.RemoteAddr = remoteAddr + for name, values := range headers { + for _, value := range values { + req.Header.Add(name, value) + } + } + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, req) + return recorder +} + +func TestModifyHTTPMappingFromEmptyPathsRegistersRoute(t *testing.T) { + upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("X-Test-Upstream", "reached") + w.WriteHeader(http.StatusNoContent) + })) + t.Cleanup(upstream.Close) + + runtime, handler := newTargetAccessRuntime(t) + listener, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + t.Cleanup(func() { _ = listener.Close() }) + + router := nbtcp.NewRouter(runtime.Logger, func(proxytypes.AccountID) (proxytypes.DialContextFunc, error) { + return func(context.Context, string, string) (net.Conn, error) { + return nil, net.ErrClosed + }, nil + }, listener.Addr()) + router.SetFallback(nbtcp.Route{ + Type: nbtcp.RouteTCP, + AccountID: "fallback-account", + ServiceID: "fallback-service", + Target: "unreachable.test:443", + }) + runtime.mainRouter = router + + httpServer := &http.Server{ + Handler: handler, + TLSConfig: selfSignedTLSConfig(t), + ReadHeaderTimeout: time.Second, + } + t.Cleanup(func() { _ = httpServer.Close() }) + go func() { _ = httpServer.ServeTLS(router.HTTPListener(), "", "") }() + go feedRouterFromListener(t.Context(), listener, router, runtime.Logger, proxytypes.AccountID(targetAccessAccountID)) + + previous := &proto.ProxyMapping{ + Id: "empty-to-routable", + AccountId: targetAccessAccountID, + Domain: targetAccessDomain, + } + runtime.storeMapping(previous) + replacement := &proto.ProxyMapping{ + Id: previous.GetId(), + AccountId: previous.GetAccountId(), + Domain: previous.GetDomain(), + Path: []*proto.PathMapping{{ + Path: "/", + Target: upstream.URL, + }}, + } + require.NoError(t, runtime.modifyMapping(t.Context(), replacement)) + + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true, MinVersion: tls.VersionTLS12}, //nolint:gosec + DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) { + return (&net.Dialer{}).DialContext(ctx, network, listener.Addr().String()) + }, + } + t.Cleanup(transport.CloseIdleConnections) + client := &http.Client{Transport: transport, Timeout: 3 * time.Second} + response, err := client.Get("https://" + targetAccessDomain + "/ready") + require.NoError(t, err, "first target must register the domain with the SNI router") + t.Cleanup(func() { _ = response.Body.Close() }) + assert.Equal(t, http.StatusNoContent, response.StatusCode) + assert.Equal(t, "reached", response.Header.Get("X-Test-Upstream")) +} diff --git a/proxy/target_access_control_test.go b/proxy/target_access_control_test.go new file mode 100644 index 000000000..a1be3a83b --- /dev/null +++ b/proxy/target_access_control_test.go @@ -0,0 +1,151 @@ +package proxy + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + + internalproxy "github.com/netbirdio/netbird/proxy/internal/proxy" + managementproto "github.com/netbirdio/netbird/shared/management/proto" +) + +func TestTargetAccessActionFromProto(t *testing.T) { + tests := []struct { + name string + wire managementproto.TargetAccessAction + want internalproxy.AccessAction + }{ + {"inherit", managementproto.TargetAccessAction_TARGET_ACCESS_ACTION_INHERIT, internalproxy.AccessActionInherit}, + {"bypass", managementproto.TargetAccessAction_TARGET_ACCESS_ACTION_BYPASS, internalproxy.AccessActionBypass}, + {"block", managementproto.TargetAccessAction_TARGET_ACCESS_ACTION_BLOCK, internalproxy.AccessActionBlock}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := targetAccessActionFromProto(tt.wire) + require.NoError(t, err) + assert.Equal(t, tt.want, got, "wire action must retain its enforcement meaning") + }) + } + _, err := targetAccessActionFromProto(managementproto.TargetAccessAction(99)) + require.Error(t, err, "an unknown wire action must reject the mapping") +} + +func TestProtoToMappingPreservesLegacyDuplicateLocations(t *testing.T) { + runtime, _ := newTargetAccessRuntime(t) + mapping, err := runtime.protoToMapping(t.Context(), &managementproto.ProxyMapping{ + Id: "legacy-service", AccountId: "account", Domain: targetAccessDomain, + Path: []*managementproto.PathMapping{ + {Path: "/", Target: "http://first.internal"}, + {Path: "/", Target: "https://second.internal"}, + {Path: "", Target: "http://empty.internal"}, + }, + }) + require.NoError(t, err) + require.Len(t, mapping.Paths, 2, "legacy empty and slash locations must remain distinct") + assert.Equal(t, "second.internal", mapping.Paths["/"].URL.Host, + "legacy duplicate locations must retain last-entry-wins behavior") + assert.Equal(t, "empty.internal", mapping.Paths[""].URL.Host) +} + +func TestProtoToMappingRejectsDuplicateLocationsWhenAccessControlIsActive(t *testing.T) { + tests := []struct { + name string + paths []*managementproto.PathMapping + }{ + { + name: "duplicate action location", + paths: []*managementproto.PathMapping{ + {Path: "/", Target: "http://first.internal"}, + {Path: "/", Target: "http://second.internal", AccessAction: managementproto.TargetAccessAction_TARGET_ACCESS_ACTION_BLOCK}, + }, + }, + { + name: "root aliases", + paths: []*managementproto.PathMapping{ + {Path: "", Target: "http://first.internal"}, + {Path: "/", Target: "http://second.internal", AccessAction: managementproto.TargetAccessAction_TARGET_ACCESS_ACTION_BYPASS}, + }, + }, + { + name: "action elsewhere", + paths: []*managementproto.PathMapping{ + {Path: "/", Target: "http://first.internal"}, + {Path: "/", Target: "http://second.internal"}, + {Path: "/public", Target: "http://public.internal", AccessAction: managementproto.TargetAccessAction_TARGET_ACCESS_ACTION_BYPASS}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + runtime, _ := newTargetAccessRuntime(t) + _, err := runtime.protoToMapping(t.Context(), &managementproto.ProxyMapping{ + Id: "guarded-service", AccountId: "account", Domain: targetAccessDomain, Path: tt.paths, + }) + assert.ErrorContains(t, err, `duplicate target location "/"`) + }) + } +} + +func TestModifyHTTPMappingRejectsInvalidReplacement(t *testing.T) { + tests := []struct { + name string + modify func(*managementproto.ProxyMapping) + }{ + {"unknown action", func(m *managementproto.ProxyMapping) { + m.Path[0].AccessAction = managementproto.TargetAccessAction(99) + }}, + {"duplicate locations", func(m *managementproto.ProxyMapping) { + m.Path = append(m.Path, &managementproto.PathMapping{Path: "/", Target: "http://127.0.0.1:1"}) + }}, + {"duplicate root aliases", func(m *managementproto.ProxyMapping) { + m.Path = append(m.Path, &managementproto.PathMapping{Target: "http://127.0.0.1:1"}) + }}, + {"nil target", func(m *managementproto.ProxyMapping) { + m.Path = append(m.Path, nil) + }}, + {"malformed URL", func(m *managementproto.ProxyMapping) { + m.Path[0].Target = "http://[" + }}, + {"missing host", func(m *managementproto.ProxyMapping) { + m.Path[0].Target = "http:/backend" + }}, + {"unsupported protocol", func(m *managementproto.ProxyMapping) { + m.Path[0].Target = "ftp://backend" + }}, + {"private bypass", func(m *managementproto.ProxyMapping) { + m.Private = true + m.Path[0].AccessAction = managementproto.TargetAccessAction_TARGET_ACCESS_ACTION_BYPASS + }}, + {"agent network bypass", func(m *managementproto.ProxyMapping) { + m.Path[0].AccessAction = managementproto.TargetAccessAction_TARGET_ACCESS_ACTION_BYPASS + m.Path[0].Options = &managementproto.PathTargetOptions{AgentNetwork: true} + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + runtime, handler := newTargetAccessRuntime(t) + original := &managementproto.ProxyMapping{ + Id: "guarded-service", AccountId: "account", Domain: targetAccessDomain, + Path: []*managementproto.PathMapping{{ + Path: "/", Target: "http://127.0.0.1:1", + AccessAction: managementproto.TargetAccessAction_TARGET_ACCESS_ACTION_BLOCK, + }}, + } + applyTargetAccessMapping(t, runtime, original) + replacement := proto.Clone(original).(*managementproto.ProxyMapping) + tt.modify(replacement) + require.Error(t, runtime.modifyMapping(t.Context(), replacement)) + response := targetAccessRequestTo(handler, "/protected", "192.0.2.1:1234", nil) + assert.Equal(t, http.StatusNotFound, response.Code, + "invalid replacement must withdraw the route instead of retaining a stale policy") + require.NoError(t, runtime.modifyMapping(t.Context(), original), + "a valid update must reinstall a previously withdrawn route") + recovered := targetAccessRequestTo(handler, "/protected", "192.0.2.1:1234", nil) + assert.Equal(t, http.StatusForbidden, recovered.Code, "the reinstalled block must be enforced") + }) + } +} diff --git a/proxy/target_middleware_revision_test.go b/proxy/target_middleware_revision_test.go new file mode 100644 index 000000000..7dd5453a2 --- /dev/null +++ b/proxy/target_middleware_revision_test.go @@ -0,0 +1,115 @@ +package proxy + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + protobuf "google.golang.org/protobuf/proto" + + "github.com/netbirdio/netbird/proxy/internal/middleware" + guardrail "github.com/netbirdio/netbird/proxy/internal/middleware/builtin/llm_guardrail" + internalproxy "github.com/netbirdio/netbird/proxy/internal/proxy" + "github.com/netbirdio/netbird/shared/management/proto" +) + +func TestUpdateMappingBindsMiddlewareRevisionAcrossPolicyUpdates(t *testing.T) { + oldUpstream := &targetAccessUpstream{} + oldBackend := httptest.NewServer(oldUpstream) + t.Cleanup(oldBackend.Close) + newUpstream := &targetAccessUpstream{} + newBackend := httptest.NewServer(newUpstream) + t.Cleanup(newBackend.Close) + + runtime, _ := newTargetAccessRuntime(t) + registry := middleware.NewRegistry() + require.NoError(t, registry.Register(guardrail.Factory{})) + manager := middleware.NewManager(0, nil, runtime.Logger) + manager.SetResolver(middleware.NewResolver(registry)) + manager.SetLiveServiceCheck(runtime.isLiveService) + t.Cleanup(manager.InvalidateAll) + runtime.middlewareRegistry = registry + runtime.middlewareManager = manager + runtime.proxy = internalproxy.NewReverseProxy(http.DefaultTransport, "auto", nil, runtime.Logger, + internalproxy.WithMiddlewareManager(manager)) + handler := runtime.auth.Protect(runtime.proxy) + + mapping := &proto.ProxyMapping{ + Id: "middleware-revision-service", AccountId: targetAccessAccountID, Domain: targetAccessDomain, + Path: []*proto.PathMapping{{ + Path: "/", Target: oldBackend.URL, + Options: &proto.PathTargetOptions{ + AgentNetwork: true, + DirectUpstream: true, + Middlewares: []*proto.MiddlewareConfig{{ + Id: guardrail.ID, + Slot: proto.MiddlewareSlot_MIDDLEWARE_SLOT_ON_REQUEST, + Enabled: true, + ConfigJson: []byte(`{"provider_allowlists":{"provider-1":["allowed-model"]}}`), + }}, + }, + }}, + } + applyTargetAccessMapping(t, runtime, mapping) + initial := targetAccessRequestTo(handler, "/v1/chat/completions", "192.0.2.1:1234", nil) + require.Equal(t, http.StatusForbidden, initial.Code, + "the original guardrail must reject a request with no known model") + require.Empty(t, oldUpstream.snapshot(), "the original policy must prevent upstream forwarding") + + // Hold the request at the auth/proxy boundary so updateMapping runs after + // authorization has pinned the route but before its middleware is selected. + captureAuthorizedRequest := func() *http.Request { + var pinned *http.Request + capture := runtime.auth.Protect(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + pinned = r + w.WriteHeader(http.StatusNoContent) + })) + response := targetAccessRequestTo(capture, "/v1/chat/completions", "192.0.2.1:1234", nil) + require.Equal(t, http.StatusNoContent, response.Code, "auth must pass the resolved request to the proxy") + require.NotNil(t, pinned, "the request must be captured after auth target resolution") + return pinned + } + staleRequest := captureAuthorizedRequest() + oldChain := manager.ChainFor(mapping.GetId(), "/") + require.NotNil(t, oldChain, "updateMapping must install the original guardrail chain") + + replacement := protobuf.Clone(mapping).(*proto.ProxyMapping) + replacement.Path[0].Target = newBackend.URL + replacement.Path[0].Options.Middlewares[0].ConfigJson = []byte(`{}`) + applyTargetAccessMapping(t, runtime, replacement) + newChain := manager.ChainFor(mapping.GetId(), "/") + require.NotNil(t, newChain, "updateMapping must install the replacement guardrail chain") + require.NotSame(t, oldChain, newChain, "a policy update must replace the middleware chain") + + staleResponse := httptest.NewRecorder() + runtime.proxy.ServeHTTP(staleResponse, staleRequest) + assert.Equal(t, http.StatusServiceUnavailable, staleResponse.Code, + "a request authorized against the old target must not execute the replacement policy") + assert.Equal(t, "no-store", staleResponse.Header().Get("Cache-Control"), "stale policy failures must not be cached") + assert.Empty(t, oldUpstream.snapshot(), "a replacement policy must never authorize the old upstream") + assert.Empty(t, newUpstream.snapshot(), "a stale request must not forward to the replacement upstream") + + fresh := targetAccessRequestTo(handler, "/v1/chat/completions", "192.0.2.1:1234", nil) + require.Equal(t, http.StatusOK, fresh.Code, "fresh auth resolution must use the replacement middleware revision") + require.Len(t, newUpstream.snapshot(), 1, "the replacement target must receive the fresh request") + + // Provider changes rebuild middleware instances without replacing the + // service mapping, so requests pinned to its current revision stay valid. + currentRequest := captureAuthorizedRequest() + manager.InvalidateMiddleware(guardrail.ID) + refreshedChain := manager.ChainFor(mapping.GetId(), "/") + require.NotNil(t, refreshedChain, "a live service must retain its chain after provider refresh") + require.NotSame(t, newChain, refreshedChain, "provider refresh must rebuild the concrete guardrail chain") + + currentResponse := httptest.NewRecorder() + runtime.proxy.ServeHTTP(currentResponse, currentRequest) + assert.Equal(t, http.StatusOK, currentResponse.Code, "provider refresh must preserve the current request's revision") + assert.Len(t, newUpstream.snapshot(), 2, "the current request must reach the replacement target after refresh") + + afterRefresh := targetAccessRequestTo(handler, "/v1/chat/completions", "192.0.2.1:1234", nil) + assert.Equal(t, http.StatusOK, afterRefresh.Code, "new requests must continue after provider refresh") + assert.Len(t, newUpstream.snapshot(), 3, "fresh requests must use the rebuilt chain and current target") + assert.Empty(t, oldUpstream.snapshot(), "no request may reach the retired target") +} diff --git a/shared/management/proto/proxy_service.pb.go b/shared/management/proto/proxy_service.pb.go index 09dff0d36..fe5cc0125 100644 --- a/shared/management/proto/proxy_service.pb.go +++ b/shared/management/proto/proxy_service.pb.go @@ -117,6 +117,55 @@ func (PathRewriteMode) EnumDescriptor() ([]byte, []int) { return file_proxy_service_proto_rawDescGZIP(), []int{1} } +type TargetAccessAction int32 + +const ( + TargetAccessAction_TARGET_ACCESS_ACTION_INHERIT TargetAccessAction = 0 + TargetAccessAction_TARGET_ACCESS_ACTION_BYPASS TargetAccessAction = 1 + TargetAccessAction_TARGET_ACCESS_ACTION_BLOCK TargetAccessAction = 2 +) + +// Enum value maps for TargetAccessAction. +var ( + TargetAccessAction_name = map[int32]string{ + 0: "TARGET_ACCESS_ACTION_INHERIT", + 1: "TARGET_ACCESS_ACTION_BYPASS", + 2: "TARGET_ACCESS_ACTION_BLOCK", + } + TargetAccessAction_value = map[string]int32{ + "TARGET_ACCESS_ACTION_INHERIT": 0, + "TARGET_ACCESS_ACTION_BYPASS": 1, + "TARGET_ACCESS_ACTION_BLOCK": 2, + } +) + +func (x TargetAccessAction) Enum() *TargetAccessAction { + p := new(TargetAccessAction) + *p = x + return p +} + +func (x TargetAccessAction) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TargetAccessAction) Descriptor() protoreflect.EnumDescriptor { + return file_proxy_service_proto_enumTypes[2].Descriptor() +} + +func (TargetAccessAction) Type() protoreflect.EnumType { + return &file_proxy_service_proto_enumTypes[2] +} + +func (x TargetAccessAction) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TargetAccessAction.Descriptor instead. +func (TargetAccessAction) EnumDescriptor() ([]byte, []int) { + return file_proxy_service_proto_rawDescGZIP(), []int{2} +} + // MiddlewareSlot identifies where in the request lifecycle a middleware // runs. Mirrors proxy/internal/middleware.Slot. type MiddlewareSlot int32 @@ -155,11 +204,11 @@ func (x MiddlewareSlot) String() string { } func (MiddlewareSlot) Descriptor() protoreflect.EnumDescriptor { - return file_proxy_service_proto_enumTypes[2].Descriptor() + return file_proxy_service_proto_enumTypes[3].Descriptor() } func (MiddlewareSlot) Type() protoreflect.EnumType { - return &file_proxy_service_proto_enumTypes[2] + return &file_proxy_service_proto_enumTypes[3] } func (x MiddlewareSlot) Number() protoreflect.EnumNumber { @@ -168,7 +217,7 @@ func (x MiddlewareSlot) Number() protoreflect.EnumNumber { // Deprecated: Use MiddlewareSlot.Descriptor instead. func (MiddlewareSlot) EnumDescriptor() ([]byte, []int) { - return file_proxy_service_proto_rawDescGZIP(), []int{2} + return file_proxy_service_proto_rawDescGZIP(), []int{3} } type ProxyStatus int32 @@ -213,11 +262,11 @@ func (x ProxyStatus) String() string { } func (ProxyStatus) Descriptor() protoreflect.EnumDescriptor { - return file_proxy_service_proto_enumTypes[3].Descriptor() + return file_proxy_service_proto_enumTypes[4].Descriptor() } func (ProxyStatus) Type() protoreflect.EnumType { - return &file_proxy_service_proto_enumTypes[3] + return &file_proxy_service_proto_enumTypes[4] } func (x ProxyStatus) Number() protoreflect.EnumNumber { @@ -226,7 +275,7 @@ func (x ProxyStatus) Number() protoreflect.EnumNumber { // Deprecated: Use ProxyStatus.Descriptor instead. func (ProxyStatus) EnumDescriptor() ([]byte, []int) { - return file_proxy_service_proto_rawDescGZIP(), []int{3} + return file_proxy_service_proto_rawDescGZIP(), []int{4} } type MiddlewareConfig_FailMode int32 @@ -259,11 +308,11 @@ func (x MiddlewareConfig_FailMode) String() string { } func (MiddlewareConfig_FailMode) Descriptor() protoreflect.EnumDescriptor { - return file_proxy_service_proto_enumTypes[4].Descriptor() + return file_proxy_service_proto_enumTypes[5].Descriptor() } func (MiddlewareConfig_FailMode) Type() protoreflect.EnumType { - return &file_proxy_service_proto_enumTypes[4] + return &file_proxy_service_proto_enumTypes[5] } func (x MiddlewareConfig_FailMode) Number() protoreflect.EnumNumber { @@ -777,9 +826,10 @@ type PathMapping struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` - Options *PathTargetOptions `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` + Options *PathTargetOptions `protobuf:"bytes,3,opt,name=options,proto3" json:"options,omitempty"` + AccessAction TargetAccessAction `protobuf:"varint,4,opt,name=access_action,json=accessAction,proto3,enum=management.TargetAccessAction" json:"access_action,omitempty"` } func (x *PathMapping) Reset() { @@ -835,6 +885,13 @@ func (x *PathMapping) GetOptions() *PathTargetOptions { return nil } +func (x *PathMapping) GetAccessAction() TargetAccessAction { + if x != nil { + return x.AccessAction + } + return TargetAccessAction_TARGET_ACCESS_ACTION_INHERIT +} + type HeaderAuth struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3325,442 +3382,454 @@ var file_proxy_service_proto_rawDesc = []byte{ 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x61, 0x6e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x22, 0x2a, 0x0a, 0x08, 0x46, 0x61, 0x69, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x41, 0x49, 0x4c, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, - 0x46, 0x41, 0x49, 0x4c, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, 0x01, 0x22, 0x72, 0x0a, - 0x0b, 0x50, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x37, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x54, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0x47, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x12, - 0x16, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x61, 0x73, 0x68, 0x65, - 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, - 0x61, 0x73, 0x68, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x91, 0x02, 0x0a, 0x0e, 0x41, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, - 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x35, - 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x67, - 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x14, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x65, 0x53, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, - 0x70, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x12, 0x39, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x41, 0x75, 0x74, 0x68, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x75, 0x74, - 0x68, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0xdd, - 0x01, 0x0a, 0x12, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, - 0x5f, 0x63, 0x69, 0x64, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x43, 0x69, 0x64, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x43, 0x69, 0x64, 0x72, 0x73, 0x12, - 0x2b, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x6f, - 0x77, 0x64, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x63, 0x72, 0x6f, 0x77, 0x64, 0x73, 0x65, 0x63, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x80, - 0x04, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, - 0x36, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, - 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x2b, - 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x4d, 0x61, - 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x61, - 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2e, 0x0a, 0x04, 0x61, 0x75, - 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x61, - 0x73, 0x73, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x61, 0x73, 0x73, 0x48, 0x6f, 0x73, 0x74, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, - 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x10, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, - 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x5f, - 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x4f, 0x0a, 0x13, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x74, 0x72, - 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x22, 0x3f, 0x0a, 0x14, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, - 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x03, 0x6c, 0x6f, 0x67, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x03, 0x6c, - 0x6f, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa9, 0x05, 0x0a, 0x09, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x73, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, - 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, - 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x61, - 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x69, 0x73, 0x6d, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x4d, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x69, - 0x73, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, - 0x75, 0x74, 0x68, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x21, - 0x0a, 0x0c, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x55, 0x70, 0x6c, 0x6f, 0x61, - 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, - 0x6f, 0x61, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x2e, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf8, 0x01, 0x0a, 0x13, 0x41, 0x75, 0x74, 0x68, - 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x39, - 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, - 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x2a, 0x0a, 0x03, 0x70, 0x69, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, - 0x52, 0x03, 0x70, 0x69, 0x6e, 0x12, 0x40, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, - 0x61, 0x75, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x75, - 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x57, 0x0a, 0x11, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x0f, 0x50, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x1e, 0x0a, 0x0a, 0x50, 0x69, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x69, 0x6e, 0x22, 0x55, 0x0a, 0x14, 0x41, 0x75, - 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x22, 0xda, 0x02, 0x0a, 0x17, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, - 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x0a, 0x12, - 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x73, 0x73, 0x75, - 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x12, 0x28, 0x0a, 0x0d, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x50, 0x0a, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, - 0x72, 0x48, 0x01, 0x52, 0x0f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x69, 0x6e, - 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x22, 0x6f, - 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, 0x69, - 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, - 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x75, 0x6e, 0x6e, 0x65, - 0x6c, 0x49, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x5f, 0x70, 0x6f, 0x72, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x68, 0x74, 0x74, 0x70, 0x73, 0x50, 0x6f, - 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x68, 0x74, 0x74, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x22, - 0x1a, 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb8, 0x01, 0x0a, 0x16, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x77, 0x69, - 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, - 0x61, 0x72, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x6f, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x28, 0x0a, 0x0d, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x65, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4f, 0x49, - 0x44, 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x72, - 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x6c, 0x22, 0x26, - 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x7c, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x27, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x02, 0x18, 0x01, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x43, 0x6f, 0x64, 0x65, 0x22, 0x81, 0x02, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, - 0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x52, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, - 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x65, 0x65, - 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x50, 0x0a, 0x19, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, - 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, - 0x49, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x84, 0x02, 0x0a, 0x1a, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, - 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, - 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, - 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x69, 0x65, - 0x64, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, - 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x70, 0x65, 0x65, 0x72, 0x5f, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x22, 0x81, 0x01, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, - 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x69, 0x6e, 0x69, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, - 0x73, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x2f, 0x0a, - 0x03, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, - 0x69, 0x6e, 0x67, 0x73, 0x41, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x03, 0x61, 0x63, 0x6b, 0x42, 0x05, - 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0xdf, 0x01, 0x0a, 0x10, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, - 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x41, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x61, 0x70, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x11, 0x0a, 0x0f, 0x53, 0x79, 0x6e, 0x63, 0x4d, - 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x41, 0x63, 0x6b, 0x22, 0x7e, 0x0a, 0x14, 0x53, 0x79, - 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x32, 0x0a, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x6d, - 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x32, 0x0a, 0x15, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, - 0x6c, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x79, - 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x22, 0xa9, 0x01, 0x0a, 0x1b, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d, - 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, - 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x22, 0xff, 0x01, 0x0a, 0x1c, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x10, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, - 0x64, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x77, 0x69, 0x6e, - 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, - 0x6e, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, - 0x65, 0x6e, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x6e, 0x79, 0x5f, - 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, - 0x6e, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x91, 0x02, 0x0a, 0x15, 0x52, 0x65, 0x63, - 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, - 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, - 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x77, - 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, - 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x64, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x63, 0x6f, 0x73, 0x74, 0x55, 0x73, 0x64, 0x12, - 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x22, 0x18, 0x0a, 0x16, - 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x64, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, - 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x50, 0x44, - 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x46, 0x0a, 0x0f, - 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, - 0x18, 0x0a, 0x14, 0x50, 0x41, 0x54, 0x48, 0x5f, 0x52, 0x45, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, - 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x50, 0x41, 0x54, - 0x48, 0x5f, 0x52, 0x45, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x50, 0x52, 0x45, 0x53, 0x45, 0x52, - 0x56, 0x45, 0x10, 0x01, 0x2a, 0x90, 0x01, 0x0a, 0x0e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, - 0x61, 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x49, 0x44, 0x44, 0x4c, - 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, 0x49, 0x44, 0x44, - 0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4f, 0x4e, 0x5f, 0x52, - 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x49, 0x44, 0x44, - 0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4f, 0x4e, 0x5f, 0x52, - 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x49, 0x44, - 0x44, 0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x54, 0x45, 0x52, - 0x4d, 0x49, 0x4e, 0x41, 0x4c, 0x10, 0x03, 0x2a, 0xc8, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x52, 0x4f, 0x58, 0x59, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, - 0x00, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, - 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x52, - 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x55, 0x4e, 0x4e, 0x45, - 0x4c, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, - 0x24, 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, - 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, - 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, - 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, - 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, - 0x10, 0x05, 0x32, 0xfc, 0x07, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, - 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70, - 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x30, 0x01, 0x12, 0x55, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, - 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, - 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x53, - 0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x12, 0x20, 0x2e, 0x6d, - 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x46, 0x41, 0x49, 0x4c, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, 0x01, 0x22, 0xb7, 0x01, + 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x37, 0x0a, 0x07, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x47, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x41, 0x75, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, + 0x0c, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x61, 0x73, 0x68, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x22, 0x91, 0x02, 0x0a, 0x0e, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x14, 0x6d, 0x61, 0x78, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x41, 0x67, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x70, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x70, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6f, 0x69, 0x64, + 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x12, 0x39, 0x0a, + 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x52, 0x0b, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x07, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x49, 0x64, 0x73, 0x22, 0xdd, 0x01, 0x0a, 0x12, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, + 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x43, 0x69, 0x64, 0x72, 0x73, + 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x69, 0x64, 0x72, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, + 0x43, 0x69, 0x64, 0x72, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x10, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x69, + 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, + 0x23, 0x0a, 0x0d, 0x63, 0x72, 0x6f, 0x77, 0x64, 0x73, 0x65, 0x63, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, 0x6f, 0x77, 0x64, 0x73, 0x65, 0x63, + 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x80, 0x04, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x61, + 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x36, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x50, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x04, 0x70, 0x61, 0x74, + 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x2e, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, + 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, + 0x12, 0x28, 0x0a, 0x10, 0x70, 0x61, 0x73, 0x73, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x61, 0x73, 0x73, + 0x48, 0x6f, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x65, 0x77, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0a, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x4f, 0x0a, 0x13, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, + 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x12, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x52, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0x3f, 0x0a, 0x14, 0x53, 0x65, 0x6e, 0x64, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x27, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x4c, 0x6f, 0x67, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x51, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, - 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, - 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, - 0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x4b, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, 0x12, 0x1d, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x49, - 0x44, 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, + 0x65, 0x22, 0xa9, 0x05, 0x0a, 0x09, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x12, + 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x15, 0x0a, 0x06, 0x6c, 0x6f, 0x67, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, + 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, + 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x65, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, + 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x63, 0x68, 0x61, 0x6e, + 0x69, 0x73, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x4d, + 0x65, 0x63, 0x68, 0x61, 0x6e, 0x69, 0x73, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, + 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x53, 0x75, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x75, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0d, 0x62, 0x79, 0x74, 0x65, 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3f, 0x0a, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x4c, 0x6f, 0x67, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x61, + 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x12, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf8, 0x01, + 0x0a, 0x13, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, + 0x2a, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x03, 0x70, 0x69, 0x6e, 0x12, 0x40, 0x0a, 0x0b, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, + 0x00, 0x52, 0x0a, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x42, 0x09, 0x0a, + 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x11, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, + 0x0c, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x22, 0x2d, 0x0a, 0x0f, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x22, 0x1e, 0x0a, 0x0a, 0x50, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, + 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x69, 0x6e, + 0x22, 0x55, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xda, 0x02, 0x0a, 0x17, 0x53, 0x65, 0x6e, 0x64, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x64, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x5f, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x49, 0x73, 0x73, 0x75, 0x65, + 0x64, 0x12, 0x28, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x12, 0x50, 0x0a, 0x10, 0x69, + 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x18, + 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, + 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x48, 0x01, 0x52, 0x0f, 0x69, 0x6e, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, + 0x0e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, + 0x13, 0x0a, 0x11, 0x5f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, + 0x65, 0x6e, 0x65, 0x72, 0x22, 0x6f, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x49, 0x6e, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, + 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x74, 0x74, 0x70, + 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x68, 0x74, 0x74, + 0x70, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1a, 0x0a, 0x18, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0xb8, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x30, 0x0a, 0x14, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x5f, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, + 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x22, 0x6f, 0x0a, 0x17, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x12, 0x28, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x65, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, + 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x55, 0x72, 0x6c, 0x22, 0x26, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, + 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, + 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x7c, 0x0a, 0x16, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x27, + 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x81, 0x02, 0x0a, 0x17, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, + 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, + 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, + 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x5f, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x6e, + 0x69, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, + 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, + 0x28, 0x0a, 0x10, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x50, + 0x0a, 0x19, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, + 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, + 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x22, 0x84, 0x02, 0x0a, 0x1a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, + 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, + 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, + 0x0d, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x52, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x5f, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0c, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x28, 0x0a, + 0x10, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x65, 0x72, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x13, 0x53, 0x79, 0x6e, 0x63, + 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x32, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, + 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, + 0x6e, 0x69, 0x74, 0x12, 0x2f, 0x0a, 0x03, 0x61, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x79, + 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x41, 0x63, 0x6b, 0x48, 0x00, 0x52, + 0x03, 0x61, 0x63, 0x6b, 0x42, 0x05, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0xdf, 0x01, 0x0a, 0x10, + 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x49, 0x6e, 0x69, 0x74, + 0x12, 0x19, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x41, 0x0a, 0x0c, 0x63, 0x61, + 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, + 0x6f, 0x78, 0x79, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, + 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x11, 0x0a, + 0x0f, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x41, 0x63, 0x6b, + 0x22, 0x7e, 0x0a, 0x14, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x07, 0x6d, 0x61, 0x70, 0x70, + 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x61, 0x70, 0x70, + 0x69, 0x6e, 0x67, 0x52, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x32, 0x0a, 0x15, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x22, 0xa9, 0x01, 0x0a, 0x1b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, + 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x49, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x22, 0xff, 0x01, 0x0a, + 0x1c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, + 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x6e, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, + 0x0b, 0x64, 0x65, 0x6e, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x6e, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x91, + 0x02, 0x0a, 0x15, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x77, + 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0d, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x5f, + 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x6f, + 0x73, 0x74, 0x5f, 0x75, 0x73, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x63, 0x6f, + 0x73, 0x74, 0x55, 0x73, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, + 0x64, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x64, 0x0a, 0x16, + 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x18, 0x0a, 0x14, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, + 0x4f, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x50, 0x44, + 0x41, 0x54, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56, 0x45, 0x44, + 0x10, 0x02, 0x2a, 0x46, 0x0a, 0x0f, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x41, 0x54, 0x48, 0x5f, 0x52, 0x45, + 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, + 0x19, 0x0a, 0x15, 0x50, 0x41, 0x54, 0x48, 0x5f, 0x52, 0x45, 0x57, 0x52, 0x49, 0x54, 0x45, 0x5f, + 0x50, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x10, 0x01, 0x2a, 0x77, 0x0a, 0x12, 0x54, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x20, 0x0a, 0x1c, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, + 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x48, 0x45, 0x52, 0x49, 0x54, + 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x41, 0x43, 0x43, + 0x45, 0x53, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x59, 0x50, 0x41, 0x53, + 0x53, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x41, 0x43, + 0x43, 0x45, 0x53, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, + 0x4b, 0x10, 0x02, 0x2a, 0x90, 0x01, 0x0a, 0x0e, 0x4d, 0x69, 0x64, 0x64, 0x6c, 0x65, 0x77, 0x61, + 0x72, 0x65, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x49, 0x44, 0x44, 0x4c, 0x45, + 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x4d, 0x49, 0x44, 0x44, 0x4c, + 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4f, 0x4e, 0x5f, 0x52, 0x45, + 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x4d, 0x49, 0x44, 0x44, 0x4c, + 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4f, 0x4e, 0x5f, 0x52, 0x45, + 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x49, 0x44, 0x44, + 0x4c, 0x45, 0x57, 0x41, 0x52, 0x45, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x54, 0x45, 0x52, 0x4d, + 0x49, 0x4e, 0x41, 0x4c, 0x10, 0x03, 0x2a, 0xc8, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x00, + 0x12, 0x17, 0x0a, 0x13, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x52, 0x4f, + 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x55, 0x4e, 0x4e, 0x45, 0x4c, + 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x24, + 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, + 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, + 0x4e, 0x47, 0x10, 0x03, 0x12, 0x23, 0x0a, 0x1f, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, + 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, + 0x58, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, + 0x05, 0x32, 0xfc, 0x07, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x70, 0x70, + 0x69, 0x6e, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x30, 0x01, 0x12, 0x55, 0x0a, 0x0c, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x53, 0x65, + 0x6e, 0x64, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x12, 0x20, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x51, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x12, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x75, + 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x41, + 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, + 0x79, 0x50, 0x65, 0x65, 0x72, 0x12, 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x65, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, + 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, 0x55, 0x52, 0x4c, 0x12, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, - 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0f, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x12, 0x25, + 0x43, 0x55, 0x52, 0x4c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x49, 0x44, 0x43, + 0x55, 0x52, 0x4c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0f, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, - 0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, - 0x14, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, - 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x6f, - 0x72, 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, - 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, - 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x12, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x12, 0x25, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, + 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x14, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x4c, 0x4c, 0x4d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0e, 0x52, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x4c, 0x4d, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, + 0x4c, 0x4c, 0x4d, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -3775,114 +3844,116 @@ func file_proxy_service_proto_rawDescGZIP() []byte { return file_proxy_service_proto_rawDescData } -var file_proxy_service_proto_enumTypes = make([]protoimpl.EnumInfo, 5) +var file_proxy_service_proto_enumTypes = make([]protoimpl.EnumInfo, 6) var file_proxy_service_proto_msgTypes = make([]protoimpl.MessageInfo, 39) var file_proxy_service_proto_goTypes = []interface{}{ (ProxyMappingUpdateType)(0), // 0: management.ProxyMappingUpdateType (PathRewriteMode)(0), // 1: management.PathRewriteMode - (MiddlewareSlot)(0), // 2: management.MiddlewareSlot - (ProxyStatus)(0), // 3: management.ProxyStatus - (MiddlewareConfig_FailMode)(0), // 4: management.MiddlewareConfig.FailMode - (*ProxyCapabilities)(nil), // 5: management.ProxyCapabilities - (*GetMappingUpdateRequest)(nil), // 6: management.GetMappingUpdateRequest - (*GetMappingUpdateResponse)(nil), // 7: management.GetMappingUpdateResponse - (*PathTargetOptions)(nil), // 8: management.PathTargetOptions - (*MiddlewareConfig)(nil), // 9: management.MiddlewareConfig - (*PathMapping)(nil), // 10: management.PathMapping - (*HeaderAuth)(nil), // 11: management.HeaderAuth - (*Authentication)(nil), // 12: management.Authentication - (*AccessRestrictions)(nil), // 13: management.AccessRestrictions - (*ProxyMapping)(nil), // 14: management.ProxyMapping - (*SendAccessLogRequest)(nil), // 15: management.SendAccessLogRequest - (*SendAccessLogResponse)(nil), // 16: management.SendAccessLogResponse - (*AccessLog)(nil), // 17: management.AccessLog - (*AuthenticateRequest)(nil), // 18: management.AuthenticateRequest - (*HeaderAuthRequest)(nil), // 19: management.HeaderAuthRequest - (*PasswordRequest)(nil), // 20: management.PasswordRequest - (*PinRequest)(nil), // 21: management.PinRequest - (*AuthenticateResponse)(nil), // 22: management.AuthenticateResponse - (*SendStatusUpdateRequest)(nil), // 23: management.SendStatusUpdateRequest - (*ProxyInboundListener)(nil), // 24: management.ProxyInboundListener - (*SendStatusUpdateResponse)(nil), // 25: management.SendStatusUpdateResponse - (*CreateProxyPeerRequest)(nil), // 26: management.CreateProxyPeerRequest - (*CreateProxyPeerResponse)(nil), // 27: management.CreateProxyPeerResponse - (*GetOIDCURLRequest)(nil), // 28: management.GetOIDCURLRequest - (*GetOIDCURLResponse)(nil), // 29: management.GetOIDCURLResponse - (*ValidateSessionRequest)(nil), // 30: management.ValidateSessionRequest - (*ValidateSessionResponse)(nil), // 31: management.ValidateSessionResponse - (*ValidateTunnelPeerRequest)(nil), // 32: management.ValidateTunnelPeerRequest - (*ValidateTunnelPeerResponse)(nil), // 33: management.ValidateTunnelPeerResponse - (*SyncMappingsRequest)(nil), // 34: management.SyncMappingsRequest - (*SyncMappingsInit)(nil), // 35: management.SyncMappingsInit - (*SyncMappingsAck)(nil), // 36: management.SyncMappingsAck - (*SyncMappingsResponse)(nil), // 37: management.SyncMappingsResponse - (*CheckLLMPolicyLimitsRequest)(nil), // 38: management.CheckLLMPolicyLimitsRequest - (*CheckLLMPolicyLimitsResponse)(nil), // 39: management.CheckLLMPolicyLimitsResponse - (*RecordLLMUsageRequest)(nil), // 40: management.RecordLLMUsageRequest - (*RecordLLMUsageResponse)(nil), // 41: management.RecordLLMUsageResponse - nil, // 42: management.PathTargetOptions.CustomHeadersEntry - nil, // 43: management.AccessLog.MetadataEntry - (*timestamppb.Timestamp)(nil), // 44: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 45: google.protobuf.Duration + (TargetAccessAction)(0), // 2: management.TargetAccessAction + (MiddlewareSlot)(0), // 3: management.MiddlewareSlot + (ProxyStatus)(0), // 4: management.ProxyStatus + (MiddlewareConfig_FailMode)(0), // 5: management.MiddlewareConfig.FailMode + (*ProxyCapabilities)(nil), // 6: management.ProxyCapabilities + (*GetMappingUpdateRequest)(nil), // 7: management.GetMappingUpdateRequest + (*GetMappingUpdateResponse)(nil), // 8: management.GetMappingUpdateResponse + (*PathTargetOptions)(nil), // 9: management.PathTargetOptions + (*MiddlewareConfig)(nil), // 10: management.MiddlewareConfig + (*PathMapping)(nil), // 11: management.PathMapping + (*HeaderAuth)(nil), // 12: management.HeaderAuth + (*Authentication)(nil), // 13: management.Authentication + (*AccessRestrictions)(nil), // 14: management.AccessRestrictions + (*ProxyMapping)(nil), // 15: management.ProxyMapping + (*SendAccessLogRequest)(nil), // 16: management.SendAccessLogRequest + (*SendAccessLogResponse)(nil), // 17: management.SendAccessLogResponse + (*AccessLog)(nil), // 18: management.AccessLog + (*AuthenticateRequest)(nil), // 19: management.AuthenticateRequest + (*HeaderAuthRequest)(nil), // 20: management.HeaderAuthRequest + (*PasswordRequest)(nil), // 21: management.PasswordRequest + (*PinRequest)(nil), // 22: management.PinRequest + (*AuthenticateResponse)(nil), // 23: management.AuthenticateResponse + (*SendStatusUpdateRequest)(nil), // 24: management.SendStatusUpdateRequest + (*ProxyInboundListener)(nil), // 25: management.ProxyInboundListener + (*SendStatusUpdateResponse)(nil), // 26: management.SendStatusUpdateResponse + (*CreateProxyPeerRequest)(nil), // 27: management.CreateProxyPeerRequest + (*CreateProxyPeerResponse)(nil), // 28: management.CreateProxyPeerResponse + (*GetOIDCURLRequest)(nil), // 29: management.GetOIDCURLRequest + (*GetOIDCURLResponse)(nil), // 30: management.GetOIDCURLResponse + (*ValidateSessionRequest)(nil), // 31: management.ValidateSessionRequest + (*ValidateSessionResponse)(nil), // 32: management.ValidateSessionResponse + (*ValidateTunnelPeerRequest)(nil), // 33: management.ValidateTunnelPeerRequest + (*ValidateTunnelPeerResponse)(nil), // 34: management.ValidateTunnelPeerResponse + (*SyncMappingsRequest)(nil), // 35: management.SyncMappingsRequest + (*SyncMappingsInit)(nil), // 36: management.SyncMappingsInit + (*SyncMappingsAck)(nil), // 37: management.SyncMappingsAck + (*SyncMappingsResponse)(nil), // 38: management.SyncMappingsResponse + (*CheckLLMPolicyLimitsRequest)(nil), // 39: management.CheckLLMPolicyLimitsRequest + (*CheckLLMPolicyLimitsResponse)(nil), // 40: management.CheckLLMPolicyLimitsResponse + (*RecordLLMUsageRequest)(nil), // 41: management.RecordLLMUsageRequest + (*RecordLLMUsageResponse)(nil), // 42: management.RecordLLMUsageResponse + nil, // 43: management.PathTargetOptions.CustomHeadersEntry + nil, // 44: management.AccessLog.MetadataEntry + (*timestamppb.Timestamp)(nil), // 45: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 46: google.protobuf.Duration } var file_proxy_service_proto_depIdxs = []int32{ - 44, // 0: management.GetMappingUpdateRequest.started_at:type_name -> google.protobuf.Timestamp - 5, // 1: management.GetMappingUpdateRequest.capabilities:type_name -> management.ProxyCapabilities - 14, // 2: management.GetMappingUpdateResponse.mapping:type_name -> management.ProxyMapping - 45, // 3: management.PathTargetOptions.request_timeout:type_name -> google.protobuf.Duration + 45, // 0: management.GetMappingUpdateRequest.started_at:type_name -> google.protobuf.Timestamp + 6, // 1: management.GetMappingUpdateRequest.capabilities:type_name -> management.ProxyCapabilities + 15, // 2: management.GetMappingUpdateResponse.mapping:type_name -> management.ProxyMapping + 46, // 3: management.PathTargetOptions.request_timeout:type_name -> google.protobuf.Duration 1, // 4: management.PathTargetOptions.path_rewrite:type_name -> management.PathRewriteMode - 42, // 5: management.PathTargetOptions.custom_headers:type_name -> management.PathTargetOptions.CustomHeadersEntry - 45, // 6: management.PathTargetOptions.session_idle_timeout:type_name -> google.protobuf.Duration - 9, // 7: management.PathTargetOptions.middlewares:type_name -> management.MiddlewareConfig - 2, // 8: management.MiddlewareConfig.slot:type_name -> management.MiddlewareSlot - 4, // 9: management.MiddlewareConfig.fail_mode:type_name -> management.MiddlewareConfig.FailMode - 45, // 10: management.MiddlewareConfig.timeout:type_name -> google.protobuf.Duration - 8, // 11: management.PathMapping.options:type_name -> management.PathTargetOptions - 11, // 12: management.Authentication.header_auths:type_name -> management.HeaderAuth - 0, // 13: management.ProxyMapping.type:type_name -> management.ProxyMappingUpdateType - 10, // 14: management.ProxyMapping.path:type_name -> management.PathMapping - 12, // 15: management.ProxyMapping.auth:type_name -> management.Authentication - 13, // 16: management.ProxyMapping.access_restrictions:type_name -> management.AccessRestrictions - 17, // 17: management.SendAccessLogRequest.log:type_name -> management.AccessLog - 44, // 18: management.AccessLog.timestamp:type_name -> google.protobuf.Timestamp - 43, // 19: management.AccessLog.metadata:type_name -> management.AccessLog.MetadataEntry - 20, // 20: management.AuthenticateRequest.password:type_name -> management.PasswordRequest - 21, // 21: management.AuthenticateRequest.pin:type_name -> management.PinRequest - 19, // 22: management.AuthenticateRequest.header_auth:type_name -> management.HeaderAuthRequest - 3, // 23: management.SendStatusUpdateRequest.status:type_name -> management.ProxyStatus - 24, // 24: management.SendStatusUpdateRequest.inbound_listener:type_name -> management.ProxyInboundListener - 35, // 25: management.SyncMappingsRequest.init:type_name -> management.SyncMappingsInit - 36, // 26: management.SyncMappingsRequest.ack:type_name -> management.SyncMappingsAck - 44, // 27: management.SyncMappingsInit.started_at:type_name -> google.protobuf.Timestamp - 5, // 28: management.SyncMappingsInit.capabilities:type_name -> management.ProxyCapabilities - 14, // 29: management.SyncMappingsResponse.mapping:type_name -> management.ProxyMapping - 6, // 30: management.ProxyService.GetMappingUpdate:input_type -> management.GetMappingUpdateRequest - 34, // 31: management.ProxyService.SyncMappings:input_type -> management.SyncMappingsRequest - 15, // 32: management.ProxyService.SendAccessLog:input_type -> management.SendAccessLogRequest - 18, // 33: management.ProxyService.Authenticate:input_type -> management.AuthenticateRequest - 23, // 34: management.ProxyService.SendStatusUpdate:input_type -> management.SendStatusUpdateRequest - 26, // 35: management.ProxyService.CreateProxyPeer:input_type -> management.CreateProxyPeerRequest - 28, // 36: management.ProxyService.GetOIDCURL:input_type -> management.GetOIDCURLRequest - 30, // 37: management.ProxyService.ValidateSession:input_type -> management.ValidateSessionRequest - 32, // 38: management.ProxyService.ValidateTunnelPeer:input_type -> management.ValidateTunnelPeerRequest - 38, // 39: management.ProxyService.CheckLLMPolicyLimits:input_type -> management.CheckLLMPolicyLimitsRequest - 40, // 40: management.ProxyService.RecordLLMUsage:input_type -> management.RecordLLMUsageRequest - 7, // 41: management.ProxyService.GetMappingUpdate:output_type -> management.GetMappingUpdateResponse - 37, // 42: management.ProxyService.SyncMappings:output_type -> management.SyncMappingsResponse - 16, // 43: management.ProxyService.SendAccessLog:output_type -> management.SendAccessLogResponse - 22, // 44: management.ProxyService.Authenticate:output_type -> management.AuthenticateResponse - 25, // 45: management.ProxyService.SendStatusUpdate:output_type -> management.SendStatusUpdateResponse - 27, // 46: management.ProxyService.CreateProxyPeer:output_type -> management.CreateProxyPeerResponse - 29, // 47: management.ProxyService.GetOIDCURL:output_type -> management.GetOIDCURLResponse - 31, // 48: management.ProxyService.ValidateSession:output_type -> management.ValidateSessionResponse - 33, // 49: management.ProxyService.ValidateTunnelPeer:output_type -> management.ValidateTunnelPeerResponse - 39, // 50: management.ProxyService.CheckLLMPolicyLimits:output_type -> management.CheckLLMPolicyLimitsResponse - 41, // 51: management.ProxyService.RecordLLMUsage:output_type -> management.RecordLLMUsageResponse - 41, // [41:52] is the sub-list for method output_type - 30, // [30:41] is the sub-list for method input_type - 30, // [30:30] is the sub-list for extension type_name - 30, // [30:30] is the sub-list for extension extendee - 0, // [0:30] is the sub-list for field type_name + 43, // 5: management.PathTargetOptions.custom_headers:type_name -> management.PathTargetOptions.CustomHeadersEntry + 46, // 6: management.PathTargetOptions.session_idle_timeout:type_name -> google.protobuf.Duration + 10, // 7: management.PathTargetOptions.middlewares:type_name -> management.MiddlewareConfig + 3, // 8: management.MiddlewareConfig.slot:type_name -> management.MiddlewareSlot + 5, // 9: management.MiddlewareConfig.fail_mode:type_name -> management.MiddlewareConfig.FailMode + 46, // 10: management.MiddlewareConfig.timeout:type_name -> google.protobuf.Duration + 9, // 11: management.PathMapping.options:type_name -> management.PathTargetOptions + 2, // 12: management.PathMapping.access_action:type_name -> management.TargetAccessAction + 12, // 13: management.Authentication.header_auths:type_name -> management.HeaderAuth + 0, // 14: management.ProxyMapping.type:type_name -> management.ProxyMappingUpdateType + 11, // 15: management.ProxyMapping.path:type_name -> management.PathMapping + 13, // 16: management.ProxyMapping.auth:type_name -> management.Authentication + 14, // 17: management.ProxyMapping.access_restrictions:type_name -> management.AccessRestrictions + 18, // 18: management.SendAccessLogRequest.log:type_name -> management.AccessLog + 45, // 19: management.AccessLog.timestamp:type_name -> google.protobuf.Timestamp + 44, // 20: management.AccessLog.metadata:type_name -> management.AccessLog.MetadataEntry + 21, // 21: management.AuthenticateRequest.password:type_name -> management.PasswordRequest + 22, // 22: management.AuthenticateRequest.pin:type_name -> management.PinRequest + 20, // 23: management.AuthenticateRequest.header_auth:type_name -> management.HeaderAuthRequest + 4, // 24: management.SendStatusUpdateRequest.status:type_name -> management.ProxyStatus + 25, // 25: management.SendStatusUpdateRequest.inbound_listener:type_name -> management.ProxyInboundListener + 36, // 26: management.SyncMappingsRequest.init:type_name -> management.SyncMappingsInit + 37, // 27: management.SyncMappingsRequest.ack:type_name -> management.SyncMappingsAck + 45, // 28: management.SyncMappingsInit.started_at:type_name -> google.protobuf.Timestamp + 6, // 29: management.SyncMappingsInit.capabilities:type_name -> management.ProxyCapabilities + 15, // 30: management.SyncMappingsResponse.mapping:type_name -> management.ProxyMapping + 7, // 31: management.ProxyService.GetMappingUpdate:input_type -> management.GetMappingUpdateRequest + 35, // 32: management.ProxyService.SyncMappings:input_type -> management.SyncMappingsRequest + 16, // 33: management.ProxyService.SendAccessLog:input_type -> management.SendAccessLogRequest + 19, // 34: management.ProxyService.Authenticate:input_type -> management.AuthenticateRequest + 24, // 35: management.ProxyService.SendStatusUpdate:input_type -> management.SendStatusUpdateRequest + 27, // 36: management.ProxyService.CreateProxyPeer:input_type -> management.CreateProxyPeerRequest + 29, // 37: management.ProxyService.GetOIDCURL:input_type -> management.GetOIDCURLRequest + 31, // 38: management.ProxyService.ValidateSession:input_type -> management.ValidateSessionRequest + 33, // 39: management.ProxyService.ValidateTunnelPeer:input_type -> management.ValidateTunnelPeerRequest + 39, // 40: management.ProxyService.CheckLLMPolicyLimits:input_type -> management.CheckLLMPolicyLimitsRequest + 41, // 41: management.ProxyService.RecordLLMUsage:input_type -> management.RecordLLMUsageRequest + 8, // 42: management.ProxyService.GetMappingUpdate:output_type -> management.GetMappingUpdateResponse + 38, // 43: management.ProxyService.SyncMappings:output_type -> management.SyncMappingsResponse + 17, // 44: management.ProxyService.SendAccessLog:output_type -> management.SendAccessLogResponse + 23, // 45: management.ProxyService.Authenticate:output_type -> management.AuthenticateResponse + 26, // 46: management.ProxyService.SendStatusUpdate:output_type -> management.SendStatusUpdateResponse + 28, // 47: management.ProxyService.CreateProxyPeer:output_type -> management.CreateProxyPeerResponse + 30, // 48: management.ProxyService.GetOIDCURL:output_type -> management.GetOIDCURLResponse + 32, // 49: management.ProxyService.ValidateSession:output_type -> management.ValidateSessionResponse + 34, // 50: management.ProxyService.ValidateTunnelPeer:output_type -> management.ValidateTunnelPeerResponse + 40, // 51: management.ProxyService.CheckLLMPolicyLimits:output_type -> management.CheckLLMPolicyLimitsResponse + 42, // 52: management.ProxyService.RecordLLMUsage:output_type -> management.RecordLLMUsageResponse + 42, // [42:53] is the sub-list for method output_type + 31, // [31:42] is the sub-list for method input_type + 31, // [31:31] is the sub-list for extension type_name + 31, // [31:31] is the sub-list for extension extendee + 0, // [0:31] is the sub-list for field type_name } func init() { file_proxy_service_proto_init() } @@ -4353,7 +4424,7 @@ func file_proxy_service_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proxy_service_proto_rawDesc, - NumEnums: 5, + NumEnums: 6, NumMessages: 39, NumExtensions: 0, NumServices: 1, diff --git a/shared/management/proto/proxy_service.proto b/shared/management/proto/proxy_service.proto index 7c5fb08eb..d988f8767 100644 --- a/shared/management/proto/proxy_service.proto +++ b/shared/management/proto/proxy_service.proto @@ -105,6 +105,12 @@ enum PathRewriteMode { PATH_REWRITE_PRESERVE = 1; } +enum TargetAccessAction { + TARGET_ACCESS_ACTION_INHERIT = 0; + TARGET_ACCESS_ACTION_BYPASS = 1; + TARGET_ACCESS_ACTION_BLOCK = 2; +} + message PathTargetOptions { bool skip_tls_verify = 1; google.protobuf.Duration request_timeout = 2; @@ -178,6 +184,7 @@ message PathMapping { string path = 1; string target = 2; PathTargetOptions options = 3; + TargetAccessAction access_action = 4; } message HeaderAuth {