istio

Форк
0
169 строк · 4.8 Кб
1
// Copyright Istio Authors
2
//
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
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
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.
14

15
package caclient
16

17
import (
18
	"context"
19
	"errors"
20
	"fmt"
21

22
	"google.golang.org/grpc"
23
	"google.golang.org/grpc/metadata"
24
	"google.golang.org/protobuf/types/known/structpb"
25

26
	pb "istio.io/api/security/v1alpha1"
27
	istiogrpc "istio.io/istio/pilot/pkg/grpc"
28
	"istio.io/istio/pkg/log"
29
	"istio.io/istio/pkg/security"
30
	"istio.io/istio/security/pkg/nodeagent/caclient"
31
)
32

33
const (
34
	bearerTokenPrefix = "Bearer "
35
)
36

37
var citadelClientLog = log.RegisterScope("citadelclient", "citadel client debugging")
38

39
type CitadelClient struct {
40
	// It means enable tls connection to Citadel if this is not nil.
41
	tlsOpts  *TLSOptions
42
	client   pb.IstioCertificateServiceClient
43
	conn     *grpc.ClientConn
44
	provider *caclient.TokenProvider
45
	opts     *security.Options
46
}
47

48
type TLSOptions struct {
49
	RootCert string
50
	Key      string
51
	Cert     string
52
}
53

54
// NewCitadelClient create a CA client for Citadel.
55
func NewCitadelClient(opts *security.Options, tlsOpts *TLSOptions) (*CitadelClient, error) {
56
	c := &CitadelClient{
57
		tlsOpts:  tlsOpts,
58
		opts:     opts,
59
		provider: caclient.NewCATokenProvider(opts),
60
	}
61

62
	conn, err := c.buildConnection()
63
	if err != nil {
64
		citadelClientLog.Errorf("Failed to connect to endpoint %s: %v", opts.CAEndpoint, err)
65
		return nil, fmt.Errorf("failed to connect to endpoint %s", opts.CAEndpoint)
66
	}
67
	c.conn = conn
68
	c.client = pb.NewIstioCertificateServiceClient(conn)
69
	return c, nil
70
}
71

72
func (c *CitadelClient) Close() {
73
	if c.conn != nil {
74
		c.conn.Close()
75
	}
76
}
77

78
// CSRSign calls Citadel to sign a CSR.
79
func (c *CitadelClient) CSRSign(csrPEM []byte, certValidTTLInSec int64) (res []string, err error) {
80
	crMetaStruct := &structpb.Struct{
81
		Fields: map[string]*structpb.Value{
82
			security.CertSigner: {
83
				Kind: &structpb.Value_StringValue{StringValue: c.opts.CertSigner},
84
			},
85
		},
86
	}
87
	req := &pb.IstioCertificateRequest{
88
		Csr:              string(csrPEM),
89
		ValidityDuration: certValidTTLInSec,
90
		Metadata:         crMetaStruct,
91
	}
92
	// TODO(hzxuzhonghu): notify caclient rebuilding only when root cert is updated.
93
	// It can happen when the istiod dns certs is resigned after root cert is updated,
94
	// in this case, the ca grpc client can not automatically connect to istiod after the underlying network connection closed.
95
	// Becase that the grpc client still use the old tls configuration to reconnect to istiod.
96
	// So here we need to rebuild the caClient in order to use the new root cert.
97
	defer func() {
98
		if err != nil {
99
			citadelClientLog.Errorf("failed to sign CSR: %v", err)
100
			if err := c.reconnect(); err != nil {
101
				citadelClientLog.Errorf("failed reconnect: %v", err)
102
			}
103
		}
104
	}()
105

106
	ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("ClusterID", c.opts.ClusterID))
107
	resp, err := c.client.CreateCertificate(ctx, req)
108
	if err != nil {
109
		return nil, fmt.Errorf("create certificate: %v", err)
110
	}
111

112
	if len(resp.CertChain) <= 1 {
113
		return nil, errors.New("invalid empty CertChain")
114
	}
115

116
	return resp.CertChain, nil
117
}
118

119
func (c *CitadelClient) getTLSOptions() *istiogrpc.TLSOptions {
120
	if c.tlsOpts != nil {
121
		return &istiogrpc.TLSOptions{
122
			RootCert:      c.tlsOpts.RootCert,
123
			Key:           c.tlsOpts.Key,
124
			Cert:          c.tlsOpts.Cert,
125
			ServerAddress: c.opts.CAEndpoint,
126
			SAN:           c.opts.CAEndpointSAN,
127
		}
128
	}
129
	return nil
130
}
131

132
func (c *CitadelClient) buildConnection() (*grpc.ClientConn, error) {
133
	tlsOpts := c.getTLSOptions()
134
	opts, err := istiogrpc.ClientOptions(nil, tlsOpts)
135
	if err != nil {
136
		return nil, err
137
	}
138
	opts = append(opts,
139
		grpc.WithPerRPCCredentials(c.provider),
140
		security.CARetryInterceptor(),
141
	)
142
	conn, err := grpc.Dial(c.opts.CAEndpoint, opts...)
143
	if err != nil {
144
		citadelClientLog.Errorf("Failed to connect to endpoint %s: %v", c.opts.CAEndpoint, err)
145
		return nil, fmt.Errorf("failed to connect to endpoint %s", c.opts.CAEndpoint)
146
	}
147

148
	return conn, nil
149
}
150

151
func (c *CitadelClient) reconnect() error {
152
	if err := c.conn.Close(); err != nil {
153
		return fmt.Errorf("failed to close connection: %v", err)
154
	}
155

156
	conn, err := c.buildConnection()
157
	if err != nil {
158
		return err
159
	}
160
	c.conn = conn
161
	c.client = pb.NewIstioCertificateServiceClient(conn)
162
	citadelClientLog.Info("recreated connection")
163
	return nil
164
}
165

166
// GetRootCertBundle: Citadel (Istiod) CA doesn't publish any endpoint to retrieve CA certs
167
func (c *CitadelClient) GetRootCertBundle() ([]string, error) {
168
	return []string{}, nil
169
}
170

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.