Skip to content
Open
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
16 changes: 16 additions & 0 deletions pkg/controller/apiserver/apiserver_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ func Add(mgr manager.Manager, opts options.ControllerOptions) error {
}
}

if err = utils.AddSecretsWatch(c, render.VoltronLinseedPublicCert, common.OperatorNamespace()); err != nil {
return fmt.Errorf("apiserver-controller failed to watch the Secret resource: %v", err)
}

// Watch for changes to authentication
err = c.WatchObject(&operatorv1.Authentication{}, &handler.EnqueueRequestForObject{})
if err != nil {
Expand Down Expand Up @@ -394,6 +398,17 @@ func (r *ReconcileAPIServer) Reconcile(ctx context.Context, request reconcile.Re
trustedBundle.AddCertificates(prometheusCertificate)
}

if managementClusterConnection != nil {
voltronLinseedCert, err := certificateManager.GetCertificate(r.client, render.VoltronLinseedPublicCert, common.OperatorNamespace())
if err != nil {
r.status.SetDegraded(operatorv1.ResourceReadError, fmt.Sprintf("Failed to retrieve %s", render.VoltronLinseedPublicCert), err, reqLogger)
return reconcile.Result{}, err
}
if voltronLinseedCert != nil {
trustedBundle.AddCertificates(voltronLinseedCert)
}
}

var authenticationCR *operatorv1.Authentication
// Fetch the Authentication spec. If present, we use it to configure user authentication.
authenticationCR, err = utils.GetAuthentication(ctx, r.client)
Expand Down Expand Up @@ -480,6 +495,7 @@ func (r *ReconcileAPIServer) Reconcile(ctx context.Context, request reconcile.Re
MultiTenant: r.opts.MultiTenant,
KeyValidatorConfig: keyValidatorConfig,
KubernetesVersion: r.opts.KubernetesVersion,
ClusterDomain: r.opts.ClusterDomain,
RequiresAggregationServer: !r.opts.UseV3CRDs,
QueryServerTLSKeyPairCertificateManagementOnly: queryServerTLSSecretCertificateManagementOnly,
}
Expand Down
59 changes: 54 additions & 5 deletions pkg/render/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/tigera/operator/pkg/controller/k8sapi"
"github.com/tigera/operator/pkg/render/common/authentication"
rcomp "github.com/tigera/operator/pkg/render/common/components"
relasticsearch "github.com/tigera/operator/pkg/render/common/elasticsearch"
rmeta "github.com/tigera/operator/pkg/render/common/meta"
"github.com/tigera/operator/pkg/render/common/networkpolicy"
"github.com/tigera/operator/pkg/render/common/podaffinity"
Expand Down Expand Up @@ -140,6 +141,7 @@ type APIServerConfiguration struct {
MultiTenant bool
KeyValidatorConfig authentication.KeyValidatorConfig
KubernetesVersion *common.VersionInfo
ClusterDomain string

// Whether or not we should run the aggregation API server for projectcalico.org/v3 APIs
// as part of this component.
Expand Down Expand Up @@ -322,6 +324,11 @@ func (c *apiServerComponent) Objects() ([]client.Object, []client.Object) {
} else {
objsToDelete = append(objsToDelete, &admregv1.MutatingWebhookConfiguration{ObjectMeta: metav1.ObjectMeta{Name: common.SidecarMutatingWebhookConfigName}})
}
if c.cfg.ManagementClusterConnection != nil {
namespacedEnterpriseObjects = append(namespacedEnterpriseObjects,
c.externalLinseedRoleBinding(),
)
}

// Compile the final arrays based on the variant.
if c.cfg.Installation.Variant.IsEnterprise() {
Expand Down Expand Up @@ -1272,16 +1279,18 @@ func (c *apiServerComponent) queryServerContainer() corev1.Container {
env = append(env, c.cfg.KeyValidatorConfig.RequiredEnv("")...)
}

// Linseed client configuration for policy activity enrichment.
linseedURL := fmt.Sprintf("https://tigera-linseed.%s.svc", ElasticsearchNamespace)
if c.cfg.ManagementClusterConnection != nil {
linseedURL = "https://guardian.calico-system.svc"
}
linseedURL := relasticsearch.LinseedEndpoint(c.SupportedOSType(), c.cfg.ClusterDomain, ElasticsearchNamespace, c.cfg.ManagementClusterConnection != nil, false)
env = append(env,
corev1.EnvVar{Name: "LINSEED_URL", Value: linseedURL},
corev1.EnvVar{Name: "LINSEED_CLIENT_CERT", Value: fmt.Sprintf("/%s/tls.crt", tlsSecret.GetName())},
corev1.EnvVar{Name: "LINSEED_CLIENT_KEY", Value: fmt.Sprintf("/%s/tls.key", tlsSecret.GetName())},
)
if c.cfg.ManagementClusterConnection != nil {
env = append(env,
corev1.EnvVar{Name: "CLUSTER_ID", Value: ""},
corev1.EnvVar{Name: "LINSEED_TOKEN", Value: GetLinseedTokenPath(true)},
)
}
if c.cfg.TrustedBundle != nil {
env = append(env, corev1.EnvVar{Name: "LINSEED_CA", Value: c.cfg.TrustedBundle.MountPath()})
}
Expand All @@ -1302,6 +1311,12 @@ func (c *apiServerComponent) queryServerContainer() corev1.Container {
if c.cfg.TrustedBundle != nil {
volumeMounts = append(volumeMounts, c.cfg.TrustedBundle.VolumeMounts(c.SupportedOSType())...)
}
if c.cfg.ManagementClusterConnection != nil {
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: LinseedTokenVolumeName,
MountPath: LinseedVolumeMountPath,
})
}

container := corev1.Container{
Name: string(TigeraAPIServerQueryServerContainerName),
Expand All @@ -1324,6 +1339,28 @@ func (c *apiServerComponent) queryServerContainer() corev1.Container {
return container
}

func (c *apiServerComponent) externalLinseedRoleBinding() *rbacv1.RoleBinding {
return &rbacv1.RoleBinding{
TypeMeta: metav1.TypeMeta{Kind: "RoleBinding", APIVersion: "rbac.authorization.k8s.io/v1"},
ObjectMeta: metav1.ObjectMeta{
Name: "tigera-linseed",
Namespace: APIServerNamespace,
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: TigeraLinseedSecretsClusterRole,
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: GuardianServiceAccountName,
Namespace: GuardianNamespace,
},
},
}
}

// apiServerVolumes creates the volumes used by the API server deployment.
func (c *apiServerComponent) apiServerVolumes() []corev1.Volume {
volumes := []corev1.Volume{
Expand Down Expand Up @@ -1367,6 +1404,18 @@ func (c *apiServerComponent) apiServerVolumes() []corev1.Volume {
volumes = append(volumes, c.cfg.TrustedBundle.Volume())
}

if c.cfg.ManagementClusterConnection != nil {
volumes = append(volumes, corev1.Volume{
Name: LinseedTokenVolumeName,
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: fmt.Sprintf(LinseedTokenSecret, "calico-apiserver"),
Items: []corev1.KeyToPath{{Key: LinseedTokenKey, Path: LinseedTokenSubPath}},
},
},
})
}

return volumes
}

Expand Down
45 changes: 45 additions & 0 deletions pkg/render/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,51 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() {
Expect(deploy.Spec.Template.Spec.Affinity).To(Equal(podaffinity.NewPodAntiAffinity("calico-apiserver", []string{"calico-system", "tigera-system", "calico-apiserver"})))
})

It("should render Linseed routing for the queryserver when ManagementClusterConnection is set", func() {
cfg.ManagementClusterConnection = &operatorv1.ManagementClusterConnection{}
cfg.ClusterDomain = "cluster.local"

component, err := render.APIServer(cfg)
Expect(err).To(BeNil(), "Expected APIServer to create successfully %s", err)
resources, _ := component.Objects()

rb, ok := rtest.GetResource(resources, "tigera-linseed", "calico-system", "rbac.authorization.k8s.io", "v1", "RoleBinding").(*rbacv1.RoleBinding)
Expect(ok).To(BeTrue(), "expected tigera-linseed RoleBinding in calico-system")
Expect(rb.RoleRef.Name).To(Equal("tigera-linseed-secrets"))
Expect(rb.Subjects).To(ConsistOf(rbacv1.Subject{
Kind: "ServiceAccount",
Name: render.GuardianServiceAccountName,
Namespace: render.GuardianNamespace,
}))

deploy, ok := rtest.GetResource(resources, "calico-apiserver", "calico-system", "apps", "v1", "Deployment").(*appsv1.Deployment)
Expect(ok).To(BeTrue())
var qs *corev1.Container
for i := range deploy.Spec.Template.Spec.Containers {
if deploy.Spec.Template.Spec.Containers[i].Name == "tigera-queryserver" {
qs = &deploy.Spec.Template.Spec.Containers[i]
}
}
Expect(qs).NotTo(BeNil())
Expect(qs.Env).To(ContainElement(corev1.EnvVar{Name: "LINSEED_URL", Value: "https://guardian.calico-system.svc"}))
Expect(qs.Env).To(ContainElement(corev1.EnvVar{Name: "CLUSTER_ID", Value: ""}))
Expect(qs.Env).To(ContainElement(corev1.EnvVar{Name: "LINSEED_TOKEN", Value: "/var/run/secrets/tigera.io/linseed/token"}))
Expect(qs.VolumeMounts).To(ContainElement(corev1.VolumeMount{
Name: render.LinseedTokenVolumeName,
MountPath: render.LinseedVolumeMountPath,
}))

var tokenVol *corev1.Volume
for i := range deploy.Spec.Template.Spec.Volumes {
if deploy.Spec.Template.Spec.Volumes[i].Name == render.LinseedTokenVolumeName {
tokenVol = &deploy.Spec.Template.Spec.Volumes[i]
}
}
Expect(tokenVol).NotTo(BeNil())
Expect(tokenVol.Secret).NotTo(BeNil())
Expect(tokenVol.Secret.SecretName).To(Equal("calico-apiserver-tigera-linseed-token"))
})

Context("calico-system rendering", func() {
policyName := types.NamespacedName{Name: "calico-system.apiserver-access", Namespace: "calico-system"}

Expand Down
Loading