istio
288 строк · 7.6 Кб
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
15package trustbundle16
17import (18"crypto/x509"19"encoding/pem"20"fmt"21"sort"22"strings"23"sync"24"time"25
26meshconfig "istio.io/api/mesh/v1alpha1"27"istio.io/istio/pkg/log"28"istio.io/istio/pkg/slices"29"istio.io/istio/pkg/spiffe"30"istio.io/istio/pkg/util/sets"31)
32
33// Source is all possible sources of MeshConfig
34type Source int35
36const (37SourceIstioCA Source = iota38SourceMeshConfig
39SourceIstioRA
40sourceSpiffeEndpoints
41
42RemoteDefaultPollPeriod = 30 * time.Minute43)
44
45func (s Source) String() string {46switch s {47case SourceIstioCA:48return "IstioCA"49case SourceMeshConfig:50return "MeshConfig"51case SourceIstioRA:52return "IstioRA"53case sourceSpiffeEndpoints:54return "SpiffeEndpoints"55default:56return "Unknown"57}58}
59
60type TrustAnchorConfig struct {61Certs []string62}
63
64type TrustAnchorUpdate struct {65TrustAnchorConfig
66Source Source
67}
68
69type TrustBundle struct {70sourceConfig map[Source]TrustAnchorConfig71mutex sync.RWMutex72mergedCerts []string73updatecb func()74endpointMutex sync.RWMutex75endpoints []string76endpointUpdateChan chan struct{}77remoteCaCertPool *x509.CertPool78}
79
80var (81trustBundleLog = log.RegisterScope("trustBundle", "Workload mTLS trust bundle logs")82remoteTimeout = 10 * time.Second83)
84
85// NewTrustBundle returns a new trustbundle
86func NewTrustBundle(remoteCaCertPool *x509.CertPool) *TrustBundle {87var err error88tb := &TrustBundle{89sourceConfig: map[Source]TrustAnchorConfig{90SourceIstioCA: {Certs: []string{}},91SourceMeshConfig: {Certs: []string{}},92SourceIstioRA: {Certs: []string{}},93sourceSpiffeEndpoints: {Certs: []string{}},94},95mergedCerts: []string{},96updatecb: nil,97endpointUpdateChan: make(chan struct{}, 1),98endpoints: []string{},99}100if remoteCaCertPool == nil {101tb.remoteCaCertPool, err = x509.SystemCertPool()102if err != nil {103trustBundleLog.Errorf("failed to initialize remote Cert pool: %v", err)104}105} else {106tb.remoteCaCertPool = remoteCaCertPool107}108return tb109}
110
111func (tb *TrustBundle) UpdateCb(updatecb func()) {112tb.updatecb = updatecb113}
114
115// GetTrustBundle : Retrieves all the trustAnchors for current Spiffee Trust Domain
116func (tb *TrustBundle) GetTrustBundle() []string {117tb.mutex.RLock()118defer tb.mutex.RUnlock()119trustedCerts := make([]string, len(tb.mergedCerts))120copy(trustedCerts, tb.mergedCerts)121return trustedCerts122}
123
124func verifyTrustAnchor(trustAnchor string) error {125block, _ := pem.Decode([]byte(trustAnchor))126if block == nil {127return fmt.Errorf("failed to decode pem certificate")128}129cert, err := x509.ParseCertificate(block.Bytes)130if err != nil {131return fmt.Errorf("failed to parse X.509 certificate: %v", err)132}133if !cert.IsCA {134return fmt.Errorf("certificate is not a CA certificate")135}136return nil137}
138
139func (tb *TrustBundle) mergeInternal() {140var mergeCerts []string141certMap := sets.New[string]()142
143tb.mutex.Lock()144defer tb.mutex.Unlock()145
146for _, configSource := range tb.sourceConfig {147for _, cert := range configSource.Certs {148if !certMap.InsertContains(cert) {149mergeCerts = append(mergeCerts, cert)150}151}152}153tb.mergedCerts = mergeCerts154sort.Strings(tb.mergedCerts)155}
156
157// UpdateTrustAnchor : External Function to merge a TrustAnchor config with the existing TrustBundle
158func (tb *TrustBundle) UpdateTrustAnchor(anchorConfig *TrustAnchorUpdate) error {159var ok bool160var err error161
162tb.mutex.RLock()163cachedConfig, ok := tb.sourceConfig[anchorConfig.Source]164tb.mutex.RUnlock()165if !ok {166return fmt.Errorf("invalid source of TrustBundle configuration %v", anchorConfig.Source)167}168
169// Check if anything needs to be changed at all170if slices.Equal(anchorConfig.Certs, cachedConfig.Certs) {171trustBundleLog.Debugf("no change to trustAnchor configuration after recent update")172return nil173}174
175for _, cert := range anchorConfig.Certs {176err = verifyTrustAnchor(cert)177if err != nil {178return err179}180}181tb.mutex.Lock()182tb.sourceConfig[anchorConfig.Source] = anchorConfig.TrustAnchorConfig183tb.mutex.Unlock()184tb.mergeInternal()185
186trustBundleLog.Infof("updating Source %v with certs %v",187anchorConfig.Source,188strings.Join(anchorConfig.TrustAnchorConfig.Certs, "\n"))189
190if tb.updatecb != nil {191tb.updatecb()192}193return nil194}
195
196func (tb *TrustBundle) updateRemoteEndpoint(spiffeEndpoints []string) {197tb.endpointMutex.RLock()198remoteEndpoints := tb.endpoints199tb.endpointMutex.RUnlock()200
201if slices.Equal(spiffeEndpoints, remoteEndpoints) {202return203}204trustBundleLog.Infof("updated remote endpoints :%v", spiffeEndpoints)205tb.endpointMutex.Lock()206tb.endpoints = spiffeEndpoints207tb.endpointMutex.Unlock()208tb.endpointUpdateChan <- struct{}{}209}
210
211// AddMeshConfigUpdate : Update trustAnchor configurations from meshConfig
212func (tb *TrustBundle) AddMeshConfigUpdate(cfg *meshconfig.MeshConfig) error {213var err error214if cfg != nil {215certs := []string{}216endpoints := []string{}217for _, pemCert := range cfg.GetCaCertificates() {218cert := pemCert.GetPem()219if cert != "" {220certs = append(certs, cert)221} else if pemCert.GetSpiffeBundleUrl() != "" {222endpoints = append(endpoints, pemCert.GetSpiffeBundleUrl())223}224}225
226err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{227TrustAnchorConfig: TrustAnchorConfig{Certs: certs},228Source: SourceMeshConfig,229})230if err != nil {231trustBundleLog.Errorf("failed to update meshConfig PEM trustAnchors: %v", err)232return err233}234
235tb.updateRemoteEndpoint(endpoints)236}237return nil238}
239
240func (tb *TrustBundle) fetchRemoteTrustAnchors() {241var err error242
243tb.endpointMutex.RLock()244remoteEndpoints := tb.endpoints245tb.endpointMutex.RUnlock()246remoteCerts := []string{}247
248currentTrustDomain := spiffe.GetTrustDomain()249for _, endpoint := range remoteEndpoints {250trustDomainAnchorMap, err := spiffe.RetrieveSpiffeBundleRootCerts(251map[string]string{currentTrustDomain: endpoint}, tb.remoteCaCertPool, remoteTimeout)252if err != nil {253trustBundleLog.Errorf("unable to fetch trust Anchors from endpoint %s: %s", endpoint, err)254continue255}256certs := trustDomainAnchorMap[currentTrustDomain]257for _, cert := range certs {258certStr := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))259trustBundleLog.Debugf("from endpoint %v, fetched trust anchor cert: %v", endpoint, certStr)260remoteCerts = append(remoteCerts, certStr)261}262}263err = tb.UpdateTrustAnchor(&TrustAnchorUpdate{264TrustAnchorConfig: TrustAnchorConfig{Certs: remoteCerts},265Source: sourceSpiffeEndpoints,266})267if err != nil {268trustBundleLog.Errorf("failed to update meshConfig Spiffe trustAnchors: %v", err)269}270}
271
272func (tb *TrustBundle) ProcessRemoteTrustAnchors(stop <-chan struct{}, pollInterval time.Duration) {273ticker := time.NewTicker(pollInterval)274defer ticker.Stop()275for {276select {277case <-ticker.C:278trustBundleLog.Infof("waking up to perform periodic checks")279tb.fetchRemoteTrustAnchors()280case <-stop:281trustBundleLog.Infof("stop processing endpoint trustAnchor updates")282return283case <-tb.endpointUpdateChan:284tb.fetchRemoteTrustAnchors()285trustBundleLog.Infof("processing endpoint trustAnchor Updates for config change")286}287}288}
289