1
// Copyright Istio Authors
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
7
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
22
privateca "cloud.google.com/go/security/privateca/apiv1"
23
privatecapb "cloud.google.com/go/security/privateca/apiv1/privatecapb"
24
"google.golang.org/api/option"
25
"google.golang.org/protobuf/types/known/durationpb"
26
"k8s.io/apimachinery/pkg/util/rand"
28
"istio.io/istio/pkg/log"
29
"istio.io/istio/pkg/security"
30
"istio.io/istio/pkg/util/sets"
33
var googleCASClientLog = log.RegisterScope("googlecas", "Google CAS client debugging")
35
// GoogleCASClient: Agent side plugin for Google CAS
36
type GoogleCASClient struct {
38
caClient *privateca.CertificateAuthorityClient
41
// NewGoogleCASClient create a CA client for Google CAS.
42
func NewGoogleCASClient(capool string, options ...option.ClientOption) (security.Client, error) {
43
caClient := &GoogleCASClient{caSigner: capool}
44
ctx := context.Background()
47
caClient.caClient, err = privateca.NewCertificateAuthorityClient(ctx, options...)
49
googleCASClientLog.Errorf("unable to initialize google cas caclient: %v", err)
52
googleCASClientLog.Debugf("Intitialized Google CAS plugin with endpoint: %v", capool)
56
func (r *GoogleCASClient) createCertReq(name string, csrPEM []byte, lifetime time.Duration) *privatecapb.CreateCertificateRequest {
59
// We use Certificate_Config option to ensure that we only request a certificate with CAS supported extensions/usages.
60
// CAS uses the PEM encoded CSR only for its public key and infers the certificate SAN (identity) of the workload through SPIFFE identity reflection
61
creq := &privatecapb.CreateCertificateRequest{
64
Certificate: &privatecapb.Certificate{
65
Lifetime: durationpb.New(lifetime),
66
CertificateConfig: &privatecapb.Certificate_Config{
67
Config: &privatecapb.CertificateConfig{
68
SubjectConfig: &privatecapb.CertificateConfig_SubjectConfig{
69
Subject: &privatecapb.Subject{},
71
X509Config: &privatecapb.X509Parameters{
72
KeyUsage: &privatecapb.KeyUsage{
73
BaseKeyUsage: &privatecapb.KeyUsage_KeyUsageOptions{
74
DigitalSignature: true,
75
KeyEncipherment: true,
77
ExtendedKeyUsage: &privatecapb.KeyUsage_ExtendedKeyUsageOptions{
82
CaOptions: &privatecapb.X509Parameters_CaOptions{
86
PublicKey: &privatecapb.PublicKey{
87
Format: privatecapb.PublicKey_PEM,
92
SubjectMode: privatecapb.SubjectRequestMode_REFLECTED_SPIFFE,
98
// CSR Sign calls Google CAS to sign a CSR.
99
func (r *GoogleCASClient) CSRSign(csrPEM []byte, certValidTTLInSec int64) ([]string, error) {
100
certChain := []string{}
102
name := fmt.Sprintf("csr-workload-%s", rand.String(8))
103
creq := r.createCertReq(name, csrPEM, time.Duration(certValidTTLInSec)*time.Second)
105
ctx := context.Background()
107
cresp, err := r.caClient.CreateCertificate(ctx, creq)
109
googleCASClientLog.Errorf("unable to create certificate: %v", err)
110
return []string{}, err
112
certChain = append(certChain, cresp.GetPemCertificate())
113
certChain = append(certChain, cresp.GetPemCertificateChain()...)
114
return certChain, nil
117
// GetRootCertBundle: Get CA certs of the pool from Google CAS API endpoint
118
func (r *GoogleCASClient) GetRootCertBundle() ([]string, error) {
119
rootCertSet := sets.New[string]()
121
ctx := context.Background()
123
req := &privatecapb.FetchCaCertsRequest{
126
resp, err := r.caClient.FetchCaCerts(ctx, req)
128
googleCASClientLog.Errorf("error when getting root-certs from CAS pool: %v", err)
131
for _, certChain := range resp.CaCerts {
132
certs := certChain.Certificates
133
rootCert := certs[len(certs)-1]
134
rootCertSet.Insert(rootCert)
137
return sets.SortedList(rootCertSet), nil
140
func (r *GoogleCASClient) Close() {
141
_ = r.caClient.Close()