blob: d8f07cf5c9b3fb64172c712f767c67efdbf7f172 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 manager
import (
"context"
"crypto/tls"
"os"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
"sigs.k8s.io/gateway-api/apis/v1beta1"
"github.com/apache/apisix-ingress-controller/api/v1alpha1"
apiv2 "github.com/apache/apisix-ingress-controller/api/v2"
"github.com/apache/apisix-ingress-controller/internal/controller"
"github.com/apache/apisix-ingress-controller/internal/controller/config"
"github.com/apache/apisix-ingress-controller/internal/controller/status"
"github.com/apache/apisix-ingress-controller/internal/manager/readiness"
"github.com/apache/apisix-ingress-controller/internal/manager/server"
"github.com/apache/apisix-ingress-controller/internal/provider"
_ "github.com/apache/apisix-ingress-controller/internal/provider/init"
_ "github.com/apache/apisix-ingress-controller/pkg/metrics"
)
var (
scheme = runtime.NewScheme()
)
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
if err := gatewayv1.Install(scheme); err != nil {
panic(err)
}
if err := gatewayv1alpha2.Install(scheme); err != nil {
panic(err)
}
if err := v1alpha1.AddToScheme(scheme); err != nil {
panic(err)
}
if err := apiv2.AddToScheme(scheme); err != nil {
panic(err)
}
if err := v1beta1.Install(scheme); err != nil {
panic(err)
}
// +kubebuilder:scaffold:scheme
}
func Run(ctx context.Context, logger logr.Logger) error {
cfg := config.ControllerConfig
setupLog := ctrl.LoggerFrom(ctx).WithName("setup")
// if the enable-http2 flag is false (the default), http/2 should be disabled
// due to its vulnerabilities. More specifically, disabling http/2 will
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
// Rapid Reset CVEs. For more information see:
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
// - https://github.com/advisories/GHSA-4374-p667-p6c8
disableHTTP2 := func(c *tls.Config) {
setupLog.Info("disabling http/2")
c.NextProtos = []string{"http/1.1"}
}
var tlsOpts []func(*tls.Config)
if !cfg.EnableHTTP2 {
tlsOpts = append(tlsOpts, disableHTTP2)
}
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
// More info:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/metrics/server
// - https://book.kubebuilder.io/reference/metrics.html
metricsServerOptions := metricsserver.Options{
BindAddress: cfg.MetricsAddr,
SecureServing: cfg.SecureMetrics,
// TODO(user): TLSOpts is used to allow configuring the TLS config used for the server. If certificates are
// not provided, self-signed certificates will be generated by default. This option is not recommended for
// production environments as self-signed certificates do not offer the same level of trust and security
// as certificates issued by a trusted Certificate Authority (CA). The primary risk is potentially allowing
// unauthorized access to sensitive metrics data. Consider replacing with CertDir, CertName, and KeyName
// to provide certificates, ensuring the server communicates using trusted and secure certificates.
TLSOpts: tlsOpts,
}
if cfg.SecureMetrics {
// FilterProvider is used to protect the metrics endpoint with authn/authz.
// These configurations ensure that only authorized users and service accounts
// can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/metrics/filters#WithAuthenticationAndAuthorization
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
}
namespace := os.Getenv("POD_NAMESPACE")
if namespace == "" {
namespace = "default"
}
mgrOptions := ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
HealthProbeBindAddress: cfg.ProbeAddr,
LeaderElection: !config.ControllerConfig.LeaderElection.Disable,
LeaderElectionID: cfg.LeaderElectionID,
LeaderElectionNamespace: namespace,
LeaseDuration: ptr.To(config.ControllerConfig.LeaderElection.LeaseDuration.Duration),
RenewDeadline: ptr.To(config.ControllerConfig.LeaderElection.RenewDeadline.Duration),
RetryPeriod: ptr.To(config.ControllerConfig.LeaderElection.RetryPeriod.Duration),
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
// when the Manager ends. This requires the binary to immediately end when the
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
// speeds up voluntary leader transitions as the new leader don't have to wait
// LeaseDuration time first.
//
// In the default scaffold provided, the program ends immediately after
// the manager stops, so would be fine to enable this option. However,
// if you are doing or is intended to do any operation such as perform cleanups
// after the manager stops then its usage might be unsafe.
// LeaderElectionReleaseOnCancel: true,
}
if cfg.Webhook != nil && cfg.Webhook.Enable {
webhookServer := webhook.NewServer(webhook.Options{
Port: cfg.Webhook.Port,
CertDir: cfg.Webhook.TLSCertDir,
CertName: cfg.Webhook.TLSCertFile,
KeyName: cfg.Webhook.TLSKeyFile,
})
mgrOptions.WebhookServer = webhookServer
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), mgrOptions)
if err != nil {
setupLog.Error(err, "unable to start manager")
return err
}
readier := readiness.NewReadinessManager(mgr.GetClient(), logger)
registerReadinessGVK(mgr.GetClient(), readier)
if err := mgr.Add(readier); err != nil {
setupLog.Error(err, "unable to add readiness manager")
}
updater := status.NewStatusUpdateHandler(ctrl.LoggerFrom(ctx).WithName("status").WithName("updater"), mgr.GetClient())
if err := mgr.Add(updater); err != nil {
setupLog.Error(err, "unable to add status updater")
return err
}
providerType := string(config.ControllerConfig.ProviderConfig.Type)
providerOptions := &provider.Options{
SyncTimeout: config.ControllerConfig.ExecADCTimeout.Duration,
SyncPeriod: config.ControllerConfig.ProviderConfig.SyncPeriod.Duration,
InitSyncDelay: config.ControllerConfig.ProviderConfig.InitSyncDelay.Duration,
}
provider, err := provider.New(providerType, logger, updater.Writer(), readier, providerOptions)
if err != nil {
setupLog.Error(err, "unable to create provider")
return err
}
if cfg.EnableServer {
srv := server.NewServer(config.ControllerConfig.ServerAddr)
srv.Register("/debug", provider)
if err := mgr.Add(srv); err != nil {
setupLog.Error(err, "unable to add debug server to manager")
return err
}
}
if err := mgr.Add(provider); err != nil {
setupLog.Error(err, "unable to add provider to manager")
return err
}
setupLog.Info("check ReferenceGrants is enabled")
_, err = mgr.GetRESTMapper().KindsFor(schema.GroupVersionResource{
Group: v1beta1.GroupVersion.Group,
Version: v1beta1.GroupVersion.Version,
Resource: "referencegrants",
})
if err != nil {
setupLog.Info("CRD ReferenceGrants is not installed", "err", err)
}
controller.SetEnableReferenceGrant(err == nil)
setupLog.Info("setting up controllers")
controllers, err := setupControllers(ctx, mgr, provider, updater.Writer(), readier)
if err != nil {
setupLog.Error(err, "unable to set up controllers")
return err
}
for _, c := range controllers {
if err := c.SetupWithManager(mgr); err != nil {
return err
}
}
// +kubebuilder:scaffold:builder
if cfg.Webhook != nil && cfg.Webhook.Enable {
setupLog.Info("setting up webhooks")
if err := setupWebhooks(ctx, mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "Ingress")
return err
}
} else {
setupLog.Info("webhooks disabled, skipping webhook setup")
}
setupLog.Info("setting up health checks")
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
return err
}
setupLog.Info("setting up ready checks")
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
return err
}
setupLog.Info("starting controller manager")
return mgr.Start(ctrl.SetupSignalHandler())
}