google.golang.org/grpc@v1.62.1/internal/xds/rbac/converter.go (about) 1 /* 2 * Copyright 2023 gRPC authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package rbac 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "strings" 23 24 v1xdsudpatypepb "github.com/cncf/xds/go/udpa/type/v1" 25 v3xdsxdstypepb "github.com/cncf/xds/go/xds/type/v3" 26 v3rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" 27 v3auditloggersstreampb "github.com/envoyproxy/go-control-plane/envoy/extensions/rbac/audit_loggers/stream/v3" 28 "google.golang.org/grpc/authz/audit" 29 "google.golang.org/grpc/authz/audit/stdout" 30 "google.golang.org/protobuf/encoding/protojson" 31 "google.golang.org/protobuf/types/known/anypb" 32 "google.golang.org/protobuf/types/known/structpb" 33 ) 34 35 func buildLogger(loggerConfig *v3rbacpb.RBAC_AuditLoggingOptions_AuditLoggerConfig) (audit.Logger, error) { 36 if loggerConfig.GetAuditLogger().GetTypedConfig() == nil { 37 return nil, fmt.Errorf("missing required field: TypedConfig") 38 } 39 customConfig, loggerName, err := getCustomConfig(loggerConfig.AuditLogger.TypedConfig) 40 if err != nil { 41 return nil, err 42 } 43 if loggerName == "" { 44 return nil, fmt.Errorf("field TypedConfig.TypeURL cannot be an empty string") 45 } 46 factory := audit.GetLoggerBuilder(loggerName) 47 if factory == nil { 48 if loggerConfig.IsOptional { 49 return nil, nil 50 } 51 return nil, fmt.Errorf("no builder registered for %v", loggerName) 52 } 53 auditLoggerConfig, err := factory.ParseLoggerConfig(customConfig) 54 if err != nil { 55 return nil, fmt.Errorf("custom config could not be parsed by registered factory. error: %v", err) 56 } 57 auditLogger := factory.Build(auditLoggerConfig) 58 return auditLogger, nil 59 } 60 61 func getCustomConfig(config *anypb.Any) (json.RawMessage, string, error) { 62 any, err := config.UnmarshalNew() 63 if err != nil { 64 return nil, "", err 65 } 66 switch m := any.(type) { 67 case *v1xdsudpatypepb.TypedStruct: 68 return convertCustomConfig(m.TypeUrl, m.Value) 69 case *v3xdsxdstypepb.TypedStruct: 70 return convertCustomConfig(m.TypeUrl, m.Value) 71 case *v3auditloggersstreampb.StdoutAuditLog: 72 return convertStdoutConfig(m) 73 } 74 return nil, "", fmt.Errorf("custom config not implemented for type [%v]", config.GetTypeUrl()) 75 } 76 77 func convertStdoutConfig(config *v3auditloggersstreampb.StdoutAuditLog) (json.RawMessage, string, error) { 78 json, err := protojson.Marshal(config) 79 return json, stdout.Name, err 80 } 81 82 func convertCustomConfig(typeURL string, s *structpb.Struct) (json.RawMessage, string, error) { 83 // The gRPC policy name will be the "type name" part of the value of the 84 // type_url field in the TypedStruct. We get this by using the part after 85 // the last / character. Can assume a valid type_url from the control plane. 86 urls := strings.Split(typeURL, "/") 87 if len(urls) == 0 { 88 return nil, "", fmt.Errorf("error converting custom audit logger %v for %v: typeURL must have a url-like format with the typeName being the value after the last /", typeURL, s) 89 } 90 name := urls[len(urls)-1] 91 92 rawJSON := []byte("{}") 93 var err error 94 if s != nil { 95 rawJSON, err = json.Marshal(s) 96 if err != nil { 97 return nil, "", fmt.Errorf("error converting custom audit logger %v for %v: %v", typeURL, s, err) 98 } 99 } 100 return rawJSON, name, nil 101 }