Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSSM-1420 Ensure istiod can run with no cluster-scoped privileges #513

Merged
merged 1 commit into from
May 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -149,16 +149,17 @@ func NewForSchemas(ctx context.Context, client kube.Client, revision, domainSuff
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),
crdWatches: map[config.GroupVersionKind]chan struct{}{
gvk.KubernetesGateway: make(chan struct{}),
},
}
var known map[string]struct{}
if enableCRDScan {
out.crdMetadataInformer = client.MetadataInformer().ForResource(collections.K8SApiextensionsK8SIoV1Customresourcedefinitions.Resource().
GroupVersionResource()).Informer()

var err error
known, err = knownCRDs(ctx, client.Ext())
if err != nil {
Expand Down Expand Up @@ -219,19 +220,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,17 +21,12 @@ 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"
"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"
"k8s.io/client-go/util/workqueue"
gateway "sigs.k8s.io/gateway-api/apis/v1alpha2"
"sigs.k8s.io/yaml"
Expand Down Expand Up @@ -91,14 +86,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"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth to create a Jira and link it here? We might thinks this is a TODO from upstream and never look back here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've created OSSM-1448, but I have the feeling that this is something that we'll never actually implement. We'll either move away from xns-informers or upstream will remove the label selector.

client.KubeInformer().Apps().V1().Deployments().Informer().AddEventHandler(handler)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could consider using NewFilteredSecretInformer() func of xns-informer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, the NewFilteredSecretInformer is a more low-level function. If we were to use it, we'd have to keep the list of namespaces in sync using the SMMR controller.

On the other hand, this filter here is only used for optimization.


// Use the full informer; we are already watching all Gateways for the core Istiod logic
client.GatewayAPIInformer().Gateway().V1alpha2().Gateways().Informer().
Expand Down
20 changes: 1 addition & 19 deletions pilot/pkg/credentials/kube/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ 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 @@ -81,23 +79,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. Unfortunately, it is not as simple as selecting type=kubernetes.io/tls.
// 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()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, we could use NewFilteredSecretInformer(). let's create a follow-up ticket

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, the filtering is only done as a best-effort optimization (according to the comment, the code behaves correctly without it). And since we're only watching the secrets in member namespaces, whereas the upstream code uses a cluster-scoped watch, this optimization may not do much at all.


return &CredentialsController{
secrets: informerAdapter{listersv1.NewSecretLister(informer.GetIndexer()), informer},
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 @@ -365,26 +365,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 @@ -22,6 +22,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
listerv1 "k8s.io/client-go/listers/core/v1"

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

Expand Down Expand Up @@ -202,3 +203,51 @@ 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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

matches exactly what I have :) just called it FakeDiscoveryNamespacesFilter

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 {
d.lock.RLock()
defer d.lock.RUnlock()
return d.namespaces.Has(obj.(metav1.Object).GetNamespace())
}

func (d *maistraDiscoveryNamespacesFilter) SelectorsChanged(
discoverySelectors []*metav1.LabelSelector) (selectedNamespaces []string, deselectedNamespaces []string) {
panic("Not implemented")
}

func (d *maistraDiscoveryNamespacesFilter) NamespaceCreated(ns metav1.ObjectMeta) (membershipChanged bool) {
panic("Not implemented")
}

func (d *maistraDiscoveryNamespacesFilter) NamespaceUpdated(oldNs, newNs metav1.ObjectMeta) (membershipChanged bool, namespaceAdded bool) {
panic("Not implemented")
}

func (d *maistraDiscoveryNamespacesFilter) NamespaceDeleted(ns 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
}