istio
162 строки · 7.5 Кб
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 trustdomain
16
17import (
18"fmt"
19"strings"
20
21"istio.io/istio/pilot/pkg/features"
22"istio.io/istio/pkg/config/constants"
23istiolog "istio.io/istio/pkg/log"
24)
25
26var authzLog = istiolog.RegisterScope("authorization", "Istio Authorization Policy")
27
28type Bundle struct {
29// Contain the local trust domain and its aliases.
30// The trust domain corresponds to the trust root of a system.
31// Refer to [SPIFFE-ID](https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain)
32// The trust domain aliases represent the aliases of `trust_domain`.
33// For example, if we have
34// trustDomain: td1, trustDomainAliases: ["td2", "td3"]
35// Any service with the identity `td1/ns/foo/sa/a-service-account`, `td2/ns/foo/sa/a-service-account`,
36// or `td3/ns/foo/sa/a-service-account` will be treated the same in the Istio mesh.
37TrustDomains []string
38}
39
40// NewBundle returns a new trust domain bundle.
41func NewBundle(trustDomain string, trustDomainAliases []string) Bundle {
42return Bundle{
43// Put the new trust domain to the beginning of the list to avoid changing existing tests.
44TrustDomains: append([]string{trustDomain}, trustDomainAliases...),
45}
46}
47
48// ReplaceTrustDomainAliases checks the existing principals and returns a list of new principals
49// with the current trust domain and its aliases.
50// For example, for a user "bar" in namespace "foo".
51// If the local trust domain is "td2" and its alias is "td1" (migrating from td1 to td2),
52// replaceTrustDomainAliases returns ["td2/ns/foo/sa/bar", "td1/ns/foo/sa/bar]].
53func (t Bundle) ReplaceTrustDomainAliases(principals []string) []string {
54principalsIncludingAliases := []string{}
55for _, principal := range principals {
56isTrustDomainBeingEnforced := isTrustDomainBeingEnforced(principal)
57// Return the existing principals if the policy doesn't care about the trust domain.
58if !isTrustDomainBeingEnforced {
59principalsIncludingAliases = append(principalsIncludingAliases, principal)
60continue
61}
62trustDomainFromPrincipal, err := getTrustDomainFromSpiffeIdentity(principal)
63if err != nil {
64authzLog.Errorf("unexpected incorrect Spiffe format: %s", principal)
65principalsIncludingAliases = append(principalsIncludingAliases, principal)
66continue
67}
68// Only generate configuration if the extracted trust domain from the policy is part of the trust domain list,
69// or if the extracted/existing trust domain is "cluster.local", which is a pointer to the local trust domain
70// and its aliases.
71if stringMatch(trustDomainFromPrincipal, t.TrustDomains) || trustDomainFromPrincipal == constants.DefaultClusterLocalDomain {
72// Generate configuration for trust domain and trust domain aliases.
73principalsIncludingAliases = append(principalsIncludingAliases, t.replaceTrustDomains(principal, trustDomainFromPrincipal)...)
74} else {
75msg := fmt.Sprintf("Trust domain %s from principal %s does not match the current trust "+
76"domain or its aliases", trustDomainFromPrincipal, principal)
77// when SkipValidateTrustDomain is being used the message isn't very meaningful so we'll log it at a lower level
78// logging it at this level may help users who are looking to disable skipping validation understand if it's safe
79if !features.SkipValidateTrustDomain {
80authzLog.Warn(msg)
81} else {
82authzLog.Debug(msg)
83}
84// If the trust domain from the existing doesn't match with the new trust domain aliases or "cluster.local",
85// keep the policy as it is.
86principalsIncludingAliases = append(principalsIncludingAliases, principal)
87}
88}
89return principalsIncludingAliases
90}
91
92// replaceTrustDomains replace the given principal's trust domain with the trust domains from the
93// trustDomains list and return the new principals.
94func (t Bundle) replaceTrustDomains(principal, trustDomainFromPrincipal string) []string {
95principalsForAliases := []string{}
96for _, td := range t.TrustDomains {
97// If the trust domain has a prefix * (e.g. *local from *local/ns/foo/sa/bar), keep the principal
98// as-is for the matched trust domain. For others, replace the trust domain with the new trust domain
99// or alias.
100var newPrincipal string
101var err error
102if suffixMatch(td, trustDomainFromPrincipal) {
103newPrincipal = principal
104} else {
105newPrincipal, err = replaceTrustDomainInPrincipal(td, principal)
106if err != nil {
107authzLog.Errorf("Failed to replace trust domain with %s from principal %s: %v", td, principal, err)
108continue
109}
110}
111// Check to make sure we don't generate duplicated principals. This happens when trust domain
112// has a * prefix. For example, "*-td" can match with "old-td" and "new-td", but we only want
113// to keep the principal as-is in the generated config, .i.e. *-td.
114if !isKeyInList(newPrincipal, principalsForAliases) {
115principalsForAliases = append(principalsForAliases, newPrincipal)
116}
117}
118return principalsForAliases
119}
120
121// replaceTrustDomainInPrincipal returns a new SPIFFE identity with the new trust domain.
122// The trust domain corresponds to the trust root of a system.
123// Refer to
124// [SPIFFE-ID](https://github.com/spiffe/spiffe/blob/master/standards/SPIFFE-ID.md#21-trust-domain)
125// In Istio authorization, an identity is presented in the format:
126// <trust-domain>/ns/<some-namespace>/sa/<some-service-account>
127func replaceTrustDomainInPrincipal(trustDomain string, principal string) (string, error) {
128identityParts := strings.Split(principal, "/")
129// A valid SPIFFE identity in authorization has no SPIFFE:// prefix.
130// It is presented as <trust-domain>/ns/<some-namespace>/sa/<some-service-account>
131if len(identityParts) != 5 {
132return "", fmt.Errorf("wrong SPIFFE format: %s", principal)
133}
134return fmt.Sprintf("%s/%s", trustDomain, strings.Join(identityParts[1:], "/")), nil
135}
136
137// isTrustDomainBeingEnforced checks whether the trust domain is being checked in the filter or not.
138// For example, in the principal "*/ns/foo/sa/bar", the trust domain is * and it matches to any trust domain,
139// so it won't be checked in the filter.
140func isTrustDomainBeingEnforced(principal string) bool {
141identityParts := strings.Split(principal, "/")
142if len(identityParts) != 5 {
143// If a principal is mis-configured and doesn't follow Spiffe format, e.g. "sa/bar",
144// there is really no trust domain from the principal, so the trust domain is also considered not being enforced.
145return false
146}
147// Check if the first part of the spiffe string is "*" (as opposed to *-something or "").
148return identityParts[0] != "*"
149}
150
151// getTrustDomainFromSpiffeIdentity gets the trust domain from the given principal and expects
152// principal to have the right SPIFFE format.
153func getTrustDomainFromSpiffeIdentity(principal string) (string, error) {
154identityParts := strings.Split(principal, "/")
155// A valid SPIFFE identity in authorization has no SPIFFE:// prefix.
156// It is presented as <trust-domain>/ns/<some-namespace>/sa/<some-service-account>
157if len(identityParts) != 5 {
158return "", fmt.Errorf("wrong SPIFFE format: %s", principal)
159}
160trustDomain := identityParts[0]
161return trustDomain, nil
162}
163