meshca: Don't use the config proto from grpc-proto (#4056)

This commit is contained in:
Easwar Swaminathan 2020-11-23 09:44:03 -08:00 коммит произвёл GitHub
Родитель 9da74c039b
Коммит 6d0f0110bf
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 197 добавлений и 560 удалений

Просмотреть файл

@ -22,15 +22,13 @@ package meshca
import (
"context"
"encoding/json"
"fmt"
"testing"
"github.com/golang/protobuf/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials/tls/certprovider"
configpb "google.golang.org/grpc/credentials/tls/certprovider/meshca/internal/meshca_experimental"
"google.golang.org/grpc/internal/testutils"
)
@ -69,11 +67,10 @@ func (s) TestBuildSameConfig(t *testing.T) {
// Parse a good config to generate a stable config which will be passed to
// invocations of Build().
inputConfig := makeJSONConfig(t, goodConfigFullySpecified)
builder := newPluginBuilder()
buildableConfig, err := builder.ParseConfig(inputConfig)
buildableConfig, err := builder.ParseConfig(goodConfigFullySpecified)
if err != nil {
t.Fatalf("builder.ParseConfig(%q) failed: %v", inputConfig, err)
t.Fatalf("builder.ParseConfig(%q) failed: %v", goodConfigFullySpecified, err)
}
// Create multiple providers with the same config. All these providers must
@ -143,9 +140,7 @@ func (s) TestBuildDifferentConfig(t *testing.T) {
for i := 0; i < cnt; i++ {
// Copy the good test config and modify the serverURI to make sure that
// a new provider is created for the config.
cfg := proto.Clone(goodConfigFullySpecified).(*configpb.GoogleMeshCaConfig)
cfg.Server.GrpcServices[0].GetGoogleGrpc().TargetUri = fmt.Sprintf("test-mesh-ca:%d", i)
inputConfig := makeJSONConfig(t, cfg)
inputConfig := json.RawMessage(fmt.Sprintf(goodConfigFormatStr, fmt.Sprintf("test-mesh-ca:%d", i)))
buildableConfig, err := builder.ParseConfig(inputConfig)
if err != nil {
t.Fatalf("builder.ParseConfig(%q) failed: %v", inputConfig, err)

Просмотреть файл

@ -21,7 +21,6 @@
package meshca
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@ -33,11 +32,11 @@ import (
"time"
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/ptypes"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/grpc/credentials/sts"
configpb "google.golang.org/grpc/credentials/tls/certprovider/meshca/internal/meshca_experimental"
)
const (
@ -46,12 +45,12 @@ const (
mdsRequestTimeout = 5 * time.Second
// The following are default values used in the interaction with MeshCA.
defaultMeshCaEndpoint = "meshca.googleapis.com"
defaultCallTimeout = 10 * time.Second
defaultCertLifetime = 24 * time.Hour
defaultCertGraceTime = 12 * time.Hour
defaultKeyTypeRSA = "RSA"
defaultKeySize = 2048
defaultMeshCaEndpoint = "meshca.googleapis.com"
defaultCallTimeout = 10 * time.Second
defaultCertLifetimeSecs = 86400 // 24h in seconds
defaultCertGraceTimeSecs = 43200 // 12h in seconds
defaultKeyTypeRSA = "RSA"
defaultKeySize = 2048
// The following are default values used in the interaction with STS or
// Secure Token Service, which is used to exchange the JWT token for an
@ -80,6 +79,12 @@ type pluginConfig struct {
location string
}
// Type of key to be embedded in CSRs sent to the MeshCA.
const (
keyTypeUnknown = 0
keyTypeRSA = 1
)
// pluginConfigFromJSON parses the provided config in JSON.
//
// For certain values missing in the config, we use default values defined at
@ -89,18 +94,49 @@ type pluginConfig struct {
// GKE Metadata server and try to infer these values. If this attempt does not
// succeed, we let those fields have empty values.
func pluginConfigFromJSON(data json.RawMessage) (*pluginConfig, error) {
cfgProto := &configpb.GoogleMeshCaConfig{}
m := jsonpb.Unmarshaler{AllowUnknownFields: true}
if err := m.Unmarshal(bytes.NewReader(data), cfgProto); err != nil {
// This anonymous struct corresponds to the expected JSON config.
cfgJSON := &struct {
Server json.RawMessage `json:"server,omitempty"` // Expect a v3corepb.ApiConfigSource
CertificateLifetime json.RawMessage `json:"certificate_lifetime,omitempty"` // Expect a durationpb.Duration
RenewalGracePeriod json.RawMessage `json:"renewal_grace_period,omitempty"` // Expect a durationpb.Duration
KeyType int `json:"key_type,omitempty"`
KeySize int `json:"key_size,omitempty"`
Location string `json:"location,omitempty"`
}{}
if err := json.Unmarshal(data, cfgJSON); err != nil {
return nil, fmt.Errorf("meshca: failed to unmarshal config: %v", err)
}
if api := cfgProto.GetServer().GetApiType(); api != v3corepb.ApiConfigSource_GRPC {
// Further unmarshal fields represented as json.RawMessage in the above
// anonymous struct, and use default values if not specified.
server := &v3corepb.ApiConfigSource{}
if cfgJSON.Server != nil {
if err := protojson.Unmarshal(cfgJSON.Server, server); err != nil {
return nil, fmt.Errorf("meshca: protojson.Unmarshal(%+v) failed: %v", cfgJSON.Server, err)
}
}
certLifetime := &durationpb.Duration{Seconds: defaultCertLifetimeSecs}
if cfgJSON.CertificateLifetime != nil {
if err := protojson.Unmarshal(cfgJSON.CertificateLifetime, certLifetime); err != nil {
return nil, fmt.Errorf("meshca: protojson.Unmarshal(%+v) failed: %v", cfgJSON.CertificateLifetime, err)
}
}
certGraceTime := &durationpb.Duration{Seconds: defaultCertGraceTimeSecs}
if cfgJSON.RenewalGracePeriod != nil {
if err := protojson.Unmarshal(cfgJSON.RenewalGracePeriod, certGraceTime); err != nil {
return nil, fmt.Errorf("meshca: protojson.Unmarshal(%+v) failed: %v", cfgJSON.RenewalGracePeriod, err)
}
}
if api := server.GetApiType(); api != v3corepb.ApiConfigSource_GRPC {
return nil, fmt.Errorf("meshca: server has apiType %s, want %s", api, v3corepb.ApiConfigSource_GRPC)
}
pc := &pluginConfig{}
gs := cfgProto.GetServer().GetGrpcServices()
pc := &pluginConfig{
certLifetime: certLifetime.AsDuration(),
certGraceTime: certGraceTime.AsDuration(),
}
gs := server.GetGrpcServices()
if l := len(gs); l != 1 {
return nil, fmt.Errorf("meshca: number of gRPC services in config is %d, expected 1", l)
}
@ -136,23 +172,17 @@ func pluginConfigFromJSON(data json.RawMessage) (*pluginConfig, error) {
if pc.callTimeout, err = ptypes.Duration(grpcService.GetTimeout()); err != nil {
pc.callTimeout = defaultCallTimeout
}
if pc.certLifetime, err = ptypes.Duration(cfgProto.GetCertificateLifetime()); err != nil {
pc.certLifetime = defaultCertLifetime
}
if pc.certGraceTime, err = ptypes.Duration(cfgProto.GetRenewalGracePeriod()); err != nil {
pc.certGraceTime = defaultCertGraceTime
}
switch cfgProto.GetKeyType() {
case configpb.GoogleMeshCaConfig_KEY_TYPE_UNKNOWN, configpb.GoogleMeshCaConfig_KEY_TYPE_RSA:
switch cfgJSON.KeyType {
case keyTypeUnknown, keyTypeRSA:
pc.keyType = defaultKeyTypeRSA
default:
return nil, fmt.Errorf("meshca: unsupported key type: %s, only support RSA keys", pc.keyType)
}
pc.keySize = int(cfgProto.GetKeySize())
pc.keySize = cfgJSON.KeySize
if pc.keySize == 0 {
pc.keySize = defaultKeySize
}
pc.location = cfgProto.GetLocation()
pc.location = cfgJSON.Location
if pc.location == "" {
pc.location = readZoneFunc(makeHTTPDoer())
}

Просмотреть файл

@ -30,12 +30,8 @@ import (
"strings"
"testing"
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
"github.com/golang/protobuf/jsonpb"
durationpb "github.com/golang/protobuf/ptypes/duration"
"github.com/google/go-cmp/cmp"
configpb "google.golang.org/grpc/credentials/tls/certprovider/meshca/internal/meshca_experimental"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/testutils"
)
@ -55,84 +51,66 @@ func Test(t *testing.T) {
}
var (
goodConfigFullySpecified = &configpb.GoogleMeshCaConfig{
Server: &v3corepb.ApiConfigSource{
ApiType: v3corepb.ApiConfigSource_GRPC,
GrpcServices: []*v3corepb.GrpcService{
goodConfigFormatStr = `
{
"server": {
"api_type": 2,
"grpc_services": [
{
TargetSpecifier: &v3corepb.GrpcService_GoogleGrpc_{
GoogleGrpc: &v3corepb.GrpcService_GoogleGrpc{
TargetUri: "test-meshca",
CallCredentials: []*v3corepb.GrpcService_GoogleGrpc_CallCredentials{
// This call creds should be ignored.
{
CredentialSpecifier: &v3corepb.GrpcService_GoogleGrpc_CallCredentials_AccessToken{},
},
{
CredentialSpecifier: &v3corepb.GrpcService_GoogleGrpc_CallCredentials_StsService_{
StsService: &v3corepb.GrpcService_GoogleGrpc_CallCredentials_StsService{
TokenExchangeServiceUri: "http://test-sts",
Resource: "test-resource",
Audience: "test-audience",
Scope: "test-scope",
RequestedTokenType: "test-requested-token-type",
SubjectTokenPath: "test-subject-token-path",
SubjectTokenType: "test-subject-token-type",
ActorTokenPath: "test-actor-token-path",
ActorTokenType: "test-actor-token-type",
},
},
},
"googleGrpc": {
"target_uri": %q,
"call_credentials": [
{
"access_token": "foo"
},
},
{
"sts_service": {
"token_exchange_service_uri": "http://test-sts",
"resource": "test-resource",
"audience": "test-audience",
"scope": "test-scope",
"requested_token_type": "test-requested-token-type",
"subject_token_path": "test-subject-token-path",
"subject_token_type": "test-subject-token-type",
"actor_token_path": "test-actor-token-path",
"actor_token_type": "test-actor-token-type"
}
}
]
},
Timeout: &durationpb.Duration{Seconds: 10}, // 10s
},
},
"timeout": "10s"
}
]
},
CertificateLifetime: &durationpb.Duration{Seconds: 86400}, // 1d
RenewalGracePeriod: &durationpb.Duration{Seconds: 43200}, //12h
KeyType: configpb.GoogleMeshCaConfig_KEY_TYPE_RSA,
KeySize: uint32(2048),
Location: "us-west1-b",
}
goodConfigWithDefaults = &configpb.GoogleMeshCaConfig{
Server: &v3corepb.ApiConfigSource{
ApiType: v3corepb.ApiConfigSource_GRPC,
GrpcServices: []*v3corepb.GrpcService{
"certificate_lifetime": "86400s",
"renewal_grace_period": "43200s",
"key_type": 1,
"key_size": 2048,
"location": "us-west1-b"
}`
goodConfigWithDefaults = json.RawMessage(`
{
"server": {
"api_type": 2,
"grpc_services": [
{
TargetSpecifier: &v3corepb.GrpcService_GoogleGrpc_{
GoogleGrpc: &v3corepb.GrpcService_GoogleGrpc{
CallCredentials: []*v3corepb.GrpcService_GoogleGrpc_CallCredentials{
{
CredentialSpecifier: &v3corepb.GrpcService_GoogleGrpc_CallCredentials_StsService_{
StsService: &v3corepb.GrpcService_GoogleGrpc_CallCredentials_StsService{
SubjectTokenPath: "test-subject-token-path",
},
},
},
},
},
"googleGrpc": {
"call_credentials": [
{
"sts_service": {
"subject_token_path": "test-subject-token-path"
}
}
]
},
},
},
},
}
"timeout": "10s"
}
]
}
}`)
)
// makeJSONConfig marshals the provided config proto into JSON. This makes it
// possible for tests to specify the config in proto form, which is much easier
// than specifying the config in JSON form.
func makeJSONConfig(t *testing.T, cfg *configpb.GoogleMeshCaConfig) json.RawMessage {
t.Helper()
b := &bytes.Buffer{}
m := &jsonpb.Marshaler{EnumsAsInts: true}
if err := m.Marshal(b, cfg); err != nil {
t.Fatalf("jsonpb.Marshal(%+v) failed: %v", cfg, err)
}
return json.RawMessage(b.Bytes())
}
var goodConfigFullySpecified = json.RawMessage(fmt.Sprintf(goodConfigFormatStr, "test-meshca"))
// verifyReceivedRequest reads the HTTP request received by the fake client
// (exposed through a channel), and verifies that it matches the expected
@ -157,23 +135,21 @@ func verifyReceivedRequest(fc *testutils.FakeHTTPClient, wantURI string) error {
// TestParseConfigSuccessFullySpecified tests the case where the config is fully
// specified and no defaults are required.
func (s) TestParseConfigSuccessFullySpecified(t *testing.T) {
inputConfig := makeJSONConfig(t, goodConfigFullySpecified)
wantConfig := "test-meshca:http://test-sts:test-resource:test-audience:test-scope:test-requested-token-type:test-subject-token-path:test-subject-token-type:test-actor-token-path:test-actor-token-type:10s:24h0m0s:12h0m0s:RSA:2048:us-west1-b"
cfg, err := pluginConfigFromJSON(inputConfig)
cfg, err := pluginConfigFromJSON(goodConfigFullySpecified)
if err != nil {
t.Fatalf("pluginConfigFromJSON(%q) failed: %v", inputConfig, err)
t.Fatalf("pluginConfigFromJSON(%q) failed: %v", goodConfigFullySpecified, err)
}
gotConfig := cfg.canonical()
if diff := cmp.Diff(wantConfig, string(gotConfig)); diff != "" {
t.Errorf("pluginConfigFromJSON(%q) returned config does not match expected (-want +got):\n%s", inputConfig, diff)
t.Errorf("pluginConfigFromJSON(%q) returned config does not match expected (-want +got):\n%s", string(goodConfigFullySpecified), diff)
}
}
// TestParseConfigSuccessWithDefaults tests cases where the config is not fully
// specified, and we end up using some sane defaults.
func (s) TestParseConfigSuccessWithDefaults(t *testing.T) {
inputConfig := makeJSONConfig(t, goodConfigWithDefaults)
wantConfig := fmt.Sprintf("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s",
"meshca.googleapis.com", // Mesh CA Server URI.
"securetoken.googleapis.com", // STS Server URI.
@ -248,13 +224,13 @@ func (s) TestParseConfigSuccessWithDefaults(t *testing.T) {
errCh <- nil
}()
cfg, err := pluginConfigFromJSON(inputConfig)
cfg, err := pluginConfigFromJSON(goodConfigWithDefaults)
if err != nil {
t.Fatalf("pluginConfigFromJSON(%q) failed: %v", inputConfig, err)
t.Fatalf("pluginConfigFromJSON(%q) failed: %v", goodConfigWithDefaults, err)
}
gotConfig := cfg.canonical()
if diff := cmp.Diff(wantConfig, string(gotConfig)); diff != "" {
t.Errorf("builder.ParseConfig(%q) returned config does not match expected (-want +got):\n%s", inputConfig, diff)
t.Errorf("builder.ParseConfig(%q) returned config does not match expected (-want +got):\n%s", goodConfigWithDefaults, diff)
}
if err := <-errCh; err != nil {
@ -277,113 +253,109 @@ func (s) TestParseConfigFailureCases(t *testing.T) {
},
{
desc: "bad apiType",
inputConfig: makeJSONConfig(t, &configpb.GoogleMeshCaConfig{
Server: &v3corepb.ApiConfigSource{
ApiType: v3corepb.ApiConfigSource_REST,
},
}),
inputConfig: json.RawMessage(`
{
"server": {
"api_type": 1
}
}`),
wantErr: "server has apiType REST, want GRPC",
},
{
desc: "no grpc services",
inputConfig: makeJSONConfig(t, &configpb.GoogleMeshCaConfig{
Server: &v3corepb.ApiConfigSource{
ApiType: v3corepb.ApiConfigSource_GRPC,
},
}),
inputConfig: json.RawMessage(`
{
"server": {
"api_type": 2
}
}`),
wantErr: "number of gRPC services in config is 0, expected 1",
},
{
desc: "too many grpc services",
inputConfig: makeJSONConfig(t, &configpb.GoogleMeshCaConfig{
Server: &v3corepb.ApiConfigSource{
ApiType: v3corepb.ApiConfigSource_GRPC,
GrpcServices: []*v3corepb.GrpcService{nil, nil},
},
}),
inputConfig: json.RawMessage(`
{
"server": {
"api_type": 2,
"grpc_services": [{}, {}]
}
}`),
wantErr: "number of gRPC services in config is 2, expected 1",
},
{
desc: "missing google grpc service",
inputConfig: makeJSONConfig(t, &configpb.GoogleMeshCaConfig{
Server: &v3corepb.ApiConfigSource{
ApiType: v3corepb.ApiConfigSource_GRPC,
GrpcServices: []*v3corepb.GrpcService{
inputConfig: json.RawMessage(`
{
"server": {
"api_type": 2,
"grpc_services": [
{
TargetSpecifier: &v3corepb.GrpcService_EnvoyGrpc_{
EnvoyGrpc: &v3corepb.GrpcService_EnvoyGrpc{
ClusterName: "foo",
},
},
},
},
},
}),
"envoyGrpc": {}
}
]
}
}`),
wantErr: "missing google gRPC service in config",
},
{
desc: "missing call credentials",
inputConfig: makeJSONConfig(t, &configpb.GoogleMeshCaConfig{
Server: &v3corepb.ApiConfigSource{
ApiType: v3corepb.ApiConfigSource_GRPC,
GrpcServices: []*v3corepb.GrpcService{
inputConfig: json.RawMessage(`
{
"server": {
"api_type": 2,
"grpc_services": [
{
TargetSpecifier: &v3corepb.GrpcService_GoogleGrpc_{
GoogleGrpc: &v3corepb.GrpcService_GoogleGrpc{
TargetUri: "foo",
},
},
},
},
},
}),
"googleGrpc": {
"target_uri": "foo"
}
}
]
}
}`),
wantErr: "missing call credentials in config",
},
{
desc: "missing STS call credentials",
inputConfig: makeJSONConfig(t, &configpb.GoogleMeshCaConfig{
Server: &v3corepb.ApiConfigSource{
ApiType: v3corepb.ApiConfigSource_GRPC,
GrpcServices: []*v3corepb.GrpcService{
inputConfig: json.RawMessage(`
{
"server": {
"api_type": 2,
"grpc_services": [
{
TargetSpecifier: &v3corepb.GrpcService_GoogleGrpc_{
GoogleGrpc: &v3corepb.GrpcService_GoogleGrpc{
TargetUri: "foo",
CallCredentials: []*v3corepb.GrpcService_GoogleGrpc_CallCredentials{
{
CredentialSpecifier: &v3corepb.GrpcService_GoogleGrpc_CallCredentials_AccessToken{},
},
},
},
},
},
},
},
}),
"googleGrpc": {
"target_uri": "foo",
"call_credentials": [
{
"access_token": "foo"
}
]
}
}
]
}
}`),
wantErr: "missing STS call credentials in config",
},
{
desc: "with no defaults",
inputConfig: makeJSONConfig(t, &configpb.GoogleMeshCaConfig{
Server: &v3corepb.ApiConfigSource{
ApiType: v3corepb.ApiConfigSource_GRPC,
GrpcServices: []*v3corepb.GrpcService{
inputConfig: json.RawMessage(`
{
"server": {
"api_type": 2,
"grpc_services": [
{
TargetSpecifier: &v3corepb.GrpcService_GoogleGrpc_{
GoogleGrpc: &v3corepb.GrpcService_GoogleGrpc{
CallCredentials: []*v3corepb.GrpcService_GoogleGrpc_CallCredentials{
{
CredentialSpecifier: &v3corepb.GrpcService_GoogleGrpc_CallCredentials_StsService_{
StsService: &v3corepb.GrpcService_GoogleGrpc_CallCredentials_StsService{},
},
},
},
},
},
},
},
},
}),
"googleGrpc": {
"target_uri": "foo",
"call_credentials": [
{
"sts_service": {}
}
]
}
}
]
}
}`),
wantErr: "missing subjectTokenPath in STS call credentials config",
},
}

Просмотреть файл

@ -1,331 +0,0 @@
// Copyright 2020 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.14.0
// source: grpc/tls/provider/meshca/experimental/config.proto
// NOTE: This proto will very likely move to a different namespace and a
// different git repo in the future.
package meshca_experimental
import (
v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
durationpb "google.golang.org/protobuf/types/known/durationpb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// Type of key to be embedded in CSRs sent to the MeshCA.
type GoogleMeshCaConfig_KeyType int32
const (
GoogleMeshCaConfig_KEY_TYPE_UNKNOWN GoogleMeshCaConfig_KeyType = 0
GoogleMeshCaConfig_KEY_TYPE_RSA GoogleMeshCaConfig_KeyType = 1
)
// Enum value maps for GoogleMeshCaConfig_KeyType.
var (
GoogleMeshCaConfig_KeyType_name = map[int32]string{
0: "KEY_TYPE_UNKNOWN",
1: "KEY_TYPE_RSA",
}
GoogleMeshCaConfig_KeyType_value = map[string]int32{
"KEY_TYPE_UNKNOWN": 0,
"KEY_TYPE_RSA": 1,
}
)
func (x GoogleMeshCaConfig_KeyType) Enum() *GoogleMeshCaConfig_KeyType {
p := new(GoogleMeshCaConfig_KeyType)
*p = x
return p
}
func (x GoogleMeshCaConfig_KeyType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (GoogleMeshCaConfig_KeyType) Descriptor() protoreflect.EnumDescriptor {
return file_grpc_tls_provider_meshca_experimental_config_proto_enumTypes[0].Descriptor()
}
func (GoogleMeshCaConfig_KeyType) Type() protoreflect.EnumType {
return &file_grpc_tls_provider_meshca_experimental_config_proto_enumTypes[0]
}
func (x GoogleMeshCaConfig_KeyType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use GoogleMeshCaConfig_KeyType.Descriptor instead.
func (GoogleMeshCaConfig_KeyType) EnumDescriptor() ([]byte, []int) {
return file_grpc_tls_provider_meshca_experimental_config_proto_rawDescGZIP(), []int{0, 0}
}
// GoogleMeshCaConfig contains all configuration parameters required by the
// MeshCA CertificateProvider plugin implementation.
type GoogleMeshCaConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// GoogleMeshCA server endpoint to get CSRs signed via the *CreateCertificate*
// unary call. This must have :ref:`api_type
// <envoy_api_field_config.core.v3.ApiConfigSource.api_type>` :ref:`GRPC
// <envoy_api_enum_value_config.core.v3.ApiConfigSource.ApiType.GRPC>`.
// STS based call credentials need to be supplied in :ref:`call_credentials
// <envoy_api_field_config.core.v3.GrpcService.GoogleGrpc.call_credentials>`.
//
// If :ref:`timeout envoy_api_field_config.core.v3.GrpcService.timeout` is
// left unspecified, a default value of 10s will be used.
Server *v3.ApiConfigSource `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
// Certificate lifetime to request in CSRs sent to the MeshCA.
//
// A default value of 24h will be used if left unspecified.
CertificateLifetime *durationpb.Duration `protobuf:"bytes,2,opt,name=certificate_lifetime,json=certificateLifetime,proto3" json:"certificate_lifetime,omitempty"`
// How long before certificate expiration should the certificate be renewed.
//
// A default value of 12h will be used if left unspecified.
RenewalGracePeriod *durationpb.Duration `protobuf:"bytes,3,opt,name=renewal_grace_period,json=renewalGracePeriod,proto3" json:"renewal_grace_period,omitempty"`
// Type of key.
//
// RSA keys will be used if left unspecified.
KeyType GoogleMeshCaConfig_KeyType `protobuf:"varint,4,opt,name=key_type,json=keyType,proto3,enum=grpc.tls.provider.meshca.experimental.GoogleMeshCaConfig_KeyType" json:"key_type,omitempty"`
// Size of the key in bits.
//
// 2048 bit keys will be used if left unspecified.
KeySize uint32 `protobuf:"varint,5,opt,name=key_size,json=keySize,proto3" json:"key_size,omitempty"`
// GCE location (region/zone) where the workload is located.
//
// GCE/GKE Metadata Server will be contacted if left unspecified.
Location string `protobuf:"bytes,6,opt,name=location,proto3" json:"location,omitempty"`
}
func (x *GoogleMeshCaConfig) Reset() {
*x = GoogleMeshCaConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_grpc_tls_provider_meshca_experimental_config_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GoogleMeshCaConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GoogleMeshCaConfig) ProtoMessage() {}
func (x *GoogleMeshCaConfig) ProtoReflect() protoreflect.Message {
mi := &file_grpc_tls_provider_meshca_experimental_config_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GoogleMeshCaConfig.ProtoReflect.Descriptor instead.
func (*GoogleMeshCaConfig) Descriptor() ([]byte, []int) {
return file_grpc_tls_provider_meshca_experimental_config_proto_rawDescGZIP(), []int{0}
}
func (x *GoogleMeshCaConfig) GetServer() *v3.ApiConfigSource {
if x != nil {
return x.Server
}
return nil
}
func (x *GoogleMeshCaConfig) GetCertificateLifetime() *durationpb.Duration {
if x != nil {
return x.CertificateLifetime
}
return nil
}
func (x *GoogleMeshCaConfig) GetRenewalGracePeriod() *durationpb.Duration {
if x != nil {
return x.RenewalGracePeriod
}
return nil
}
func (x *GoogleMeshCaConfig) GetKeyType() GoogleMeshCaConfig_KeyType {
if x != nil {
return x.KeyType
}
return GoogleMeshCaConfig_KEY_TYPE_UNKNOWN
}
func (x *GoogleMeshCaConfig) GetKeySize() uint32 {
if x != nil {
return x.KeySize
}
return 0
}
func (x *GoogleMeshCaConfig) GetLocation() string {
if x != nil {
return x.Location
}
return ""
}
var File_grpc_tls_provider_meshca_experimental_config_proto protoreflect.FileDescriptor
var file_grpc_tls_provider_meshca_experimental_config_proto_rawDesc = []byte{
0x0a, 0x32, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69,
0x64, 0x65, 0x72, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x63, 0x61, 0x2f, 0x65, 0x78, 0x70, 0x65, 0x72,
0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x25, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x63, 0x61, 0x2e, 0x65,
0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x1a, 0x28, 0x65, 0x6e, 0x76,
0x6f, 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76,
0x33, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x03, 0x0a, 0x12, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x4d, 0x65, 0x73, 0x68, 0x43, 0x61, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3d, 0x0a, 0x06,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65,
0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x63, 0x6f, 0x72, 0x65,
0x2e, 0x76, 0x33, 0x2e, 0x41, 0x70, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x4c, 0x0a, 0x14, 0x63,
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x66, 0x65, 0x74,
0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
0x65, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x14, 0x72, 0x65, 0x6e,
0x65, 0x77, 0x61, 0x6c, 0x5f, 0x67, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f,
0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x52, 0x12, 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x47, 0x72, 0x61, 0x63, 0x65,
0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x12, 0x5c, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79,
0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x41, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
0x74, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x73,
0x68, 0x63, 0x61, 0x2e, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c,
0x2e, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x68, 0x43, 0x61, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x2e, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x6b, 0x65, 0x79,
0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x69, 0x7a, 0x65,
0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x53, 0x69, 0x7a, 0x65, 0x12,
0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x31, 0x0a, 0x07, 0x4b,
0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x4b, 0x45, 0x59, 0x5f, 0x54, 0x59,
0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c,
0x4b, 0x45, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x53, 0x41, 0x10, 0x01, 0x42, 0x98,
0x01, 0x0a, 0x28, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x63, 0x61, 0x2e, 0x65,
0x78, 0x70, 0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x42, 0x11, 0x4d, 0x65, 0x73,
0x68, 0x43, 0x61, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x5a, 0x57, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e,
0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x61, 0x6c, 0x73, 0x2f, 0x74, 0x6c, 0x73, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x70, 0x72, 0x6f,
0x76, 0x69, 0x64, 0x65, 0x72, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x63, 0x61, 0x2f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x63, 0x61, 0x5f, 0x65, 0x78, 0x70,
0x65, 0x72, 0x69, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_grpc_tls_provider_meshca_experimental_config_proto_rawDescOnce sync.Once
file_grpc_tls_provider_meshca_experimental_config_proto_rawDescData = file_grpc_tls_provider_meshca_experimental_config_proto_rawDesc
)
func file_grpc_tls_provider_meshca_experimental_config_proto_rawDescGZIP() []byte {
file_grpc_tls_provider_meshca_experimental_config_proto_rawDescOnce.Do(func() {
file_grpc_tls_provider_meshca_experimental_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_grpc_tls_provider_meshca_experimental_config_proto_rawDescData)
})
return file_grpc_tls_provider_meshca_experimental_config_proto_rawDescData
}
var file_grpc_tls_provider_meshca_experimental_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_grpc_tls_provider_meshca_experimental_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_grpc_tls_provider_meshca_experimental_config_proto_goTypes = []interface{}{
(GoogleMeshCaConfig_KeyType)(0), // 0: grpc.tls.provider.meshca.experimental.GoogleMeshCaConfig.KeyType
(*GoogleMeshCaConfig)(nil), // 1: grpc.tls.provider.meshca.experimental.GoogleMeshCaConfig
(*v3.ApiConfigSource)(nil), // 2: envoy.config.core.v3.ApiConfigSource
(*durationpb.Duration)(nil), // 3: google.protobuf.Duration
}
var file_grpc_tls_provider_meshca_experimental_config_proto_depIdxs = []int32{
2, // 0: grpc.tls.provider.meshca.experimental.GoogleMeshCaConfig.server:type_name -> envoy.config.core.v3.ApiConfigSource
3, // 1: grpc.tls.provider.meshca.experimental.GoogleMeshCaConfig.certificate_lifetime:type_name -> google.protobuf.Duration
3, // 2: grpc.tls.provider.meshca.experimental.GoogleMeshCaConfig.renewal_grace_period:type_name -> google.protobuf.Duration
0, // 3: grpc.tls.provider.meshca.experimental.GoogleMeshCaConfig.key_type:type_name -> grpc.tls.provider.meshca.experimental.GoogleMeshCaConfig.KeyType
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_grpc_tls_provider_meshca_experimental_config_proto_init() }
func file_grpc_tls_provider_meshca_experimental_config_proto_init() {
if File_grpc_tls_provider_meshca_experimental_config_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_grpc_tls_provider_meshca_experimental_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GoogleMeshCaConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_grpc_tls_provider_meshca_experimental_config_proto_rawDesc,
NumEnums: 1,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_grpc_tls_provider_meshca_experimental_config_proto_goTypes,
DependencyIndexes: file_grpc_tls_provider_meshca_experimental_config_proto_depIdxs,
EnumInfos: file_grpc_tls_provider_meshca_experimental_config_proto_enumTypes,
MessageInfos: file_grpc_tls_provider_meshca_experimental_config_proto_msgTypes,
}.Build()
File_grpc_tls_provider_meshca_experimental_config_proto = out.File
file_grpc_tls_provider_meshca_experimental_config_proto_rawDesc = nil
file_grpc_tls_provider_meshca_experimental_config_proto_goTypes = nil
file_grpc_tls_provider_meshca_experimental_config_proto_depIdxs = nil
}

Просмотреть файл

@ -26,6 +26,7 @@ import (
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
@ -35,11 +36,8 @@ import (
"testing"
"time"
"github.com/golang/protobuf/proto"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/tls/certprovider"
configpb "google.golang.org/grpc/credentials/tls/certprovider/meshca/internal/meshca_experimental"
meshgrpc "google.golang.org/grpc/credentials/tls/certprovider/meshca/internal/v1"
meshpb "google.golang.org/grpc/credentials/tls/certprovider/meshca/internal/v1"
"google.golang.org/grpc/internal/testutils"
@ -298,9 +296,7 @@ func (s) TestCreateCertificate(t *testing.T) {
defer cancel()
// Set the MeshCA targetURI to point to our fake MeshCA.
cfg := proto.Clone(goodConfigFullySpecified).(*configpb.GoogleMeshCaConfig)
cfg.Server.GrpcServices[0].GetGoogleGrpc().TargetUri = addr
inputConfig := makeJSONConfig(t, cfg)
inputConfig := json.RawMessage(fmt.Sprintf(goodConfigFormatStr, addr))
// Lookup MeshCA plugin builder, parse config and start the plugin.
prov, err := certprovider.GetProvider(pluginName, inputConfig, certprovider.BuildOptions{})
@ -341,9 +337,7 @@ func (s) TestCreateCertificateWithBackoff(t *testing.T) {
defer cancel()
// Set the MeshCA targetURI to point to our fake MeshCA.
cfg := proto.Clone(goodConfigFullySpecified).(*configpb.GoogleMeshCaConfig)
cfg.Server.GrpcServices[0].GetGoogleGrpc().TargetUri = addr
inputConfig := makeJSONConfig(t, cfg)
inputConfig := json.RawMessage(fmt.Sprintf(goodConfigFormatStr, addr))
// Lookup MeshCA plugin builder, parse config and start the plugin.
prov, err := certprovider.GetProvider(pluginName, inputConfig, certprovider.BuildOptions{})
@ -397,9 +391,7 @@ func (s) TestCreateCertificateWithRefresh(t *testing.T) {
defer cancel()
// Set the MeshCA targetURI to point to our fake MeshCA.
cfg := proto.Clone(goodConfigFullySpecified).(*configpb.GoogleMeshCaConfig)
cfg.Server.GrpcServices[0].GetGoogleGrpc().TargetUri = addr
inputConfig := makeJSONConfig(t, cfg)
inputConfig := json.RawMessage(fmt.Sprintf(goodConfigFormatStr, addr))
// Lookup MeshCA plugin builder, parse config and start the plugin.
prov, err := certprovider.GetProvider(pluginName, inputConfig, certprovider.BuildOptions{})

Просмотреть файл

@ -45,19 +45,6 @@ mkdir -p ${WORKDIR}/googleapis/google/rpc
echo "curl https://raw.githubusercontent.com/googleapis/googleapis/master/google/rpc/code.proto"
curl --silent https://raw.githubusercontent.com/googleapis/googleapis/master/google/rpc/code.proto > ${WORKDIR}/googleapis/google/rpc/code.proto
# Pull in the following repos to build the MeshCA config proto.
ENVOY_API_REPOS=(
"https://github.com/envoyproxy/data-plane-api"
"https://github.com/cncf/udpa"
"https://github.com/envoyproxy/protoc-gen-validate"
)
for repo in ${ENVOY_API_REPOS[@]}; do
dirname=$(basename ${repo})
mkdir -p ${WORKDIR}/${dirname}
echo "git clone ${repo}"
git clone --quiet ${repo} ${WORKDIR}/${dirname}
done
# Pull in the MeshCA service proto.
mkdir -p ${WORKDIR}/istio/istio/google/security/meshca/v1
echo "curl https://raw.githubusercontent.com/istio/istio/master/security/proto/providers/google/meshca.proto"
@ -84,15 +71,13 @@ SOURCES=(
${WORKDIR}/grpc-proto/grpc/lookup/v1/rls.proto
${WORKDIR}/grpc-proto/grpc/lookup/v1/rls_config.proto
${WORKDIR}/grpc-proto/grpc/service_config/service_config.proto
${WORKDIR}/grpc-proto/grpc/tls/provider/meshca/experimental/config.proto
${WORKDIR}/istio/istio/google/security/meshca/v1/meshca.proto
)
# These options of the form 'Mfoo.proto=bar' instruct the codegen to use an
# import path of 'bar' in the generated code when 'foo.proto' is imported in
# one of the sources.
OPTS=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config,\
Menvoy/config/core/v3/config_source.proto=github.com/envoyproxy/go-control-plane/envoy/config/core/v3
OPTS=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config
for src in ${SOURCES[@]}; do
echo "protoc ${src}"
@ -100,9 +85,6 @@ for src in ${SOURCES[@]}; do
-I"." \
-I${WORKDIR}/grpc-proto \
-I${WORKDIR}/googleapis \
-I${WORKDIR}/data-plane-api \
-I${WORKDIR}/udpa \
-I${WORKDIR}/protoc-gen-validate \
-I${WORKDIR}/istio \
${src}
done
@ -113,9 +95,6 @@ for src in ${LEGACY_SOURCES[@]}; do
-I"." \
-I${WORKDIR}/grpc-proto \
-I${WORKDIR}/googleapis \
-I${WORKDIR}/data-plane-api \
-I${WORKDIR}/udpa \
-I${WORKDIR}/protoc-gen-validate \
-I${WORKDIR}/istio \
${src}
done