Files
netbird-kubernetes-operator/internal/controller/httproute_controller.go
T
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Philip Laine
b77bcd74ab Bump github.com/netbirdio/netbird from 0.66.4 to 0.69.0 (#187)
Bumps
[github.com/netbirdio/netbird](https://github.com/netbirdio/netbird)
from 0.66.4 to 0.69.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/netbirdio/netbird/releases">github.com/netbirdio/netbird's
releases</a>.</em></p>
<blockquote>
<h2>v0.69.0</h2>
<h2>Release Notes for v0.69.0</h2>
<h3>What's New</h3>
<p><strong>Reverse Proxy IP Reputation Integration</strong>
Now you can use CrowdSec to block malicious traffic based on IP
reputation on your exposed service in the reverse proxy.</p>
<p>This feature requires self-hosted installations to add another
container to their deployment. See instructions in the <a
href="https://docs.netbird.io/selfhosted/migration/enable-reverse-proxy#step-7-optional-enable-crowd-sec-ip-reputation">reverse
proxy migration documentation</a>.</p>
<blockquote>
<p>For Cloud users, support is coming soon.</p>
</blockquote>
<p>Learn more about <a
href="https://docs.netbird.io/manage/reverse-proxy">here</a>.</p>
<p><strong>macOS p2p connectivity improvements</strong>
We've improved macOS p2p connectivity with a better routing exclusion
mechanism to avoid loops. Now the client doesn't add /32 routes per
remote candidate addresses avoiding limitations on accessing remote
peer's local addresses via tunnel connections. Learn more about <a
href="https://redirect.github.com/netbirdio/netbird/pull/5918">this
change</a>.</p>
<blockquote>
<p>To use the old behavior run:</p>
<p><code>sudo netbird service reconfigure --service-env
&quot;NB_USE_LEGACY_ROUTING=true&quot;</code></p>
</blockquote>
<h4>Client Improvements</h4>
<ul>
<li>Added <strong>PCP support</strong>. This change adds support for the
PCP protocol to the client to improve the rate of P2P connectivity.
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5219">netbirdio/netbird#5219</a></li>
<li>Added <strong>--disable-networks flag</strong> to block network
selection for users.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5896">netbirdio/netbird#5896</a></li>
<li>Fixed <strong>clearing service env vars with --service-env
&quot;&quot;</strong>.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5893">netbirdio/netbird#5893</a></li>
<li>Guarded against <strong>container DNAT bypass of ACL rules in
iptables</strong>.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5697">netbirdio/netbird#5697</a></li>
<li>Populated <strong>NetworkAddresses on iOS for posture
checks</strong>.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5900">netbirdio/netbird#5900</a></li>
<li>Reconnected <strong>conntrack netlink listener on error</strong>.<br
/>
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5885">netbirdio/netbird#5885</a></li>
<li>Replaced <strong>exclusion routes with scoped default + IP_BOUND_IF
on macOS</strong>.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5918">netbirdio/netbird#5918</a></li>
<li>Fixed <strong>incorrect SSH client config combining Host and Match
directives</strong>.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5903">netbirdio/netbird#5903</a></li>
<li>Fixed <strong>WGIface.Close deadlock when DNS filter hook re-enters
GetDevice</strong>.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5916">netbirdio/netbird#5916</a></li>
</ul>
<h4>Management Improvements</h4>
<ul>
<li>Enforced <strong>peer or peer groups requirement for network
routers</strong>.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5894">netbirdio/netbird#5894</a></li>
<li>Reused <strong>single cache store across all management server
consumers</strong>.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5889">netbirdio/netbird#5889</a></li>
<li>Fixed <strong>lint error on Google Workspace
integration</strong>.<br />
<a
href="https://redirect.github.com/netbirdio/netbird/pull/5907">netbirdio/netbird#5907</a></li>
</ul>
<h4>Proxy Enhancements</h4>
<ul>
<li>Added <strong>CrowdSec IP reputation integration for reverse
proxy</strong>.</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/netbirdio/netbird/commit/3098f48b25e2a613d8f70466243d992c498e0fd2"><code>3098f48</code></a>
[client] fix ios network addresses mac filter (<a
href="https://redirect.github.com/netbirdio/netbird/issues/5906">#5906</a>)</li>
<li><a
href="https://github.com/netbirdio/netbird/commit/7f023ce8014084385e677e67c541677336e25431"><code>7f023ce</code></a>
[client] Android debug bundle support (<a
href="https://redirect.github.com/netbirdio/netbird/issues/5888">#5888</a>)</li>
<li><a
href="https://github.com/netbirdio/netbird/commit/e3611265159bd372f77ca404d32c86127e036525"><code>e361126</code></a>
[client] Fix WGIface.Close deadlock when DNS filter hook re-enters
GetDevice ...</li>
<li><a
href="https://github.com/netbirdio/netbird/commit/95213f715714177b7c9672b3f264f84c9a31cf04"><code>95213f7</code></a>
[client] Use Match host+exec instead of Host+Match in SSH client config
(<a
href="https://redirect.github.com/netbirdio/netbird/issues/5903">#5903</a>)</li>
<li><a
href="https://github.com/netbirdio/netbird/commit/2e0e3a3601d90a5d7a6b39dc16ebde2522fdf580"><code>2e0e3a3</code></a>
[client] Replace exclusion routes with scoped default + IP_BOUND_IF on
macOS ...</li>
<li><a
href="https://github.com/netbirdio/netbird/commit/8ae8f2098fb4bb7a9177b3f2d8d908a413b8fc49"><code>8ae8f20</code></a>
[management] chores: fix lint error on google workspace (<a
href="https://redirect.github.com/netbirdio/netbird/issues/5907">#5907</a>)</li>
<li><a
href="https://github.com/netbirdio/netbird/commit/a39787d6796ee26e1f6ad9c67ea33a9da7bf8d66"><code>a39787d</code></a>
[infrastructure] Add CrowdSec LAPI container to self-hosted setup script
(<a
href="https://redirect.github.com/netbirdio/netbird/issues/5880">#5880</a>)</li>
<li><a
href="https://github.com/netbirdio/netbird/commit/53b04e512af0d94f619e870467115c43d200830b"><code>53b04e5</code></a>
[management] Reuse a single cache store across all management server
consumer...</li>
<li><a
href="https://github.com/netbirdio/netbird/commit/633dde8d1f5985afc52071462c42857cb844b9ac"><code>633dde8</code></a>
[client] Reconnect conntrack netlink listener on error (<a
href="https://redirect.github.com/netbirdio/netbird/issues/5885">#5885</a>)</li>
<li><a
href="https://github.com/netbirdio/netbird/commit/7e4542adde54ebe71a3a47f5228c5915c9c1052a"><code>7e4542a</code></a>
fix(client): populate NetworkAddresses on iOS for posture checks (<a
href="https://redirect.github.com/netbirdio/netbird/issues/5900">#5900</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/netbirdio/netbird/compare/v0.66.4...v0.69.0">compare
view</a></li>
</ul>
</details>
<br />

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Philip Laine <philip.laine@gmail.com>
2026-04-23 09:50:41 +02:00

274 lines
7.3 KiB
Go

package controller
import (
"context"
"fmt"
"time"
netbird "github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
"github.com/netbirdio/kubernetes-operator/internal/gatewayutil"
"github.com/netbirdio/kubernetes-operator/internal/util"
)
const (
HTTPRouteFinalizer = "gateway.netbird.io/httproute"
)
type HTTPRouteReconciler struct {
client.Client
Netbird *netbird.Client
ClusterDNS string
}
// nolint:gocyclo
func (r *HTTPRouteReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := ctrl.Log.WithName("HTTPRoute").WithValues("namespace", req.Namespace, "name", req.Name)
hr := gatewayv1.HTTPRoute{}
err := r.Get(ctx, req.NamespacedName, &hr)
if err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if !hr.DeletionTimestamp.IsZero() {
return r.reconcileDelete(ctx, hr)
}
for _, parent := range hr.Spec.ParentRefs {
gw, err := gatewayutil.GetParentGateway(ctx, r.Client, parent, hr.Namespace, GatewayControllerName)
if err != nil {
return ctrl.Result{}, err
}
if gw == nil {
continue
}
if !meta.IsStatusConditionTrue(gw.Status.Conditions, string(gatewayv1.GatewayConditionProgrammed)) {
logger.Info("gateway is not ready", "name", gw.ObjectMeta.Name)
continue
}
nbrp, err := gatewayutil.GetGatewayRoutingPeer(ctx, r.Client, *gw)
if err != nil {
return ctrl.Result{}, err
}
if controllerutil.AddFinalizer(&hr, HTTPRouteFinalizer) {
err = r.Client.Update(ctx, &hr)
if err != nil {
return ctrl.Result{}, err
}
}
// Create network resources.
svcIdx := map[string]corev1.Service{}
for _, rule := range hr.Spec.Rules {
for _, ref := range rule.BackendRefs {
key := client.ObjectKey{Namespace: hr.Namespace, Name: string(ref.Name)}
var svc corev1.Service
err := r.Client.Get(ctx, key, &svc)
if err != nil {
return ctrl.Result{}, err
}
svcIdx[svc.Name] = svc
}
}
for _, svc := range svcIdx {
nbResource := netbirdiov1.NBResource{
ObjectMeta: metav1.ObjectMeta{
Name: svc.Name,
Namespace: svc.Namespace,
},
}
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, &nbResource, func() error {
err = controllerutil.SetControllerReference(&svc, &nbResource, r.Scheme(), controllerutil.WithBlockOwnerDeletion(false))
if err != nil {
return err
}
err = controllerutil.SetOwnerReference(&hr, &nbResource, r.Scheme())
if err != nil {
return err
}
nbResource.Spec = netbirdiov1.NBResourceSpec{
Name: svc.Name,
NetworkID: *nbrp.Status.NetworkID,
Address: fmt.Sprintf("%s.%s.%s", svc.Name, svc.Namespace, r.ClusterDNS),
Groups: []string{},
}
return nil
})
if err != nil {
return ctrl.Result{}, err
}
}
targets := []api.ServiceTarget{}
for _, svc := range svcIdx {
var nbResource netbirdiov1.NBResource
err := r.Client.Get(ctx, client.ObjectKeyFromObject(&svc), &nbResource)
if err != nil {
return ctrl.Result{}, err
}
ready := func() bool {
for _, cond := range nbResource.Status.Conditions {
if cond.Type == netbirdiov1.NBSetupKeyReady && cond.Status == corev1.ConditionTrue {
return true
}
}
return false
}()
if !ready {
return ctrl.Result{RequeueAfter: 1 * time.Second}, nil
}
target := api.ServiceTarget{
Enabled: true,
Path: nil,
TargetId: *nbResource.Status.NetworkResourceID,
Protocol: "http",
TargetType: "domain",
}
targets = append(targets, target)
}
// Create proxy service.
proxyServices, err := r.Netbird.ReverseProxyServices.List(ctx)
if err != nil {
return ctrl.Result{}, err
}
for _, hostname := range hr.Spec.Hostnames {
proxyReq := api.PostApiReverseProxiesServicesJSONRequestBody{
Domain: string(hostname),
Enabled: true,
Name: string(hostname),
PassHostHeader: util.Ptr(false),
RewriteRedirects: util.Ptr(false),
Targets: &targets,
}
err := func() error {
for _, proxyService := range proxyServices {
if proxyService.Domain != string(hostname) {
continue
}
_, err := r.Netbird.ReverseProxyServices.Update(ctx, proxyService.Id, proxyReq)
if err != nil {
return err
}
}
_, err := r.Netbird.ReverseProxyServices.Create(ctx, proxyReq)
if err != nil {
return err
}
return nil
}()
if err != nil {
return ctrl.Result{}, err
}
}
}
return ctrl.Result{}, nil
}
func (r *HTTPRouteReconciler) reconcileDelete(ctx context.Context, hr gatewayv1.HTTPRoute) (ctrl.Result, error) {
// Index all proxy services.
proxyServices, err := r.Netbird.ReverseProxyServices.List(ctx)
if err != nil {
return ctrl.Result{}, err
}
proxyIdx := map[string]string{}
for _, proxyService := range proxyServices {
proxyIdx[proxyService.Domain] = proxyService.Id
}
for _, parent := range hr.Spec.ParentRefs {
gw, err := gatewayutil.GetParentGateway(ctx, r.Client, parent, hr.Namespace, GatewayControllerName)
if err != nil {
return ctrl.Result{}, err
}
if gw == nil {
continue
}
// Remove the resource from the resource.
svcIdx := map[string]corev1.Service{}
for _, rule := range hr.Spec.Rules {
for _, ref := range rule.BackendRefs {
key := client.ObjectKey{Namespace: hr.Namespace, Name: string(ref.Name)}
var svc corev1.Service
err := r.Client.Get(ctx, key, &svc)
if kerrors.IsNotFound(err) {
continue
}
if err != nil {
return ctrl.Result{}, err
}
svcIdx[svc.Name] = svc
}
}
for _, svc := range svcIdx {
var nbResource netbirdiov1.NBResource
err = r.Client.Get(ctx, client.ObjectKeyFromObject(&svc), &nbResource)
if err != nil {
return ctrl.Result{}, err
}
err = controllerutil.RemoveOwnerReference(&hr, &nbResource, r.Scheme())
if err != nil {
return ctrl.Result{}, err
}
if len(nbResource.OwnerReferences) > 1 {
err = r.Client.Update(ctx, &nbResource)
if err != nil {
return ctrl.Result{}, err
}
} else {
// TODO: Precondition that nothing has changed.
err := r.Client.Delete(ctx, &nbResource)
if err != nil {
return ctrl.Result{}, err
}
}
}
// Remove the target from the proxy service.
for _, hostname := range hr.Spec.Hostnames {
id, ok := proxyIdx[string(hostname)]
if !ok {
continue
}
err = r.Netbird.ReverseProxyServices.Delete(ctx, id)
if err != nil && !netbird.IsNotFound(err) {
return ctrl.Result{}, err
}
}
}
if controllerutil.RemoveFinalizer(&hr, HTTPRouteFinalizer) {
err := r.Client.Update(ctx, &hr)
if err != nil {
return ctrl.Result{}, err
}
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *HTTPRouteReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&gatewayv1.HTTPRoute{}).
Complete(r)
}