{"record":{"id":"d76328f9ff4650c2","repo":"grpc/grpc-go","slug":"rbac-principal-header-matcher-for-v-is-scheme-o","errorCode":null,"errorMessage":"rbac: principal header matcher for %v is :scheme or starts with grpc","messagePattern":"rbac: principal header matcher for (.+?) is :scheme or starts with grpc","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/httpfilter/rbac/rbac.go","lineNumber":75,"sourceCode":"// Parsing is the same for the base config and the override config.\nfunc parseConfig(rbacCfg *rpb.RBAC) (httpfilter.FilterConfig, error) {\n\t// All the validation logic described in A41.\n\tfor _, policy := range rbacCfg.GetRules().GetPolicies() {\n\t\t// \"Policy.condition and Policy.checked_condition must cause a\n\t\t// validation failure if present.\" - A41\n\t\tif policy.Condition != nil {\n\t\t\treturn nil, errors.New(\"rbac: Policy.condition is present\")\n\t\t}\n\t\tif policy.CheckedCondition != nil {\n\t\t\treturn nil, errors.New(\"rbac: policy.CheckedCondition is present\")\n\t\t}\n\n\t\t// \"It is also a validation failure if Permission or Principal has a\n\t\t// header matcher for a grpc- prefixed header name or :scheme.\" - A41\n\t\tfor _, principal := range policy.Principals {\n\t\t\tname := principal.GetHeader().GetName()\n\t\t\tif name == \":scheme\" || strings.HasPrefix(name, \"grpc-\") {\n\t\t\t\treturn nil, fmt.Errorf(\"rbac: principal header matcher for %v is :scheme or starts with grpc\", name)\n\t\t\t}\n\t\t}\n\t\tfor _, permission := range policy.Permissions {\n\t\t\tname := permission.GetHeader().GetName()\n\t\t\tif name == \":scheme\" || strings.HasPrefix(name, \"grpc-\") {\n\t\t\t\treturn nil, fmt.Errorf(\"rbac: permission header matcher for %v is :scheme or starts with grpc\", name)\n\t\t\t}\n\t\t}\n\t}\n\n\t// \"Envoy aliases :authority and Host in its header map implementation, so\n\t// they should be treated equivalent for the RBAC matchers; there must be no\n\t// behavior change depending on which of the two header names is used in the\n\t// RBAC policy.\" - A41. Loop through config's principals and policies, change\n\t// any header matcher with value \"host\" to :authority\", as that is what\n\t// grpc-go shifts both headers to in transport layer.\n\tfor _, policy := range rbacCfg.GetRules().GetPolicies() {\n\t\tfor _, principal := range policy.Principals {","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/httpfilter/rbac/rbac.go#L57-L93","documentation":"Per gRFC A41, an RBAC policy is invalid if any Principal has a header matcher whose name is ':scheme' or starts with 'grpc-' (rbac.go:74). grpc-go controls these headers internally, so matching on them is unsupported and the policy is rejected at config time.","triggerScenarios":"An RBAC rules policy contains a Principal.Identifier.header with name ':scheme' or a name beginning with 'grpc-'.","commonSituations":"RBAC config ported from Envoy/HTTP semantics that matched scheme or grpc metadata headers; generic header-based authz policy; template that lists all headers as matchers.","solutions":["Remove header matchers on ':scheme' and any 'grpc-'-prefixed header from RBAC principals (and permissions, which are checked similarly).","If you need to gate on gRPC metadata, match on the corresponding well-known metadata via supported mechanisms rather than raw header names.","Validate generated RBAC policies against the A41 restrictions before publishing."],"exampleFix":"// before\npolicy.Principals = append(policy.Principals, &rbacpb.Principal{Identifier: &rbacpb.Principal_Header{Header: &routepb.HeaderMatcher{Name: \":scheme\"}}})\n\n// after: drop the unsupported header matcher (or match a supported header)\npolicy.Principals = append(policy.Principals, &rbacpb.Principal{Identifier: &rbacpb.Principal_Header{Header: &routepb.HeaderMatcher{Name: \":authority\"}}})","handlingStrategy":"validation","validationCode":"for _, p := range rbacCfg.GetRules().GetPolicies() {\n    for _, pr := range p.GetPrincipals() {\n        n := pr.GetHeader().GetName()\n        if n == \":scheme\" || strings.HasPrefix(n, \"grpc-\") {\n            return fmt.Errorf(\"unsupported header matcher: %s\", n)\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"fc, err := builder{}.ParseFilterConfig(anyMsg)\nif err != nil {\n    return err\n}","preventionTips":["Do not match on ':scheme' or 'grpc-'-prefixed headers in RBAC policies.","Validate generated RBAC against gRFC A41 restrictions before publishing.","Prefer supported matchers (e.g. ':authority') for header-based authz."],"tags":["rbac","grpc","xds","config","validation","go"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}