istio
171 строка · 6.1 Кб
1//go:build integ
2// +build integ
3
4// Copyright Istio Authors
5//
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18package ambient19
20import (21"bytes"22"crypto/x509"23"encoding/base64"24"encoding/json"25"errors"26"fmt"27"strings"28"testing"29"time"30
31v1 "k8s.io/api/core/v1"32
33"istio.io/istio/istioctl/pkg/util/configdump"34"istio.io/istio/pkg/test/framework"35"istio.io/istio/pkg/test/framework/components/istio"36"istio.io/istio/pkg/test/framework/components/istioctl"37"istio.io/istio/pkg/test/framework/components/namespace"38kubetest "istio.io/istio/pkg/test/kube"39"istio.io/istio/pkg/test/util/assert"40"istio.io/istio/pkg/test/util/retry"41"istio.io/istio/security/pkg/pki/util"42"istio.io/istio/tests/integration/security/util/cert"43)
44
45func TestIntermediateCertificateRefresh(t *testing.T) {46framework.NewTest(t).47Features("security.peer.cacert-rotation").48Run(func(t framework.TestContext) {49t.Skip("https://github.com/istio/istio/issues/49648")50istioCfg := istio.DefaultConfigOrFail(t, t)51istioCtl := istioctl.NewOrFail(t, t, istioctl.Config{})52namespace.ClaimOrFail(t, t, istioCfg.SystemNamespace)53newX509 := getX509FromFile(t, "ca-cert-alt-2.pem")54
55sa := apps.Captured[0].ServiceAccountName()56
57// we do not know which ztunnel instance is located on the node as the workload, so we need to check all of them initially58ztunnelPods, err := kubetest.NewPodFetch(t.AllClusters()[0], istioCfg.SystemNamespace, "app=ztunnel")()59assert.NoError(t, err)60
61originalWorkloadSecret, ztunnelPod, err := getWorkloadSecret(t, ztunnelPods, sa, istioCtl)62if err != nil {63t.Errorf("failed to get initial workload secret: %v", err)64}65
66// Update CA with new intermediate cert67if err := cert.CreateCustomCASecret(t,68"ca-cert-alt-2.pem", "ca-key-alt-2.pem",69"cert-chain-alt-2.pem", "root-cert-alt.pem"); err != nil {70t.Errorf("failed to update CA secret: %v", err)71}72
73// perform one retry to handle race condition where ztunnel cert is refreshed before Istiod certificates are reloaded74retry.UntilSuccess(func() error {75newWorkloadCert := waitForWorkloadCertUpdate(t, ztunnelPod, sa, istioCtl, originalWorkloadSecret)76return verifyWorkloadCert(t, newWorkloadCert, newX509)77}, retry.MaxAttempts(2), retry.Timeout(5*time.Minute))78})79}
80
81func getWorkloadSecret(t framework.TestContext, zPods []v1.Pod, serviceAccount string, ctl istioctl.Instance) (*configdump.CertsDump, v1.Pod, error) {82for _, ztunnel := range zPods {83podName := fmt.Sprintf("%s.%s", ztunnel.Name, ztunnel.Namespace)84out, errOut, err := ctl.Invoke([]string{"pc", "s", podName, "-o", "json"})85if err != nil || errOut != "" {86t.Errorf("failed to retrieve pod secrets from %s, err: %v errOut: %s", podName, err, errOut)87}88
89dump := []configdump.CertsDump{}90if err := json.Unmarshal([]byte(out), &dump); err != nil {91t.Errorf("failed to unmarshal secret dump: %v", err)92}93
94for _, s := range dump {95if strings.Contains(s.Identity, serviceAccount) {96if len(s.CertChain) == 0 {97t.Fatalf("cert chain missing in %v for identity: %v", ztunnel.Name, s.Identity)98}99return &s, ztunnel, nil100}101}102}103return nil, v1.Pod{}, errors.New("failed to find workload secret")104}
105
106// Abstracted function to wait for workload cert to be updated
107func waitForWorkloadCertUpdate(t framework.TestContext, ztunnelPod v1.Pod, serviceAccount string,108istioCtl istioctl.Instance, originalCert *configdump.CertsDump,109) *configdump.CertsDump {110var newSecret *configdump.CertsDump111retry.UntilOrFail(t, func() bool {112updatedCert, _, err := getWorkloadSecret(t, []v1.Pod{ztunnelPod}, serviceAccount, istioCtl)113if err != nil {114t.Logf("failed to get current workload secret: %v", err)115return false116}117
118// retry when workload cert is not updated119if originalCert.CertChain[0].ValidFrom != updatedCert.CertChain[0].ValidFrom {120newSecret = updatedCert121t.Logf("workload cert is updated")122return true123}124
125return false126}, retry.Timeout(5*time.Minute), retry.Delay(10*time.Second))127return newSecret128}
129
130func verifyWorkloadCert(t framework.TestContext, workloadSecret *configdump.CertsDump, caX590 *x509.Certificate) error {131intermediateCert, err := base64.StdEncoding.DecodeString(workloadSecret.CertChain[1].Pem)132if err != nil {133t.Errorf("failed to decode intermediate certificate: %v", err)134}135intermediateX509 := parseCert(t, intermediateCert)136// verify the correct intermediate cert is in the certificate chain137if intermediateX509.SerialNumber.String() != caX590.SerialNumber.String() {138return fmt.Errorf("intermediate certificate serial numbers do not match: got %v, wanted %v",139intermediateX509.SerialNumber.String(), caX590.SerialNumber.String())140}141
142workloadCert, err := base64.StdEncoding.DecodeString(workloadSecret.CertChain[0].Pem)143if err != nil {144return fmt.Errorf("failed to decode workload certificate: %v", err)145}146workloadX509 := parseCert(t, workloadCert)147
148// verify workload cert contains the correct intermediate cert149if !bytes.Equal(workloadX509.AuthorityKeyId, caX590.SubjectKeyId) {150return fmt.Errorf("workload certificate did not have expected authority key id: got %v wanted %v",151string(workloadX509.AuthorityKeyId), string(caX590.SubjectKeyId))152}153
154return nil155}
156
157func getX509FromFile(t framework.TestContext, caCertFile string) *x509.Certificate {158certBytes, err := cert.ReadSampleCertFromFile(caCertFile)159if err != nil {160t.Errorf("failed to read %s file: %v", caCertFile, err)161}162return parseCert(t, certBytes)163}
164
165func parseCert(t framework.TestContext, certBytes []byte) *x509.Certificate {166parsedCert, err := util.ParsePemEncodedCertificate(certBytes)167if err != nil {168t.Errorf("failed to parse certificate pem file: %v", err)169}170return parsedCert171}
172