Skip to content

Commit

Permalink
OSSM-1420 Ensure istiod can run with no cluster-scoped privileges (ma…
Browse files Browse the repository at this point in the history
…istra#513) (maistra#548)

Co-authored-by: Marko Lukša <marko.luksa@gmail.com>
  • Loading branch information
jewertow and luksa committed Aug 25, 2022
1 parent b464a82 commit 9985e99
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 71 deletions.
37 changes: 20 additions & 17 deletions pilot/pkg/config/kube/crdclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,15 @@ func NewForSchemas(client kube.Client, revision, domainSuffix string, schemas co
client: client,
istioClient: client.Istio(),
gatewayAPIClient: client.GatewayAPI(),
crdMetadataInformer: client.MetadataInformer().ForResource(collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Resource().
GroupVersionResource()).Informer(),
beginSync: atomic.NewBool(false),
initialSync: atomic.NewBool(false),
beginSync: atomic.NewBool(false),
initialSync: atomic.NewBool(false),
}

var known map[string]struct{}
if enableCRDScan {
out.crdMetadataInformer = client.MetadataInformer().ForResource(collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Resource().
GroupVersionResource()).Informer()

var err error
known, err = knownCRDs(client.Ext())
if err != nil {
Expand Down Expand Up @@ -239,19 +240,21 @@ func (cl *Client) Run(stop <-chan struct{}) {
cl.initialSync.Store(true)
scope.Info("Pilot K8S CRD controller synced ", time.Since(t0))

cl.crdMetadataInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
crd, ok := obj.(*metav1.PartialObjectMetadata)
if !ok {
// Shouldn't happen
scope.Errorf("wrong type %T: %v", obj, obj)
return
}
handleCRDAdd(cl, crd.Name, stop)
},
UpdateFunc: nil,
DeleteFunc: nil,
})
if cl.crdMetadataInformer != nil {
cl.crdMetadataInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
crd, ok := obj.(*metav1.PartialObjectMetadata)
if !ok {
// Shouldn't happen
scope.Errorf("wrong type %T: %v", obj, obj)
return
}
handleCRDAdd(cl, crd.Name, stop)
},
UpdateFunc: nil,
DeleteFunc: nil,
})
}

cl.queue.Run(stop)
scope.Info("controller terminated")
Expand Down
15 changes: 2 additions & 13 deletions pilot/pkg/config/kube/gateway/deploymentcontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,13 @@ import (
"fmt"
"strings"
"text/template"
"time"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
klabels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
appsinformersv1 "k8s.io/client-go/informers/apps/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
gateway "sigs.k8s.io/gateway-api/apis/v1alpha2"
"sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1alpha2"
"sigs.k8s.io/yaml"
Expand Down Expand Up @@ -114,14 +109,8 @@ func NewDeploymentController(client kube.Client) *DeploymentController {
AddEventHandler(handler)

// For Deployments, this is the only controller watching. We can filter to just the deployments we care about
client.KubeInformer().InformerFor(&appsv1.Deployment{}, func(k kubernetes.Interface, resync time.Duration) cache.SharedIndexInformer {
return appsinformersv1.NewFilteredDeploymentInformer(
k, metav1.NamespaceAll, resync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
func(options *metav1.ListOptions) {
options.LabelSelector = "gateway.istio.io/managed=istio.io-gateway-controller"
},
)
}).AddEventHandler(handler)
// TODO: filter deployments with label selector "gateway.istio.io/managed=istio.io-gateway-controller"
client.KubeInformer().Apps().V1().Deployments().Informer().AddEventHandler(handler)

// Use the full informer; we are already watching all Gateways for the core Istiod logic
gw.Informer().AddEventHandler(controllers.ObjectHandler(dc.queue.AddObject))
Expand Down
7 changes: 5 additions & 2 deletions pilot/pkg/config/kube/ingressv1/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,11 @@ func NewController(client kube.Client, meshWatcher mesh.Holder,
ingressInformer := client.KubeInformer().Networking().V1().Ingresses()
serviceInformer := client.KubeInformer().Core().V1().Services()

classes := client.KubeInformer().Networking().V1().IngressClasses()
classes.Informer()
var classes ingressinformer.IngressClassInformer
if options.EnableIngressClassName {
classes = client.KubeInformer().Networking().V1().IngressClasses()
classes.Informer()
}

c := &controller{
meshWatcher: meshWatcher,
Expand Down
22 changes: 1 addition & 21 deletions pilot/pkg/credentials/kube/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ import (
authorizationv1 "k8s.io/api/authorization/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
informersv1 "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
listersv1 "k8s.io/client-go/listers/core/v1"
Expand Down Expand Up @@ -80,24 +77,7 @@ type authorizationResponse struct {
var _ credentials.Controller = &CredentialsController{}

func NewCredentialsController(client kube.Client, clusterID cluster.ID) *CredentialsController {
informer := client.KubeInformer().InformerFor(&v1.Secret{}, func(k kubernetes.Interface, resync time.Duration) cache.SharedIndexInformer {
return informersv1.NewFilteredSecretInformer(
k, metav1.NamespaceAll, resync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
func(options *metav1.ListOptions) {
// We only care about TLS certificates and docker config for Wasm image pulling.
// Unfortunately, it is not as simple as selecting type=kubernetes.io/tls and type=kubernetes.io/dockerconfigjson.
// Because of legacy reasons and supporting an extra ca.crt, we also support generic types.
// Its also likely users have started to use random types and expect them to continue working.
// This makes the assumption we will never care about Helm secrets or SA token secrets - two common
// large secrets in clusters.
// This is a best effort optimization only; the code would behave correctly if we watched all secrets.
options.FieldSelector = fields.AndSelectors(
fields.OneTermNotEqualSelector("type", "helm.sh/release.v1"),
fields.OneTermNotEqualSelector("type", string(v1.SecretTypeServiceAccountToken)),
).String()
},
)
})
informer := client.KubeInformer().Core().V1().Secrets().Informer()

return &CredentialsController{
secretInformer: informer,
Expand Down
2 changes: 1 addition & 1 deletion pilot/pkg/leaderelection/leaderelection.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func NewLeaderElection(namespace, name, electionID, revision string, client kube

func NewLeaderElectionMulticluster(namespace, name, electionID, revision string, remote bool, client kube.Client) *LeaderElection {
var watcher revisions.DefaultWatcher
if features.PrioritizedLeaderElection {
if features.PrioritizedLeaderElection && client.GetMemberRoll() == nil {
watcher = revisions.NewDefaultWatcher(client, revision)
}
if name == "" {
Expand Down
39 changes: 22 additions & 17 deletions pilot/pkg/serviceregistry/kube/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,26 +350,31 @@ func NewController(kubeClient kubelib.Client, options Options) *Controller {
}
}

c.nsInformer = kubeClient.KubeInformer().Core().V1().Namespaces().Informer()
c.nsLister = kubeClient.KubeInformer().Core().V1().Namespaces().Lister()
// Don't start the namespace informer if Maistra's MemberRoll is in use.
if c.opts.SystemNamespace != "" && options.MemberRollName == "" {
nsInformer := filter.NewFilteredSharedIndexInformer(func(obj interface{}) bool {
ns, ok := obj.(*v1.Namespace)
if !ok {
log.Warnf("Namespace watch getting wrong type in event: %T", obj)
return false
}
return ns.Name == c.opts.SystemNamespace
}, c.nsInformer)
c.registerHandlers(nsInformer, "Namespaces", c.onSystemNamespaceEvent, nil)
}
if options.MemberRollName == "" {
c.nsInformer = kubeClient.KubeInformer().Core().V1().Namespaces().Informer()
c.nsLister = kubeClient.KubeInformer().Core().V1().Namespaces().Lister()
if c.opts.SystemNamespace != "" {
nsInformer := filter.NewFilteredSharedIndexInformer(func(obj interface{}) bool {
ns, ok := obj.(*v1.Namespace)
if !ok {
log.Warnf("Namespace watch getting wrong type in event: %T", obj)
return false
}
return ns.Name == c.opts.SystemNamespace
}, c.nsInformer)
c.registerHandlers(nsInformer, "Namespaces", c.onSystemNamespaceEvent, nil)
}

if c.opts.DiscoveryNamespacesFilter == nil {
c.opts.DiscoveryNamespacesFilter = filter.NewDiscoveryNamespacesFilter(c.nsLister, options.MeshWatcher.Mesh().DiscoverySelectors)
}
if c.opts.DiscoveryNamespacesFilter == nil {
c.opts.DiscoveryNamespacesFilter = filter.NewDiscoveryNamespacesFilter(c.nsLister, options.MeshWatcher.Mesh().DiscoverySelectors)
}

c.initDiscoveryHandlers(kubeClient, options.EndpointMode, options.MeshWatcher, c.opts.DiscoveryNamespacesFilter)
c.initDiscoveryHandlers(kubeClient, options.EndpointMode, options.MeshWatcher, c.opts.DiscoveryNamespacesFilter)

} else if c.opts.DiscoveryNamespacesFilter == nil {
c.opts.DiscoveryNamespacesFilter = filter.NewMaistraDiscoveryNamespacesFilter(kubeClient.GetMemberRoll())
}

c.serviceInformer = filter.NewFilteredSharedIndexInformer(c.opts.DiscoveryNamespacesFilter.Filter, kubeClient.KubeInformer().Core().V1().Services().Informer())
c.serviceLister = listerv1.NewServiceLister(c.serviceInformer.GetIndexer())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
listerv1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"

memberroll "istio.io/istio/pkg/servicemesh/controller"
"istio.io/pkg/log"
)

Expand Down Expand Up @@ -252,3 +253,63 @@ func (d *discoveryNamespacesFilter) isSelected(labels labels.Set) bool {

return false
}

type maistraDiscoveryNamespacesFilter struct {
lock sync.RWMutex
namespaces sets.String
}

func NewMaistraDiscoveryNamespacesFilter(mrc memberroll.MemberRollController) DiscoveryNamespacesFilter {
d := &maistraDiscoveryNamespacesFilter{}
mrc.Register(d, "MaistraDiscoveryNamespacesFilter")
return d
}

func (d *maistraDiscoveryNamespacesFilter) SetNamespaces(namespaces ...string) {
d.lock.Lock()
defer d.lock.Unlock()
d.namespaces = sets.NewString(namespaces...)
}

func (d *maistraDiscoveryNamespacesFilter) Filter(obj interface{}) bool {
// xns-informers make sure we only receive objects in member namespaces
return true
}

// SelectorsChanged is not implemented, because Maistra does not filter namespaces by selectors;
// this function should never be called if memberRollName is specified, so it's safe to call panic.
func (d *maistraDiscoveryNamespacesFilter) SelectorsChanged(_ []*metav1.LabelSelector) (selectedNamespaces []string, deselectedNamespaces []string) {
panic("Not implemented")
}

// SyncNamespaces is not implemented, because this function should never be called if memberRollName is specified,
// so it's safe to call panic.
func (d *maistraDiscoveryNamespacesFilter) SyncNamespaces() error {
panic("Not implemented")
}

// NamespaceCreated is not implemented, because added namespaces are handled by serviceMeshMemberRollListener;
// this function should never be called if memberRollName is specified, so it's safe to call panic.
func (d *maistraDiscoveryNamespacesFilter) NamespaceCreated(_ metav1.ObjectMeta) (membershipChanged bool) {
panic("Not implemented")
}

// NamespaceUpdated is not implemented, because updated namespaces are handled by serviceMeshMemberRollListener;
// this function should never be called if memberRollName is specified, so it's safe to call panic.
func (d *maistraDiscoveryNamespacesFilter) NamespaceUpdated(_, _ metav1.ObjectMeta) (membershipChanged bool, namespaceAdded bool) {
panic("Not implemented")
}

// NamespaceDeleted is not implemented, because deleted namespaces are handled by serviceMeshMemberRollListener;
// this function should never be called if memberRollName is specified, so it's safe to call panic.
func (d *maistraDiscoveryNamespacesFilter) NamespaceDeleted(_ metav1.ObjectMeta) (membershipChanged bool) {
panic("Not implemented")
}

func (d *maistraDiscoveryNamespacesFilter) GetMembers() sets.String {
d.lock.RLock()
defer d.lock.RUnlock()
members := sets.NewString()
members.Insert(d.namespaces.List()...)
return members
}

0 comments on commit 9985e99

Please sign in to comment.