1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 16:06:56 +00:00
kyverno/cmd/kyverno/main.go
2020-11-11 15:09:07 -08:00

391 lines
13 KiB
Go
Executable file

package main
import (
"context"
"flag"
"fmt"
"net/http"
_ "net/http/pprof"
"os"
"time"
"github.com/kyverno/kyverno/pkg/checker"
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions"
"github.com/kyverno/kyverno/pkg/common"
"github.com/kyverno/kyverno/pkg/config"
dclient "github.com/kyverno/kyverno/pkg/dclient"
event "github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/generate"
generatecleanup "github.com/kyverno/kyverno/pkg/generate/cleanup"
"github.com/kyverno/kyverno/pkg/openapi"
"github.com/kyverno/kyverno/pkg/policy"
"github.com/kyverno/kyverno/pkg/policycache"
"github.com/kyverno/kyverno/pkg/policyreport"
"github.com/kyverno/kyverno/pkg/policystatus"
"github.com/kyverno/kyverno/pkg/resourcecache"
"github.com/kyverno/kyverno/pkg/signal"
"github.com/kyverno/kyverno/pkg/utils"
"github.com/kyverno/kyverno/pkg/version"
"github.com/kyverno/kyverno/pkg/webhookconfig"
"github.com/kyverno/kyverno/pkg/webhooks"
webhookgenerate "github.com/kyverno/kyverno/pkg/webhooks/generate"
kubeinformers "k8s.io/client-go/informers"
"k8s.io/klog"
"k8s.io/klog/klogr"
log "sigs.k8s.io/controller-runtime/pkg/log"
)
const resyncPeriod = 15 * time.Minute
var (
kubeconfig string
serverIP string
webhookTimeout int
backgroundSync int
runValidationInMutatingWebhook string
profile bool
//TODO: this has been added to backward support command line arguments
// will be removed in future and the configuration will be set only via configmaps
filterK8Resources string
excludeGroupRole string
excludeUsername string
// User FQDN as CSR CN
fqdncn bool
policyReport bool
setupLog = log.Log.WithName("setup")
)
func main() {
klog.InitFlags(nil)
log.SetLogger(klogr.New())
flag.StringVar(&filterK8Resources, "filterK8Resources", "", "k8 resource in format [kind,namespace,name] where policy is not evaluated by the admission webhook. example --filterKind \"[Deployment, kyverno, kyverno]\" --filterKind \"[Deployment, kyverno, kyverno],[Events, *, *]\"")
flag.StringVar(&excludeGroupRole, "excludeGroupRole", "", "")
flag.StringVar(&excludeUsername, "excludeUsername", "", "")
flag.IntVar(&webhookTimeout, "webhooktimeout", 3, "timeout for webhook configurations")
flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
flag.StringVar(&serverIP, "serverIP", "", "IP address where Kyverno controller runs. Only required if out-of-cluster.")
flag.StringVar(&runValidationInMutatingWebhook, "runValidationInMutatingWebhook", "", "Validation will also be done using the mutation webhook, set to 'true' to enable. Older kubernetes versions do not work properly when a validation webhook is registered.")
flag.BoolVar(&profile, "profile", false, "Set this flag to 'true', to enable profiling.")
if err := flag.Set("v", "2"); err != nil {
setupLog.Error(err, "failed to set log level")
os.Exit(1)
}
// Generate CSR with CN as FQDN due to https://github.com/kyverno/kyverno/issues/542
flag.BoolVar(&fqdncn, "fqdn-as-cn", false, "use FQDN as Common Name in CSR")
flag.Parse()
if profile {
go http.ListenAndServe("localhost:6060", nil)
}
// Policy report is enabled by default in Kyverno 1.3.0+
os.Setenv("POLICY-TYPE", common.PolicyReport)
version.PrintVersionInfo(log.Log)
cleanUp := make(chan struct{})
stopCh := signal.SetupSignalHandler()
clientConfig, err := config.CreateClientConfig(kubeconfig, log.Log)
if err != nil {
setupLog.Error(err, "Failed to build kubeconfig")
os.Exit(1)
}
// KYVERNO CRD CLIENT
// access CRD resources
// - ClusterPolicy, Policy
// - ClusterPolicyReport, PolicyReport
// - GenerateRequest
pclient, err := kyvernoclient.NewForConfig(clientConfig)
if err != nil {
setupLog.Error(err, "Failed to create client")
os.Exit(1)
}
// DYNAMIC CLIENT
// - client for all registered resources
client, err := dclient.NewClient(clientConfig, 5*time.Minute, stopCh, log.Log)
if err != nil {
setupLog.Error(err, "Failed to create client")
os.Exit(1)
}
// ======================= resource cache ====================
rCache, err := resourcecache.NewResourceCache(log.Log, clientConfig, client, []string{"configmaps"}, []string{})
if err != nil {
setupLog.Error(err, "Failed to create resource cache")
os.Exit(1)
}
rCache.RunAllInformers(log.Log)
// ===========================================================
// CRD CHECK
// - verify if Kyverno CRDs are available
if !utils.CRDInstalled(client.DiscoveryClient, log.Log) {
setupLog.Error(fmt.Errorf("CRDs not installed"), "Failed to access Kyverno CRDs")
os.Exit(1)
}
kubeClient, err := utils.NewKubeClient(clientConfig)
if err != nil {
setupLog.Error(err, "Failed to create kubernetes client")
os.Exit(1)
}
kubeInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, resyncPeriod)
kubedynamicInformer := client.NewDynamicSharedInformerFactory(resyncPeriod)
webhookRegistrationClient := webhookconfig.NewWebhookRegistrationClient(
clientConfig,
client,
serverIP,
int32(webhookTimeout),
log.Log)
// Resource Mutating Webhook Watcher
lastReqTime := checker.NewLastReqTime(log.Log.WithName("LastReqTime"))
rWebhookWatcher := webhookconfig.NewResourceWebhookRegister(
lastReqTime,
kubeInformer.Admissionregistration().V1beta1().MutatingWebhookConfigurations(),
kubeInformer.Admissionregistration().V1beta1().ValidatingWebhookConfigurations(),
webhookRegistrationClient,
runValidationInMutatingWebhook,
log.Log.WithName("ResourceWebhookRegister"),
)
// KYVERNO CRD INFORMER
// watches CRD resources:
// - ClusterPolicy, Policy
// - ClusterPolicyReport, PolicyReport
// - GenerateRequest
// - ClusterReportChangeRequest, ReportChangeRequest
pInformer := kyvernoinformer.NewSharedInformerFactoryWithOptions(pclient, resyncPeriod)
// Configuration Data
// dynamically load the configuration from configMap
// - resource filters
// if the configMap is update, the configuration will be updated :D
configData := config.NewConfigData(
kubeClient,
kubeInformer.Core().V1().ConfigMaps(),
filterK8Resources,
excludeGroupRole,
excludeUsername,
log.Log.WithName("ConfigData"),
)
// EVENT GENERATOR
// - generate event with retry mechanism
eventGenerator := event.NewEventGenerator(
client,
pInformer.Kyverno().V1().ClusterPolicies(),
log.Log.WithName("EventGenerator"))
// Policy Status Handler - deals with all logic related to policy status
statusSync := policystatus.NewSync(
pclient,
pInformer.Kyverno().V1().ClusterPolicies().Lister(),
pInformer.Kyverno().V1().Policies().Lister())
// POLICY Report GENERATOR
// -- generate policy report
var reportReqGen *policyreport.Generator
var prgen *policyreport.ReportGenerator
reportReqGen = policyreport.NewReportChangeRequestGenerator(pclient,
client,
pInformer.Kyverno().V1alpha1().ReportChangeRequests(),
pInformer.Kyverno().V1alpha1().ClusterReportChangeRequests(),
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().Policies(),
statusSync.Listener,
log.Log.WithName("ReportChangeRequestGenerator"),
)
prgen = policyreport.NewReportGenerator(client,
pInformer.Wgpolicyk8s().V1alpha1().ClusterPolicyReports(),
pInformer.Wgpolicyk8s().V1alpha1().PolicyReports(),
pInformer.Kyverno().V1alpha1().ReportChangeRequests(),
pInformer.Kyverno().V1alpha1().ClusterReportChangeRequests(),
kubeInformer.Core().V1().Namespaces(),
log.Log.WithName("PolicyReportGenerator"),
)
// POLICY CONTROLLER
// - reconciliation policy and policy violation
// - process policy on existing resources
// - status aggregator: receives stats when a policy is applied & updates the policy status
policyCtrl, err := policy.NewPolicyController(pclient,
client,
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().Policies(),
pInformer.Kyverno().V1().GenerateRequests(),
configData,
eventGenerator,
reportReqGen,
rWebhookWatcher,
kubeInformer.Core().V1().Namespaces(),
log.Log.WithName("PolicyController"),
rCache,
)
if err != nil {
setupLog.Error(err, "Failed to create policy controller")
os.Exit(1)
}
// GENERATE REQUEST GENERATOR
grgen := webhookgenerate.NewGenerator(pclient, stopCh, log.Log.WithName("GenerateRequestGenerator"))
// GENERATE CONTROLLER
// - applies generate rules on resources based on generate requests created by webhook
grc := generate.NewController(
pclient,
client,
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().GenerateRequests(),
eventGenerator,
kubedynamicInformer,
statusSync.Listener,
log.Log.WithName("GenerateController"),
configData,
rCache,
)
// GENERATE REQUEST CLEANUP
// -- cleans up the generate requests that have not been processed(i.e. state = [Pending, Failed]) for more than defined timeout
grcc := generatecleanup.NewController(
pclient,
client,
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().GenerateRequests(),
kubedynamicInformer,
log.Log.WithName("GenerateCleanUpController"),
)
pCacheController := policycache.NewPolicyCacheController(
pInformer.Kyverno().V1().ClusterPolicies(),
pInformer.Kyverno().V1().Policies(),
log.Log.WithName("PolicyCacheController"),
)
auditHandler := webhooks.NewValidateAuditHandler(
pCacheController.Cache,
eventGenerator,
statusSync.Listener,
reportReqGen,
kubeInformer.Rbac().V1().RoleBindings(),
kubeInformer.Rbac().V1().ClusterRoleBindings(),
log.Log.WithName("ValidateAuditHandler"),
configData,
rCache,
)
// CONFIGURE CERTIFICATES
tlsPair, err := client.InitTLSPemPair(clientConfig, fqdncn)
if err != nil {
setupLog.Error(err, "Failed to initialize TLS key/certificate pair")
os.Exit(1)
}
// WEBHOOK REGISTRATION
// - mutating,validatingwebhookconfiguration (Policy)
// - verifymutatingwebhookconfiguration (Kyverno Deployment)
// resource webhook confgiuration is generated dynamically in the webhook server and policy controller
// based on the policy resources created
if err = webhookRegistrationClient.Register(); err != nil {
setupLog.Error(err, "Failed to register Admission webhooks")
os.Exit(1)
}
openAPIController, err := openapi.NewOpenAPIController()
if err != nil {
setupLog.Error(err, "Failed to create openAPIController")
os.Exit(1)
}
// Sync openAPI definitions of resources
openAPISync := openapi.NewCRDSync(client, openAPIController)
supportMutateValidate := utils.HigherThanKubernetesVersion(client, log.Log, 1, 14, 0)
// WEBHOOK
// - https server to provide endpoints called based on rules defined in Mutating & Validation webhook configuration
// - reports the results based on the response from the policy engine:
// -- annotations on resources with update details on mutation JSON patches
// -- generate policy violation resource
// -- generate events on policy and resource
server, err := webhooks.NewWebhookServer(
pclient,
client,
tlsPair,
pInformer.Kyverno().V1().ClusterPolicies(),
kubeInformer.Rbac().V1().RoleBindings(),
kubeInformer.Rbac().V1().ClusterRoleBindings(),
kubeInformer.Rbac().V1().Roles(),
kubeInformer.Rbac().V1().ClusterRoles(),
eventGenerator,
pCacheController.Cache,
webhookRegistrationClient,
statusSync.Listener,
configData,
reportReqGen,
grgen,
rWebhookWatcher,
auditHandler,
supportMutateValidate,
cleanUp,
log.Log.WithName("WebhookServer"),
openAPIController,
rCache,
)
if err != nil {
setupLog.Error(err, "Failed to create webhook server")
os.Exit(1)
}
// Start the components
pInformer.Start(stopCh)
kubeInformer.Start(stopCh)
kubedynamicInformer.Start(stopCh)
go reportReqGen.Run(2, stopCh)
go prgen.Run(1, stopCh)
go grgen.Run(1)
go rWebhookWatcher.Run(stopCh)
go configData.Run(stopCh)
go policyCtrl.Run(2, stopCh)
go eventGenerator.Run(3, stopCh)
go grc.Run(1, stopCh)
go grcc.Run(1, stopCh)
go statusSync.Run(1, stopCh)
go pCacheController.Run(1, stopCh)
go auditHandler.Run(10, stopCh)
openAPISync.Run(1, stopCh)
// verifies if the admission control is enabled and active
// resync: 60 seconds
// deadline: 60 seconds (send request)
// max deadline: deadline*3 (set the deployment annotation as false)
server.RunAsync(stopCh)
<-stopCh
// by default http.Server waits indefinitely for connections to return to idle and then shuts down
// adding a threshold will handle zombie connections
// adjust the context deadline to 5 seconds
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer func() {
cancel()
}()
// cleanup webhookconfigurations followed by webhook shutdown
server.Stop(ctx)
// resource cleanup
// remove webhook configurations
<-cleanUp
setupLog.Info("Kyverno shutdown successful")
}