Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 4 additions & 9 deletions credentials/tls/certprovider/meshca/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
80 changes: 55 additions & 25 deletions credentials/tls/certprovider/meshca/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
package meshca

import (
"bytes"
"encoding/json"
"errors"
"fmt"
Expand All @@ -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 (
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
Expand Down Expand Up @@ -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())
}
Expand Down
Loading