blob: 7174b443a421c9b0d209f20365e75e71efe11a86 [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package authz
import (
"fmt"
"strings"
)
import (
"istio.io/api/mesh/v1alpha1"
"istio.io/api/security/v1beta1"
k8s_labels "k8s.io/apimachinery/pkg/labels"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/config/analysis"
"github.com/apache/dubbo-go-pixiu/pkg/config/analysis/analyzers/util"
"github.com/apache/dubbo-go-pixiu/pkg/config/analysis/msg"
"github.com/apache/dubbo-go-pixiu/pkg/config/resource"
"github.com/apache/dubbo-go-pixiu/pkg/config/schema/collection"
"github.com/apache/dubbo-go-pixiu/pkg/config/schema/collections"
)
// AuthorizationPoliciesAnalyzer checks the validity of authorization policies
type AuthorizationPoliciesAnalyzer struct{}
var (
_ analysis.Analyzer = &AuthorizationPoliciesAnalyzer{}
meshConfig *v1alpha1.MeshConfig
)
func (a *AuthorizationPoliciesAnalyzer) Metadata() analysis.Metadata {
return analysis.Metadata{
Name: "auth.AuthorizationPoliciesAnalyzer",
Description: "Checks the validity of authorization policies",
Inputs: collection.Names{
collections.IstioMeshV1Alpha1MeshConfig.Name(),
collections.IstioSecurityV1Beta1Authorizationpolicies.Name(),
collections.K8SCoreV1Namespaces.Name(),
collections.K8SCoreV1Pods.Name(),
},
}
}
func (a *AuthorizationPoliciesAnalyzer) Analyze(c analysis.Context) {
podLabelsMap := initPodLabelsMap(c)
c.ForEach(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), func(r *resource.Instance) bool {
a.analyzeNoMatchingWorkloads(r, c, podLabelsMap)
a.analyzeNamespaceNotFound(r, c)
return true
})
}
func (a *AuthorizationPoliciesAnalyzer) analyzeNoMatchingWorkloads(r *resource.Instance, c analysis.Context, podLabelsMap map[string][]k8s_labels.Set) {
ap := r.Message.(*v1beta1.AuthorizationPolicy)
apNs := r.Metadata.FullName.Namespace.String()
// If AuthzPolicy is mesh-wide
if meshWidePolicy(apNs, c) {
// If it has selector, need further analysis
if ap.Selector != nil {
apSelector := k8s_labels.SelectorFromSet(ap.Selector.MatchLabels)
// If there is at least one pod matching the selector within the whole mesh
if !hasMatchingPodsRunning(apSelector, podLabelsMap) {
c.Report(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), msg.NewNoMatchingWorkloadsFound(r, apSelector.String()))
}
}
// If AuthzPolicy is mesh-wide and selectorless,
// no need to keep the analysis
return
}
// If the AuthzPolicy is namespace-wide and there are present Pods,
// no messages should be triggered.
if ap.Selector == nil {
if len(podLabelsMap[apNs]) == 0 {
c.Report(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), msg.NewNoMatchingWorkloadsFound(r, ""))
}
return
}
// If the AuthzPolicy has Selector, then need to find a matching Pod.
apSelector := k8s_labels.SelectorFromSet(ap.Selector.MatchLabels)
if !hasMatchingPodsRunningIn(apSelector, podLabelsMap[apNs]) {
c.Report(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), msg.NewNoMatchingWorkloadsFound(r, apSelector.String()))
}
}
// Returns true when the namespace is the root namespace.
// It takes the MeshConfig names istio, if not the last instance found.
func meshWidePolicy(ns string, c analysis.Context) bool {
mConf := fetchMeshConfig(c)
return mConf != nil && ns == mConf.GetRootNamespace()
}
func fetchMeshConfig(c analysis.Context) *v1alpha1.MeshConfig {
if meshConfig != nil {
return meshConfig
}
c.ForEach(collections.IstioMeshV1Alpha1MeshConfig.Name(), func(r *resource.Instance) bool {
meshConfig = r.Message.(*v1alpha1.MeshConfig)
return r.Metadata.FullName.Name != util.MeshConfigName
})
return meshConfig
}
func hasMatchingPodsRunning(selector k8s_labels.Selector, podLabelsMap map[string][]k8s_labels.Set) bool {
for _, setList := range podLabelsMap {
if hasMatchingPodsRunningIn(selector, setList) {
return true
}
}
return false
}
func hasMatchingPodsRunningIn(selector k8s_labels.Selector, setList []k8s_labels.Set) bool {
hasMatchingPods := false
for _, labels := range setList {
if selector.Matches(labels) {
hasMatchingPods = true
break
}
}
return hasMatchingPods
}
func (a *AuthorizationPoliciesAnalyzer) analyzeNamespaceNotFound(r *resource.Instance, c analysis.Context) {
ap := r.Message.(*v1beta1.AuthorizationPolicy)
for i, rule := range ap.Rules {
for j, from := range rule.From {
for k, ns := range append(from.Source.Namespaces, from.Source.NotNamespaces...) {
if !matchNamespace(ns, c) {
m := msg.NewReferencedResourceNotFound(r, "namespace", ns)
nsIndex := k
if nsIndex >= len(from.Source.Namespaces) {
nsIndex -= len(from.Source.Namespaces)
}
if line, ok := util.ErrorLine(r, fmt.Sprintf(util.AuthorizationPolicyNameSpace, i, j, nsIndex)); ok {
m.Line = line
}
c.Report(collections.IstioSecurityV1Beta1Authorizationpolicies.Name(), m)
}
}
}
}
}
func matchNamespace(exp string, c analysis.Context) bool {
match := false
c.ForEach(collections.K8SCoreV1Namespaces.Name(), func(r *resource.Instance) bool {
ns := r.Metadata.FullName.String()
match = namespaceMatch(ns, exp)
return !match
})
return match
}
func namespaceMatch(ns, exp string) bool {
if strings.EqualFold(exp, "*") {
return true
}
if strings.HasPrefix(exp, "*") {
return strings.HasSuffix(ns, strings.TrimPrefix(exp, "*"))
}
if strings.HasSuffix(exp, "*") {
return strings.HasPrefix(ns, strings.TrimSuffix(exp, "*"))
}
return strings.EqualFold(ns, exp)
}
// Build a map indexed by namespace with in-mesh Pod's labels
func initPodLabelsMap(c analysis.Context) map[string][]k8s_labels.Set {
podLabelsMap := make(map[string][]k8s_labels.Set)
c.ForEach(collections.K8SCoreV1Pods.Name(), func(r *resource.Instance) bool {
pLabels := k8s_labels.Set(r.Metadata.Labels)
ns := r.Metadata.FullName.Namespace.String()
if podLabelsMap[ns] == nil {
podLabelsMap[ns] = make([]k8s_labels.Set, 0)
}
if util.PodInMesh(r, c) {
podLabelsMap[ns] = append(podLabelsMap[ns], pLabels)
}
return true
})
return podLabelsMap
}