Compare commits

..

2 Commits

Author SHA1 Message Date
pascal
80bfa33f71 validate header auth on proxy 2026-08-20 14:11:32 +02:00
dmitri-netbird
a144e8c144 [client, management] switch to go.uber.org/mock (#7253)
* switch to go.uber.org/mock/gomock

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* updated go:generate commands + regenerated mocks

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* update go:generate mockgen commands

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* removed duplicate import

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

* fix go:generate

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>

---------

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
2026-08-20 11:53:19 +02:00
72 changed files with 1004 additions and 1139 deletions

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel"
"google.golang.org/grpc"

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"

View File

@@ -5,7 +5,7 @@ import (
"net/netip"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/stretchr/testify/require"

View File

@@ -4,7 +4,7 @@ import (
"net/netip"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/google/gopacket/layers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -4,7 +4,7 @@ import (
"net"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"

View File

@@ -8,7 +8,7 @@ import (
"net/netip"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
gomock "go.uber.org/mock/gomock"
)
// MockPacketFilter is a mock of PacketFilter interface.

View File

@@ -8,7 +8,7 @@ import (
os "os"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
gomock "go.uber.org/mock/gomock"
tun "golang.zx2c4.com/wireguard/tun"
)

View File

@@ -5,7 +5,7 @@ import (
"net/netip"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -7,7 +7,7 @@ package mocks
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
gomock "go.uber.org/mock/gomock"
wgdevice "golang.zx2c4.com/wireguard/device"
"github.com/netbirdio/netbird/client/iface/device"

View File

@@ -4,7 +4,7 @@ import (
"net"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/miekg/dns"

View File

@@ -9,7 +9,7 @@ import (
"os"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"

View File

@@ -12,7 +12,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

View File

@@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel"

4
go.mod
View File

@@ -62,7 +62,6 @@ require (
github.com/goccy/go-yaml v1.18.0
github.com/godbus/dbus/v5 v5.2.2
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.7.0
github.com/google/gopacket v1.1.19
github.com/google/nftables v0.3.0
@@ -217,6 +216,7 @@ require (
github.com/gobwas/pool v0.2.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.8 // indirect
@@ -340,3 +340,5 @@ replace github.com/dexidp/dex/api/v2 => github.com/netbirdio/dex/api/v2 v2.0.0-2
replace github.com/mailru/easyjson => github.com/netbirdio/easyjson v0.9.0
replace github.com/wailsapp/wails/v3 => github.com/netbirdio/wails/v3 v3.0.0-beta.3.0.20260810103952-24e716aea4db
tool go.uber.org/mock/mockgen

View File

@@ -15,12 +15,6 @@ set -o pipefail
# 2. Postgres migration — add Postgres, migrate SQLite data via migrate-store.
# 3. Traffic flow — add NATS + flow-enricher + flow-receiver.
#
# Step 2 is skipped when the deployment already runs on Postgres
# (server.store.engine: postgres in config.yaml). Nothing is provisioned or
# migrated in that case and the store config is left exactly as the operator
# wrote it — the enterprise image reads the same Postgres the community image
# did. Such a deployment gets the image swap, and can still opt into step 3.
#
# If any step fails once the stack has been touched, the script rolls itself
# back automatically: generated files are removed, the Postgres volume this run
# created is dropped, and the original deployment is started again.
@@ -44,18 +38,6 @@ ENV_BACKUP=""
PG_VOLUME_NAME=""
BACKUP_DIR=""
# Store state. STORE_ENGINE is what the deployment runs on today; when it is
# already postgres, MIGRATE_POSTGRES stays "no" and nothing is provisioned.
# POSTGRES_SERVICE is empty when Postgres lives outside this compose project.
STORE_ENGINE=""
EXISTING_POSTGRES="no"
POSTGRES_DSN=""
POSTGRES_SERVICE=""
POSTGRES_DEPENDS_CONDITION="service_healthy"
# Whether this run needs to generate config.yaml.enterprise at all. A pure
# image swap does not.
ENTERPRISE_CONFIG="no"
NETBIRD_EULA_URL="https://netbird.io/self-hosted-EULA"
check_docker_compose() {
@@ -210,85 +192,6 @@ detect_exposed_address() {
yq eval '.server.exposedAddress // ""' "$CONFIG_YAML_HOST"
}
# The engine is a config.yaml-only setting — there is no env override for it
# (combined/cmd/root.go reads it from YAML and derives the env vars), so
# config.yaml is authoritative. Absent means the sqlite default.
detect_store_engine() {
local engine
engine=$(yq eval '.server.store.engine // ""' "$CONFIG_YAML_HOST")
if [[ -z "$engine" ]] || [[ "$engine" == "null" ]]; then
engine="sqlite"
fi
echo "$engine" | tr '[:upper:]' '[:lower:]'
}
detect_store_dsn() {
yq eval '.server.store.dsn // ""' "$CONFIG_YAML_HOST"
}
# config.yaml is where a combined deployment carries its DSN; this only covers
# hand-rolled installs that keep it in the environment instead.
detect_store_dsn_from_compose() {
# `compose config` re-escapes a literal $ as $$ on the way out, so undo that
# to get the value the container actually receives.
$DOCKER_COMPOSE_COMMAND config 2>/dev/null | yq eval "
.services[\"$COMBINED_SERVICE\"].environment.NB_STORE_ENGINE_POSTGRES_DSN //
.services[\"$COMBINED_SERVICE\"].environment.NETBIRD_STORE_ENGINE_POSTGRES_DSN // \"\"
" - 2>/dev/null | sed 's/\$\$/$/g'
}
# Reads either DSN form: "host=db ..." or "postgres://user:pass@db:5432/name".
dsn_host() {
local dsn="$1"
case "$dsn" in
*://*) printf '%s' "$dsn" | sed -n 's,^[a-zA-Z+]*://\([^/?]*\).*,\1,p' | sed -e 's,.*@,,' -e 's,:.*,,' ;;
*) printf '%s' "$dsn" | sed -n 's/.*[[:space:]]*host=\([^[:space:]]*\).*/\1/p' ;;
esac
}
# flow-enricher is its own container, so a loopback host or a socket path would
# reach the enricher rather than Postgres. Only flag hosts we can positively
# identify — an unparseable DSN must not leave the operator with no way forward.
dsn_host_reachable() {
local dsn="$1"
case "$(dsn_host "$dsn")" in
localhost | 127.* | ::1 | 0.0.0.0 | /*) return 1 ;;
*) return 0 ;;
esac
}
# Names the compose service running this deployment's Postgres, for depends_on.
# Empty means external — the DSN host matched no service. A DSN with no readable
# host falls back to matching on image.
detect_postgres_service() {
local host
host=$(dsn_host "$POSTGRES_DSN")
if [[ -n "$host" ]]; then
if [[ "$(host="$host" yq eval '.services | has(env(host))' "$COMPOSE_FILE" 2>/dev/null)" == "true" ]]; then
echo "$host"
fi
return
fi
yq eval '.services | to_entries | map(select(.value.image // "" | test("(^|/)(postgres|postgis|pgvector|timescaledb)(:|@|$)"))) | .[0].key // ""' "$COMPOSE_FILE"
}
# depends_on: service_healthy is only legal if the service defines a healthcheck.
detect_postgres_depends_condition() {
local tag
tag=$(yq eval ".services[\"$POSTGRES_SERVICE\"].healthcheck | tag" "$COMPOSE_FILE" 2>/dev/null)
if [[ "$tag" == "!!map" ]]; then
echo "service_healthy"
else
echo "service_started"
fi
}
env_value() {
local value="$1"
value=$(printf '%s' "$value" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\$/$$/g')
printf '"%s"' "$value"
}
detect_compose_network() {
local tag
tag=$(yq eval ".services[\"$COMBINED_SERVICE\"].networks | tag" "$COMPOSE_FILE" 2>/dev/null)
@@ -325,30 +228,16 @@ services:
NETBIRD_LICENSE_SERVER_BASE_URL: \${NETBIRD_LICENSE_SERVER_BASE_URL}
EOF
# An existing Postgres is already wired up by the operator's own compose file,
# so only a Postgres this run creates needs a depends_on.
if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then
cat <<EOF
depends_on:
${POSTGRES_SERVICE}:
condition: ${POSTGRES_DEPENDS_CONDITION}
EOF
fi
# The server is only pointed at a different config file when this run
# generates one. A pure image swap leaves it on its original config.yaml.
if [[ "$ENTERPRISE_CONFIG" == "yes" ]]; then
cat <<EOF
postgres:
condition: service_healthy
volumes:
- ./${ENTERPRISE_CONFIG_FILE}:/etc/netbird/config.yaml.enterprise:ro
command: ["--config", "/etc/netbird/config.yaml.enterprise"]
EOF
fi
if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then
cat <<EOF
${POSTGRES_SERVICE}:
postgres:
image: postgres:17
container_name: netbird-postgres
restart: unless-stopped
@@ -368,14 +257,6 @@ EOF
fi
if [[ "$ENABLE_FLOW" == "yes" ]]; then
# Nothing to wait on when Postgres is managed outside this compose project.
local enricher_depends=""
if [[ -n "$POSTGRES_SERVICE" ]]; then
enricher_depends="
${POSTGRES_SERVICE}:
condition: ${POSTGRES_DEPENDS_CONDITION}"
fi
cat <<EOF
nats:
@@ -392,7 +273,9 @@ EOF
container_name: netbird-flow-enricher
restart: unless-stopped
networks: [${COMPOSE_NETWORK}]
depends_on:${enricher_depends}
depends_on:
postgres:
condition: service_healthy
nats:
condition: service_started
environment:
@@ -400,10 +283,10 @@ EOF
NETBIRD_LICENSE_SERVER_BASE_URL: \${NETBIRD_LICENSE_SERVER_BASE_URL}
NB_DATADIR: /var/lib/netbird
NB_MANAGEMENT_STORE_ENGINE: postgres
NB_MANAGEMENT_POSTGRES_DSN: \${NB_ENTERPRISE_POSTGRES_DSN}
NB_STORE_ENGINE_POSTGRES_DSN: \${NB_ENTERPRISE_POSTGRES_DSN}
NB_MANAGEMENT_POSTGRES_DSN: "host=postgres user=netbird password=\${POSTGRES_PASSWORD} dbname=netbird port=5432 sslmode=disable"
NB_STORE_ENGINE_POSTGRES_DSN: "host=postgres user=netbird password=\${POSTGRES_PASSWORD} dbname=netbird port=5432 sslmode=disable"
NB_TRAFFIC_EVENT_STORE_ENGINE: postgres
NB_TRAFFIC_EVENT_POSTGRES_DSN: \${NB_ENTERPRISE_POSTGRES_DSN}
NB_TRAFFIC_EVENT_POSTGRES_DSN: "host=postgres user=netbird password=\${POSTGRES_PASSWORD} dbname=netbird port=5432 sslmode=disable"
NB_MANAGEMENT_STORE_KEY: \${NETBIRD_ENCRYPTION_KEY}
NB_FLOW_ADAPTER_TYPE: nats
NB_FLOW_NATS_ENDPOINTS: nats://nats:4222
@@ -460,41 +343,27 @@ EOF
fi
}
# Build config.yaml.enterprise from the operator's existing config.yaml. We
# don't touch the original file. Values go through strenv() so a DSN carrying
# quotes, backslashes or $ cannot break out of the expression.
# Build config.yaml.enterprise by yq-editing the operator's existing
# config.yaml. We don't touch the original file.
render_enterprise_config() {
{
echo "# Generated by migrate-to-enterprise.sh from ${CONFIG_YAML_HOST}."
echo "# The enterprise server is started with --config pointing at this file,"
echo "# so later edits to ${CONFIG_YAML_HOST} have no effect until copied here."
cat "$CONFIG_YAML_HOST"
} > "$ENTERPRISE_CONFIG_FILE"
local pg_dsn="host=postgres user=netbird password=${POSTGRES_PASSWORD} dbname=netbird port=5432 sslmode=disable"
if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then
# Fresh Postgres: point every store section at it. migrate-store carries the
# SQLite contents across.
POSTGRES_DSN="$POSTGRES_DSN" yq eval -i '
.server.store.engine = "postgres" |
.server.store.dsn = strenv(POSTGRES_DSN) |
.server.activityStore.engine = "postgres" |
.server.activityStore.dsn = strenv(POSTGRES_DSN) |
.server.authStore.engine = "postgres" |
.server.authStore.dsn = strenv(POSTGRES_DSN)
' "$ENTERPRISE_CONFIG_FILE"
fi
# Otherwise the store config is the operator's and stays untouched.
# activityStore and authStore do not inherit from server.store — each falls
# back to its own SQLite file under dataDir — so repointing them at Postgres
# here would silently strand the existing audit log and the embedded IdP's
# users, with no migrate-store run to carry them over.
yq eval "
.server.store.engine = \"postgres\" |
.server.store.dsn = \"$pg_dsn\" |
.server.activityStore.engine = \"postgres\" |
.server.activityStore.dsn = \"$pg_dsn\" |
.server.authStore.engine = \"postgres\" |
.server.authStore.dsn = \"$pg_dsn\"
" "$CONFIG_YAML_HOST" > "$ENTERPRISE_CONFIG_FILE"
if [[ "$ENABLE_FLOW" == "yes" ]]; then
NETBIRD_DOMAIN="$NETBIRD_DOMAIN" yq eval -i '
local flow_addr="${NETBIRD_DOMAIN}"
yq eval -i "
.server.trafficFlow.enabled = true |
.server.trafficFlow.address = strenv(NETBIRD_DOMAIN) |
.server.trafficFlow.interval = "60s"
' "$ENTERPRISE_CONFIG_FILE"
.server.trafficFlow.address = \"$flow_addr\" |
.server.trafficFlow.interval = \"60s\"
" "$ENTERPRISE_CONFIG_FILE"
fi
}
@@ -761,91 +630,6 @@ on_exit() {
# Main
# ---------------------------------------------------------------------------
# Already on Postgres: there is nothing to provision and nothing to migrate.
# The enterprise image reads the very same store config the community image
# did, so step 2 collapses to a no-op and the run is a plain image swap.
configure_existing_postgres() {
EXISTING_POSTGRES="yes"
MIGRATE_POSTGRES="no"
# DSN first — detect_postgres_service prefers the host it names.
POSTGRES_DSN=$(detect_store_dsn)
if [[ -z "$POSTGRES_DSN" ]] || [[ "$POSTGRES_DSN" == "null" ]]; then
POSTGRES_DSN=$(detect_store_dsn_from_compose)
fi
if [[ "$POSTGRES_DSN" == "null" ]]; then
POSTGRES_DSN=""
fi
POSTGRES_SERVICE=$(detect_postgres_service)
if [[ -n "$POSTGRES_SERVICE" ]]; then
POSTGRES_DEPENDS_CONDITION=$(detect_postgres_depends_condition)
fi
echo "Step 2: Postgres migration not needed — this deployment already runs on"
echo " Postgres. Its store configuration is reused as-is and left"
echo " untouched; no database is created and no data is moved."
if [[ -n "$POSTGRES_SERVICE" ]]; then
echo " Postgres service: $POSTGRES_SERVICE (in $COMPOSE_FILE)"
else
echo " Postgres service: managed outside $COMPOSE_FILE"
fi
}
configure_sqlite_store() {
MIGRATE_POSTGRES=$(read_yes_no "Step 2: Migrate storage from SQLite to Postgres? (recommended)" "n")
[[ "$MIGRATE_POSTGRES" == "yes" ]] || return 0
# The override would otherwise merge into a service of the same name and
# quietly rewrite its image and credentials.
local existing
existing=$(yq eval '.services | has("postgres")' "$COMPOSE_FILE")
if [[ "$existing" == "true" ]]; then
echo "" > /dev/stderr
echo "$COMPOSE_FILE already defines a service named 'postgres', but config.yaml" > /dev/stderr
echo "still has server.store.engine: sqlite. This script would add its own" > /dev/stderr
echo "'postgres' service and Compose would merge the two." > /dev/stderr
echo "" > /dev/stderr
echo "Point server.store.engine at that Postgres yourself, or rename the service," > /dev/stderr
echo "then re-run." > /dev/stderr
exit 1
fi
echo ""
echo " ⚠ Data will be migrated from SQLite to Postgres. The SQLite store"
echo " will be backed up automatically. To fully revert later, restore"
echo " that backup and delete docker-compose.override.yml +"
echo " config.yaml.enterprise."
local confirm
confirm=$(read_yes_no " Continue?" "y")
if [[ "$confirm" != "yes" ]]; then
MIGRATE_POSTGRES="no"
echo " Skipping Postgres migration."
return 0
fi
POSTGRES_PASSWORD=$(rand_password)
POSTGRES_SERVICE="postgres"
POSTGRES_DEPENDS_CONDITION="service_healthy"
POSTGRES_DSN="host=postgres user=netbird password=${POSTGRES_PASSWORD} dbname=netbird port=5432 sslmode=disable"
}
# mysql, or something this script has never seen. Swapping the images is still
# valid; touching the store is not.
configure_unsupported_store() {
MIGRATE_POSTGRES="no"
echo " ⚠ server.store.engine is '$STORE_ENGINE'. This script only migrates"
echo " SQLite to Postgres, and traffic flow requires Postgres, so both are"
echo " unavailable here. The store configuration will be left untouched."
echo ""
local proceed
proceed=$(read_yes_no "Step 2 skipped. Continue with the image swap only?" "n")
if [[ "$proceed" != "yes" ]]; then
echo "Aborted."
exit 0
fi
}
init_migration() {
DOCKER_COMPOSE_COMMAND=$(check_docker_compose)
check_yq
@@ -895,15 +679,12 @@ init_migration() {
exit 1
fi
STORE_ENGINE=$(detect_store_engine)
echo "Detected existing deployment:"
echo " Combined service: $COMBINED_SERVICE"
echo " Dashboard: $DASHBOARD_SERVICE"
echo " config.yaml: $CONFIG_YAML_HOST"
echo " Data volume: $DATA_VOLUME"
echo " Network: $COMPOSE_NETWORK"
echo " Store engine: $STORE_ENGINE"
echo ""
require_eula_acceptance
@@ -922,17 +703,28 @@ init_migration() {
echo "Step 1: Image swap (community → Enterprise). License key required."
NB_LICENSE_KEY=$(read_secret " License key")
# Step 2 — what this does depends on what the deployment already stores in.
# Step 2 — optional
echo ""
case "$STORE_ENGINE" in
postgres) configure_existing_postgres ;;
sqlite) configure_sqlite_store ;;
*) configure_unsupported_store ;;
esac
MIGRATE_POSTGRES=$(read_yes_no "Step 2: Migrate storage from SQLite to Postgres? (recommended)" "n")
if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then
echo ""
echo " ⚠ Data will be migrated from SQLite to Postgres. The SQLite store"
echo " will be backed up automatically. To fully revert later, restore"
echo " that backup and delete docker-compose.override.yml +"
echo " config.yaml.enterprise."
local confirm
confirm=$(read_yes_no " Continue?" "y")
if [[ "$confirm" != "yes" ]]; then
MIGRATE_POSTGRES="no"
echo " Skipping Postgres migration."
else
POSTGRES_PASSWORD=$(rand_password)
fi
fi
# Step 3 — optional, only if Postgres is on (flow requires Postgres)
echo ""
if [[ "$MIGRATE_POSTGRES" == "yes" ]] || [[ "$EXISTING_POSTGRES" == "yes" ]]; then
if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then
ENABLE_FLOW=$(read_yes_no "Step 3: Enable traffic flow? (requires Postgres)" "n")
if [[ "$ENABLE_FLOW" == "yes" ]]; then
# Auth secret MUST match server.authSecret from config.yaml
@@ -956,46 +748,12 @@ init_migration() {
echo "Could not read server.store.encryptionKey from $CONFIG_YAML_HOST." > /dev/stderr
exit 1
fi
# flow-enricher talks to Postgres directly, so this is the one place an
# existing deployment's DSN is actually needed — and the one place a host
# that only works from inside the server container shows up.
while :; do
local dsn_problem=""
if [[ -z "$POSTGRES_DSN" ]]; then
dsn_problem="No DSN could be read from $CONFIG_YAML_HOST or from the $COMBINED_SERVICE environment."
elif ! dsn_host_reachable "$POSTGRES_DSN"; then
dsn_problem="Its host '$(dsn_host "$POSTGRES_DSN")' only resolves inside the server container."
fi
[[ -n "$dsn_problem" ]] || break
echo ""
echo " The flow enricher reaches Postgres from a container of its own."
echo " $dsn_problem"
echo " Enter a DSN reachable from other containers, or press Ctrl-C to abort."
POSTGRES_DSN=$(read_required " Postgres DSN (host=… user=… password=… dbname=… port=5432 sslmode=disable)")
done
# Only where the operator owns Postgres: a DSN entered above may name a
# different host. The sqlite path creates its own service, nothing to find.
if [[ "$EXISTING_POSTGRES" == "yes" ]]; then
POSTGRES_SERVICE=$(detect_postgres_service)
if [[ -n "$POSTGRES_SERVICE" ]]; then
POSTGRES_DEPENDS_CONDITION=$(detect_postgres_depends_condition)
fi
fi
fi
else
ENABLE_FLOW="no"
echo "Step 3 (traffic flow) skipped — requires Postgres."
fi
# config.yaml.enterprise only exists to hold changes; without any there is
# nothing to generate and the server keeps running on its own config.yaml.
if [[ "$MIGRATE_POSTGRES" == "yes" ]] || [[ "$ENABLE_FLOW" == "yes" ]]; then
ENTERPRISE_CONFIG="yes"
fi
check_data_directory
check_stale_postgres_volume
}
@@ -1013,7 +771,7 @@ apply_changes() {
sed -i.bak '/NETBIRD_LICENSE_SERVER_BASE_URL/d' "$OVERRIDE_FILE" && rm -f "$OVERRIDE_FILE.bak"
fi
if [[ "$ENTERPRISE_CONFIG" == "yes" ]]; then
if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then
echo "Writing $ENTERPRISE_CONFIG_FILE ..."
install -m 600 /dev/null "$ENTERPRISE_CONFIG_FILE"
render_enterprise_config
@@ -1049,9 +807,6 @@ apply_changes() {
echo "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}"
fi
if [[ "$ENABLE_FLOW" == "yes" ]]; then
# Own variable name rather than NB_STORE_ENGINE_POSTGRES_DSN so that a
# deployment already setting that one keeps its own value.
echo "NB_ENTERPRISE_POSTGRES_DSN=$(env_value "$POSTGRES_DSN")"
echo "NB_FLOW_AUTH_SECRET=${NB_FLOW_AUTH_SECRET}"
echo "NETBIRD_ENCRYPTION_KEY=${NETBIRD_ENCRYPTION_KEY}"
fi
@@ -1113,19 +868,14 @@ print_summary() {
echo " Summary"
echo "──────────────────────────────────────────────────────────────────────"
echo " Images: swapped to enterprise"
if [[ "$MIGRATE_POSTGRES" == "yes" ]]; then
echo " Storage: Postgres (data migrated from SQLite)"
elif [[ "$EXISTING_POSTGRES" == "yes" ]]; then
echo " Storage: Postgres (pre-existing, configuration unchanged)"
else
echo " Storage: $STORE_ENGINE (unchanged)"
fi
[[ "$MIGRATE_POSTGRES" == "yes" ]] && echo " Storage: Postgres (data migrated from SQLite)"
[[ "$MIGRATE_POSTGRES" != "yes" ]] && echo " Storage: SQLite (unchanged)"
[[ "$ENABLE_FLOW" == "yes" ]] && echo " Traffic flow: enabled"
[[ "$ENABLE_FLOW" != "yes" ]] && echo " Traffic flow: disabled"
echo ""
echo " Generated files (next to your docker-compose.yml):"
echo " $OVERRIDE_FILE"
[[ "$ENTERPRISE_CONFIG" == "yes" ]] && echo " $ENTERPRISE_CONFIG_FILE"
[[ "$MIGRATE_POSTGRES" == "yes" ]] && echo " $ENTERPRISE_CONFIG_FILE"
echo " .env (license key + secrets, mode 600)"
[[ "$ENV_EXISTED" == "yes" ]] && [[ -f "$ENV_BACKUP" ]] && echo " $ENV_BACKUP (.env as it was before this run)"
[[ "$MIGRATE_POSTGRES" == "yes" ]] && echo " backups/sqlite-pre-enterprise-*/ (SQLite backup)"
@@ -1149,11 +899,7 @@ print_summary() {
else
echo " $DOCKER_COMPOSE_COMMAND down"
fi
if [[ "$ENTERPRISE_CONFIG" == "yes" ]]; then
echo " rm -f $OVERRIDE_FILE $ENTERPRISE_CONFIG_FILE"
else
echo " rm -f $OVERRIDE_FILE"
fi
echo " rm -f $OVERRIDE_FILE $ENTERPRISE_CONFIG_FILE"
if [[ "$ENV_EXISTED" == "yes" ]] && [[ -f "$ENV_BACKUP" ]]; then
echo " mv $ENV_BACKUP .env # restores .env as it was before this run"
elif [[ "$ENV_EXISTED" == "no" ]]; then

View File

@@ -1,6 +1,6 @@
package network_map
//go:generate go run go.uber.org/mock/mockgen -package network_map -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod
//go:generate go tool mockgen -package network_map -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod
import (
"context"

View File

@@ -10,7 +10,7 @@ import (
"strings"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -4,7 +4,7 @@ import (
"context"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -6,7 +6,7 @@ import (
"strings"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -5,7 +5,7 @@ import (
"encoding/json"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -7,7 +7,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

View File

@@ -1,6 +1,6 @@
package peers
//go:generate go run github.com/golang/mock/mockgen -package peers -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
//go:generate go tool mockgen -package peers -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
import (
"context"

View File

@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./manager.go
//
// Generated by this command:
//
// mockgen -package peers -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
//
// Package peers is a generated GoMock package.
package peers
@@ -9,18 +14,19 @@ import (
net "net"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
network_map "github.com/netbirdio/netbird/management/internals/controllers/network_map"
account "github.com/netbirdio/netbird/management/server/account"
integrated_validator "github.com/netbirdio/netbird/management/server/integrations/integrated_validator"
peer "github.com/netbirdio/netbird/management/server/peer"
types "github.com/netbirdio/netbird/management/server/types"
gomock "go.uber.org/mock/gomock"
)
// MockManager is a mock of Manager interface.
type MockManager struct {
ctrl *gomock.Controller
recorder *MockManagerMockRecorder
isgomock struct{}
}
// MockManagerMockRecorder is the mock recorder for MockManager.
@@ -49,7 +55,7 @@ func (m *MockManager) CreateProxyPeer(ctx context.Context, accountID, peerKey, c
}
// CreateProxyPeer indicates an expected call of CreateProxyPeer.
func (mr *MockManagerMockRecorder) CreateProxyPeer(ctx, accountID, peerKey, cluster interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) CreateProxyPeer(ctx, accountID, peerKey, cluster any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateProxyPeer", reflect.TypeOf((*MockManager)(nil).CreateProxyPeer), ctx, accountID, peerKey, cluster)
}
@@ -63,7 +69,7 @@ func (m *MockManager) DeletePeers(ctx context.Context, accountID string, peerIDs
}
// DeletePeers indicates an expected call of DeletePeers.
func (mr *MockManagerMockRecorder) DeletePeers(ctx, accountID, peerIDs, userID, checkConnected interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) DeletePeers(ctx, accountID, peerIDs, userID, checkConnected any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePeers", reflect.TypeOf((*MockManager)(nil).DeletePeers), ctx, accountID, peerIDs, userID, checkConnected)
}
@@ -78,7 +84,7 @@ func (m *MockManager) GetAllPeers(ctx context.Context, accountID, userID string)
}
// GetAllPeers indicates an expected call of GetAllPeers.
func (mr *MockManagerMockRecorder) GetAllPeers(ctx, accountID, userID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetAllPeers(ctx, accountID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPeers", reflect.TypeOf((*MockManager)(nil).GetAllPeers), ctx, accountID, userID)
}
@@ -93,7 +99,7 @@ func (m *MockManager) GetPeer(ctx context.Context, accountID, userID, peerID str
}
// GetPeer indicates an expected call of GetPeer.
func (mr *MockManagerMockRecorder) GetPeer(ctx, accountID, userID, peerID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetPeer(ctx, accountID, userID, peerID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeer", reflect.TypeOf((*MockManager)(nil).GetPeer), ctx, accountID, userID, peerID)
}
@@ -108,7 +114,7 @@ func (m *MockManager) GetPeerAccountID(ctx context.Context, peerID string) (stri
}
// GetPeerAccountID indicates an expected call of GetPeerAccountID.
func (mr *MockManagerMockRecorder) GetPeerAccountID(ctx, peerID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetPeerAccountID(ctx, peerID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerAccountID", reflect.TypeOf((*MockManager)(nil).GetPeerAccountID), ctx, peerID)
}
@@ -123,7 +129,7 @@ func (m *MockManager) GetPeerByTunnelIP(ctx context.Context, accountID string, i
}
// GetPeerByTunnelIP indicates an expected call of GetPeerByTunnelIP.
func (mr *MockManagerMockRecorder) GetPeerByTunnelIP(ctx, accountID, ip interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetPeerByTunnelIP(ctx, accountID, ip any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerByTunnelIP", reflect.TypeOf((*MockManager)(nil).GetPeerByTunnelIP), ctx, accountID, ip)
}
@@ -138,7 +144,7 @@ func (m *MockManager) GetPeerID(ctx context.Context, peerKey string) (string, er
}
// GetPeerID indicates an expected call of GetPeerID.
func (mr *MockManagerMockRecorder) GetPeerID(ctx, peerKey interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetPeerID(ctx, peerKey any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerID", reflect.TypeOf((*MockManager)(nil).GetPeerID), ctx, peerKey)
}
@@ -154,7 +160,7 @@ func (m *MockManager) GetPeerWithGroups(ctx context.Context, accountID, peerID s
}
// GetPeerWithGroups indicates an expected call of GetPeerWithGroups.
func (mr *MockManagerMockRecorder) GetPeerWithGroups(ctx, accountID, peerID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetPeerWithGroups(ctx, accountID, peerID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerWithGroups", reflect.TypeOf((*MockManager)(nil).GetPeerWithGroups), ctx, accountID, peerID)
}
@@ -169,7 +175,7 @@ func (m *MockManager) GetPeersByGroupIDs(ctx context.Context, accountID string,
}
// GetPeersByGroupIDs indicates an expected call of GetPeersByGroupIDs.
func (mr *MockManagerMockRecorder) GetPeersByGroupIDs(ctx, accountID, groupsIDs interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetPeersByGroupIDs(ctx, accountID, groupsIDs any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeersByGroupIDs", reflect.TypeOf((*MockManager)(nil).GetPeersByGroupIDs), ctx, accountID, groupsIDs)
}
@@ -181,7 +187,7 @@ func (m *MockManager) SetAccountManager(accountManager account.Manager) {
}
// SetAccountManager indicates an expected call of SetAccountManager.
func (mr *MockManagerMockRecorder) SetAccountManager(accountManager interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) SetAccountManager(accountManager any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccountManager", reflect.TypeOf((*MockManager)(nil).SetAccountManager), accountManager)
}
@@ -193,7 +199,7 @@ func (m *MockManager) SetIntegratedPeerValidator(integratedPeerValidator integra
}
// SetIntegratedPeerValidator indicates an expected call of SetIntegratedPeerValidator.
func (mr *MockManagerMockRecorder) SetIntegratedPeerValidator(integratedPeerValidator interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) SetIntegratedPeerValidator(integratedPeerValidator any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetIntegratedPeerValidator", reflect.TypeOf((*MockManager)(nil).SetIntegratedPeerValidator), integratedPeerValidator)
}
@@ -205,7 +211,7 @@ func (m *MockManager) SetNetworkMapController(networkMapController network_map.C
}
// SetNetworkMapController indicates an expected call of SetNetworkMapController.
func (mr *MockManagerMockRecorder) SetNetworkMapController(networkMapController interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) SetNetworkMapController(networkMapController any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNetworkMapController", reflect.TypeOf((*MockManager)(nil).SetNetworkMapController), networkMapController)
}

View File

@@ -5,7 +5,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -1,6 +1,6 @@
package proxy
//go:generate go run github.com/golang/mock/mockgen -package proxy -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
//go:generate go tool mockgen -package proxy -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
import (
"context"

View File

@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./manager.go
//
// Generated by this command:
//
// mockgen -package proxy -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
//
// Package proxy is a generated GoMock package.
package proxy
@@ -9,14 +14,15 @@ import (
reflect "reflect"
time "time"
gomock "github.com/golang/mock/gomock"
proto "github.com/netbirdio/netbird/shared/management/proto"
gomock "go.uber.org/mock/gomock"
)
// MockManager is a mock of Manager interface.
type MockManager struct {
ctrl *gomock.Controller
recorder *MockManagerMockRecorder
isgomock struct{}
}
// MockManagerMockRecorder is the mock recorder for MockManager.
@@ -45,25 +51,11 @@ func (m *MockManager) CleanupStale(ctx context.Context, inactivityDuration time.
}
// CleanupStale indicates an expected call of CleanupStale.
func (mr *MockManagerMockRecorder) CleanupStale(ctx, inactivityDuration interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) CleanupStale(ctx, inactivityDuration any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanupStale", reflect.TypeOf((*MockManager)(nil).CleanupStale), ctx, inactivityDuration)
}
// ClusterSupportsCustomPorts mocks base method.
func (m *MockManager) ClusterSupportsCustomPorts(ctx context.Context, clusterAddr string) *bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ClusterSupportsCustomPorts", ctx, clusterAddr)
ret0, _ := ret[0].(*bool)
return ret0
}
// ClusterSupportsCustomPorts indicates an expected call of ClusterSupportsCustomPorts.
func (mr *MockManagerMockRecorder) ClusterSupportsCustomPorts(ctx, clusterAddr interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterSupportsCustomPorts", reflect.TypeOf((*MockManager)(nil).ClusterSupportsCustomPorts), ctx, clusterAddr)
}
// ClusterRequireSubdomain mocks base method.
func (m *MockManager) ClusterRequireSubdomain(ctx context.Context, clusterAddr string) *bool {
m.ctrl.T.Helper()
@@ -73,7 +65,7 @@ func (m *MockManager) ClusterRequireSubdomain(ctx context.Context, clusterAddr s
}
// ClusterRequireSubdomain indicates an expected call of ClusterRequireSubdomain.
func (mr *MockManagerMockRecorder) ClusterRequireSubdomain(ctx, clusterAddr interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) ClusterRequireSubdomain(ctx, clusterAddr any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterRequireSubdomain", reflect.TypeOf((*MockManager)(nil).ClusterRequireSubdomain), ctx, clusterAddr)
}
@@ -87,11 +79,25 @@ func (m *MockManager) ClusterSupportsCrowdSec(ctx context.Context, clusterAddr s
}
// ClusterSupportsCrowdSec indicates an expected call of ClusterSupportsCrowdSec.
func (mr *MockManagerMockRecorder) ClusterSupportsCrowdSec(ctx, clusterAddr interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) ClusterSupportsCrowdSec(ctx, clusterAddr any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterSupportsCrowdSec", reflect.TypeOf((*MockManager)(nil).ClusterSupportsCrowdSec), ctx, clusterAddr)
}
// ClusterSupportsCustomPorts mocks base method.
func (m *MockManager) ClusterSupportsCustomPorts(ctx context.Context, clusterAddr string) *bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ClusterSupportsCustomPorts", ctx, clusterAddr)
ret0, _ := ret[0].(*bool)
return ret0
}
// ClusterSupportsCustomPorts indicates an expected call of ClusterSupportsCustomPorts.
func (mr *MockManagerMockRecorder) ClusterSupportsCustomPorts(ctx, clusterAddr any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterSupportsCustomPorts", reflect.TypeOf((*MockManager)(nil).ClusterSupportsCustomPorts), ctx, clusterAddr)
}
// ClusterSupportsPrivate mocks base method.
func (m *MockManager) ClusterSupportsPrivate(ctx context.Context, clusterAddr string) *bool {
m.ctrl.T.Helper()
@@ -101,7 +107,7 @@ func (m *MockManager) ClusterSupportsPrivate(ctx context.Context, clusterAddr st
}
// ClusterSupportsPrivate indicates an expected call of ClusterSupportsPrivate.
func (mr *MockManagerMockRecorder) ClusterSupportsPrivate(ctx, clusterAddr interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) ClusterSupportsPrivate(ctx, clusterAddr any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterSupportsPrivate", reflect.TypeOf((*MockManager)(nil).ClusterSupportsPrivate), ctx, clusterAddr)
}
@@ -116,11 +122,40 @@ func (m *MockManager) Connect(ctx context.Context, proxyID, sessionID, clusterAd
}
// Connect indicates an expected call of Connect.
func (mr *MockManagerMockRecorder) Connect(ctx, proxyID, sessionID, clusterAddress, ipAddress, accountID, capabilities interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) Connect(ctx, proxyID, sessionID, clusterAddress, ipAddress, accountID, capabilities any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Connect", reflect.TypeOf((*MockManager)(nil).Connect), ctx, proxyID, sessionID, clusterAddress, ipAddress, accountID, capabilities)
}
// CountAccountProxies mocks base method.
func (m *MockManager) CountAccountProxies(ctx context.Context, accountID string) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CountAccountProxies", ctx, accountID)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CountAccountProxies indicates an expected call of CountAccountProxies.
func (mr *MockManagerMockRecorder) CountAccountProxies(ctx, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountAccountProxies", reflect.TypeOf((*MockManager)(nil).CountAccountProxies), ctx, accountID)
}
// DeleteAccountCluster mocks base method.
func (m *MockManager) DeleteAccountCluster(ctx context.Context, clusterAddress, accountID string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteAccountCluster", ctx, clusterAddress, accountID)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteAccountCluster indicates an expected call of DeleteAccountCluster.
func (mr *MockManagerMockRecorder) DeleteAccountCluster(ctx, clusterAddress, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccountCluster", reflect.TypeOf((*MockManager)(nil).DeleteAccountCluster), ctx, clusterAddress, accountID)
}
// Disconnect mocks base method.
func (m *MockManager) Disconnect(ctx context.Context, proxyID, sessionID string) error {
m.ctrl.T.Helper()
@@ -130,11 +165,26 @@ func (m *MockManager) Disconnect(ctx context.Context, proxyID, sessionID string)
}
// Disconnect indicates an expected call of Disconnect.
func (mr *MockManagerMockRecorder) Disconnect(ctx, proxyID, sessionID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) Disconnect(ctx, proxyID, sessionID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Disconnect", reflect.TypeOf((*MockManager)(nil).Disconnect), ctx, proxyID, sessionID)
}
// GetAccountProxy mocks base method.
func (m *MockManager) GetAccountProxy(ctx context.Context, accountID string) (*Proxy, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAccountProxy", ctx, accountID)
ret0, _ := ret[0].(*Proxy)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetAccountProxy indicates an expected call of GetAccountProxy.
func (mr *MockManagerMockRecorder) GetAccountProxy(ctx, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountProxy", reflect.TypeOf((*MockManager)(nil).GetAccountProxy), ctx, accountID)
}
// GetActiveClusterAddresses mocks base method.
func (m *MockManager) GetActiveClusterAddresses(ctx context.Context) ([]string, error) {
m.ctrl.T.Helper()
@@ -145,11 +195,12 @@ func (m *MockManager) GetActiveClusterAddresses(ctx context.Context) ([]string,
}
// GetActiveClusterAddresses indicates an expected call of GetActiveClusterAddresses.
func (mr *MockManagerMockRecorder) GetActiveClusterAddresses(ctx interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetActiveClusterAddresses(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterAddresses", reflect.TypeOf((*MockManager)(nil).GetActiveClusterAddresses), ctx)
}
// GetActiveClusterAddressesForAccount mocks base method.
func (m *MockManager) GetActiveClusterAddressesForAccount(ctx context.Context, accountID string) ([]string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetActiveClusterAddressesForAccount", ctx, accountID)
@@ -158,7 +209,8 @@ func (m *MockManager) GetActiveClusterAddressesForAccount(ctx context.Context, a
return ret0, ret1
}
func (mr *MockManagerMockRecorder) GetActiveClusterAddressesForAccount(ctx, accountID interface{}) *gomock.Call {
// GetActiveClusterAddressesForAccount indicates an expected call of GetActiveClusterAddressesForAccount.
func (mr *MockManagerMockRecorder) GetActiveClusterAddressesForAccount(ctx, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveClusterAddressesForAccount", reflect.TypeOf((*MockManager)(nil).GetActiveClusterAddressesForAccount), ctx, accountID)
}
@@ -172,41 +224,11 @@ func (m *MockManager) Heartbeat(ctx context.Context, p *Proxy) error {
}
// Heartbeat indicates an expected call of Heartbeat.
func (mr *MockManagerMockRecorder) Heartbeat(ctx, p interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) Heartbeat(ctx, p any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Heartbeat", reflect.TypeOf((*MockManager)(nil).Heartbeat), ctx, p)
}
// GetAccountProxy mocks base method.
func (m *MockManager) GetAccountProxy(ctx context.Context, accountID string) (*Proxy, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAccountProxy", ctx, accountID)
ret0, _ := ret[0].(*Proxy)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetAccountProxy indicates an expected call of GetAccountProxy.
func (mr *MockManagerMockRecorder) GetAccountProxy(ctx, accountID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountProxy", reflect.TypeOf((*MockManager)(nil).GetAccountProxy), ctx, accountID)
}
// CountAccountProxies mocks base method.
func (m *MockManager) CountAccountProxies(ctx context.Context, accountID string) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CountAccountProxies", ctx, accountID)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CountAccountProxies indicates an expected call of CountAccountProxies.
func (mr *MockManagerMockRecorder) CountAccountProxies(ctx, accountID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountAccountProxies", reflect.TypeOf((*MockManager)(nil).CountAccountProxies), ctx, accountID)
}
// IsClusterAddressAvailable mocks base method.
func (m *MockManager) IsClusterAddressAvailable(ctx context.Context, clusterAddress, accountID string) (bool, error) {
m.ctrl.T.Helper()
@@ -217,29 +239,16 @@ func (m *MockManager) IsClusterAddressAvailable(ctx context.Context, clusterAddr
}
// IsClusterAddressAvailable indicates an expected call of IsClusterAddressAvailable.
func (mr *MockManagerMockRecorder) IsClusterAddressAvailable(ctx, clusterAddress, accountID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) IsClusterAddressAvailable(ctx, clusterAddress, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsClusterAddressAvailable", reflect.TypeOf((*MockManager)(nil).IsClusterAddressAvailable), ctx, clusterAddress, accountID)
}
// DeleteAccountCluster mocks base method.
func (m *MockManager) DeleteAccountCluster(ctx context.Context, clusterAddress, accountID string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteAccountCluster", ctx, clusterAddress, accountID)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteAccountCluster indicates an expected call of DeleteAccountCluster.
func (mr *MockManagerMockRecorder) DeleteAccountCluster(ctx, clusterAddress, accountID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccountCluster", reflect.TypeOf((*MockManager)(nil).DeleteAccountCluster), ctx, clusterAddress, accountID)
}
// MockController is a mock of Controller interface.
type MockController struct {
ctrl *gomock.Controller
recorder *MockControllerMockRecorder
isgomock struct{}
}
// MockControllerMockRecorder is the mock recorder for MockController.
@@ -282,7 +291,7 @@ func (m *MockController) GetProxiesForCluster(clusterAddr string) []string {
}
// GetProxiesForCluster indicates an expected call of GetProxiesForCluster.
func (mr *MockControllerMockRecorder) GetProxiesForCluster(clusterAddr interface{}) *gomock.Call {
func (mr *MockControllerMockRecorder) GetProxiesForCluster(clusterAddr any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetProxiesForCluster", reflect.TypeOf((*MockController)(nil).GetProxiesForCluster), clusterAddr)
}
@@ -296,7 +305,7 @@ func (m *MockController) RegisterProxyToCluster(ctx context.Context, clusterAddr
}
// RegisterProxyToCluster indicates an expected call of RegisterProxyToCluster.
func (mr *MockControllerMockRecorder) RegisterProxyToCluster(ctx, clusterAddr, proxyID interface{}) *gomock.Call {
func (mr *MockControllerMockRecorder) RegisterProxyToCluster(ctx, clusterAddr, proxyID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterProxyToCluster", reflect.TypeOf((*MockController)(nil).RegisterProxyToCluster), ctx, clusterAddr, proxyID)
}
@@ -308,7 +317,7 @@ func (m *MockController) SendServiceUpdateToCluster(ctx context.Context, account
}
// SendServiceUpdateToCluster indicates an expected call of SendServiceUpdateToCluster.
func (mr *MockControllerMockRecorder) SendServiceUpdateToCluster(ctx, accountID, update, clusterAddr interface{}) *gomock.Call {
func (mr *MockControllerMockRecorder) SendServiceUpdateToCluster(ctx, accountID, update, clusterAddr any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendServiceUpdateToCluster", reflect.TypeOf((*MockController)(nil).SendServiceUpdateToCluster), ctx, accountID, update, clusterAddr)
}
@@ -322,7 +331,7 @@ func (m *MockController) UnregisterProxyFromCluster(ctx context.Context, cluster
}
// UnregisterProxyFromCluster indicates an expected call of UnregisterProxyFromCluster.
func (mr *MockControllerMockRecorder) UnregisterProxyFromCluster(ctx, clusterAddr, proxyID interface{}) *gomock.Call {
func (mr *MockControllerMockRecorder) UnregisterProxyFromCluster(ctx, clusterAddr, proxyID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnregisterProxyFromCluster", reflect.TypeOf((*MockController)(nil).UnregisterProxyFromCluster), ctx, clusterAddr, proxyID)
}

View File

@@ -9,7 +9,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -1,6 +1,6 @@
package service
//go:generate go run github.com/golang/mock/mockgen -package service -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod
//go:generate go tool mockgen -package service -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod
import (
"context"

View File

@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./interface.go
//
// Generated by this command:
//
// mockgen -package service -destination=interface_mock.go -source=./interface.go -build_flags=-mod=mod
//
// Package service is a generated GoMock package.
package service
@@ -8,14 +13,15 @@ import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
proxy "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/proxy"
gomock "go.uber.org/mock/gomock"
)
// MockManager is a mock of Manager interface.
type MockManager struct {
ctrl *gomock.Controller
recorder *MockManagerMockRecorder
isgomock struct{}
}
// MockManagerMockRecorder is the mock recorder for MockManager.
@@ -45,7 +51,7 @@ func (m *MockManager) CreateService(ctx context.Context, accountID, userID strin
}
// CreateService indicates an expected call of CreateService.
func (mr *MockManagerMockRecorder) CreateService(ctx, accountID, userID, service interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) CreateService(ctx, accountID, userID, service any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateService", reflect.TypeOf((*MockManager)(nil).CreateService), ctx, accountID, userID, service)
}
@@ -60,7 +66,7 @@ func (m *MockManager) CreateServiceFromPeer(ctx context.Context, accountID, peer
}
// CreateServiceFromPeer indicates an expected call of CreateServiceFromPeer.
func (mr *MockManagerMockRecorder) CreateServiceFromPeer(ctx, accountID, peerID, req interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) CreateServiceFromPeer(ctx, accountID, peerID, req any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateServiceFromPeer", reflect.TypeOf((*MockManager)(nil).CreateServiceFromPeer), ctx, accountID, peerID, req)
}
@@ -74,7 +80,7 @@ func (m *MockManager) DeleteAccountCluster(ctx context.Context, accountID, userI
}
// DeleteAccountCluster indicates an expected call of DeleteAccountCluster.
func (mr *MockManagerMockRecorder) DeleteAccountCluster(ctx, accountID, userID, clusterAddress interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) DeleteAccountCluster(ctx, accountID, userID, clusterAddress any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccountCluster", reflect.TypeOf((*MockManager)(nil).DeleteAccountCluster), ctx, accountID, userID, clusterAddress)
}
@@ -88,7 +94,7 @@ func (m *MockManager) DeleteAllServices(ctx context.Context, accountID, userID s
}
// DeleteAllServices indicates an expected call of DeleteAllServices.
func (mr *MockManagerMockRecorder) DeleteAllServices(ctx, accountID, userID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) DeleteAllServices(ctx, accountID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllServices", reflect.TypeOf((*MockManager)(nil).DeleteAllServices), ctx, accountID, userID)
}
@@ -102,7 +108,7 @@ func (m *MockManager) DeleteService(ctx context.Context, accountID, userID, serv
}
// DeleteService indicates an expected call of DeleteService.
func (mr *MockManagerMockRecorder) DeleteService(ctx, accountID, userID, serviceID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) DeleteService(ctx, accountID, userID, serviceID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteService", reflect.TypeOf((*MockManager)(nil).DeleteService), ctx, accountID, userID, serviceID)
}
@@ -117,7 +123,7 @@ func (m *MockManager) GetAccountServices(ctx context.Context, accountID string)
}
// GetAccountServices indicates an expected call of GetAccountServices.
func (mr *MockManagerMockRecorder) GetAccountServices(ctx, accountID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetAccountServices(ctx, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountServices", reflect.TypeOf((*MockManager)(nil).GetAccountServices), ctx, accountID)
}
@@ -132,7 +138,7 @@ func (m *MockManager) GetAllServices(ctx context.Context, accountID, userID stri
}
// GetAllServices indicates an expected call of GetAllServices.
func (mr *MockManagerMockRecorder) GetAllServices(ctx, accountID, userID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetAllServices(ctx, accountID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllServices", reflect.TypeOf((*MockManager)(nil).GetAllServices), ctx, accountID, userID)
}
@@ -147,7 +153,7 @@ func (m *MockManager) GetClusters(ctx context.Context, accountID, userID string)
}
// GetClusters indicates an expected call of GetClusters.
func (mr *MockManagerMockRecorder) GetClusters(ctx, accountID, userID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetClusters(ctx, accountID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusters", reflect.TypeOf((*MockManager)(nil).GetClusters), ctx, accountID, userID)
}
@@ -162,7 +168,7 @@ func (m *MockManager) GetGlobalServices(ctx context.Context) ([]*Service, error)
}
// GetGlobalServices indicates an expected call of GetGlobalServices.
func (mr *MockManagerMockRecorder) GetGlobalServices(ctx interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetGlobalServices(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetGlobalServices", reflect.TypeOf((*MockManager)(nil).GetGlobalServices), ctx)
}
@@ -177,7 +183,7 @@ func (m *MockManager) GetService(ctx context.Context, accountID, userID, service
}
// GetService indicates an expected call of GetService.
func (mr *MockManagerMockRecorder) GetService(ctx, accountID, userID, serviceID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetService(ctx, accountID, userID, serviceID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetService", reflect.TypeOf((*MockManager)(nil).GetService), ctx, accountID, userID, serviceID)
}
@@ -192,7 +198,7 @@ func (m *MockManager) GetServiceByDomain(ctx context.Context, domain string) (*S
}
// GetServiceByDomain indicates an expected call of GetServiceByDomain.
func (mr *MockManagerMockRecorder) GetServiceByDomain(ctx, domain interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetServiceByDomain(ctx, domain any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceByDomain", reflect.TypeOf((*MockManager)(nil).GetServiceByDomain), ctx, domain)
}
@@ -207,7 +213,7 @@ func (m *MockManager) GetServiceByID(ctx context.Context, accountID, serviceID s
}
// GetServiceByID indicates an expected call of GetServiceByID.
func (mr *MockManagerMockRecorder) GetServiceByID(ctx, accountID, serviceID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetServiceByID(ctx, accountID, serviceID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceByID", reflect.TypeOf((*MockManager)(nil).GetServiceByID), ctx, accountID, serviceID)
}
@@ -222,7 +228,7 @@ func (m *MockManager) GetServiceIDByTargetID(ctx context.Context, accountID, res
}
// GetServiceIDByTargetID indicates an expected call of GetServiceIDByTargetID.
func (mr *MockManagerMockRecorder) GetServiceIDByTargetID(ctx, accountID, resourceID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetServiceIDByTargetID(ctx, accountID, resourceID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceIDByTargetID", reflect.TypeOf((*MockManager)(nil).GetServiceIDByTargetID), ctx, accountID, resourceID)
}
@@ -236,7 +242,7 @@ func (m *MockManager) ReloadAllServicesForAccount(ctx context.Context, accountID
}
// ReloadAllServicesForAccount indicates an expected call of ReloadAllServicesForAccount.
func (mr *MockManagerMockRecorder) ReloadAllServicesForAccount(ctx, accountID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) ReloadAllServicesForAccount(ctx, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReloadAllServicesForAccount", reflect.TypeOf((*MockManager)(nil).ReloadAllServicesForAccount), ctx, accountID)
}
@@ -250,7 +256,7 @@ func (m *MockManager) ReloadService(ctx context.Context, accountID, serviceID st
}
// ReloadService indicates an expected call of ReloadService.
func (mr *MockManagerMockRecorder) ReloadService(ctx, accountID, serviceID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) ReloadService(ctx, accountID, serviceID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReloadService", reflect.TypeOf((*MockManager)(nil).ReloadService), ctx, accountID, serviceID)
}
@@ -264,7 +270,7 @@ func (m *MockManager) RenewServiceFromPeer(ctx context.Context, accountID, peerI
}
// RenewServiceFromPeer indicates an expected call of RenewServiceFromPeer.
func (mr *MockManagerMockRecorder) RenewServiceFromPeer(ctx, accountID, peerID, serviceID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) RenewServiceFromPeer(ctx, accountID, peerID, serviceID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RenewServiceFromPeer", reflect.TypeOf((*MockManager)(nil).RenewServiceFromPeer), ctx, accountID, peerID, serviceID)
}
@@ -278,7 +284,7 @@ func (m *MockManager) SetCertificateIssuedAt(ctx context.Context, accountID, ser
}
// SetCertificateIssuedAt indicates an expected call of SetCertificateIssuedAt.
func (mr *MockManagerMockRecorder) SetCertificateIssuedAt(ctx, accountID, serviceID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) SetCertificateIssuedAt(ctx, accountID, serviceID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetCertificateIssuedAt", reflect.TypeOf((*MockManager)(nil).SetCertificateIssuedAt), ctx, accountID, serviceID)
}
@@ -292,7 +298,7 @@ func (m *MockManager) SetStatus(ctx context.Context, accountID, serviceID string
}
// SetStatus indicates an expected call of SetStatus.
func (mr *MockManagerMockRecorder) SetStatus(ctx, accountID, serviceID, status interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) SetStatus(ctx, accountID, serviceID, status any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStatus", reflect.TypeOf((*MockManager)(nil).SetStatus), ctx, accountID, serviceID, status)
}
@@ -304,7 +310,7 @@ func (m *MockManager) StartExposeReaper(ctx context.Context) {
}
// StartExposeReaper indicates an expected call of StartExposeReaper.
func (mr *MockManagerMockRecorder) StartExposeReaper(ctx interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) StartExposeReaper(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartExposeReaper", reflect.TypeOf((*MockManager)(nil).StartExposeReaper), ctx)
}
@@ -318,7 +324,7 @@ func (m *MockManager) StopServiceFromPeer(ctx context.Context, accountID, peerID
}
// StopServiceFromPeer indicates an expected call of StopServiceFromPeer.
func (mr *MockManagerMockRecorder) StopServiceFromPeer(ctx, accountID, peerID, serviceID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) StopServiceFromPeer(ctx, accountID, peerID, serviceID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopServiceFromPeer", reflect.TypeOf((*MockManager)(nil).StopServiceFromPeer), ctx, accountID, peerID, serviceID)
}
@@ -333,7 +339,7 @@ func (m *MockManager) UpdateService(ctx context.Context, accountID, userID strin
}
// UpdateService indicates an expected call of UpdateService.
func (mr *MockManagerMockRecorder) UpdateService(ctx, accountID, userID, service interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) UpdateService(ctx, accountID, userID, service any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateService", reflect.TypeOf((*MockManager)(nil).UpdateService), ctx, accountID, userID, service)
}

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -8,7 +8,7 @@ import (
"time"
cachestore "github.com/eko/gocache/lib/v4/store"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/metric/noop"

View File

@@ -5,7 +5,7 @@ import (
"fmt"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -4,7 +4,7 @@ import (
"context"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -5,7 +5,7 @@ import (
"errors"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/require"
nbconfig "github.com/netbirdio/netbird/management/internals/server/config"

View File

@@ -1311,7 +1311,7 @@ func (s *ProxyServiceServer) authenticateHeader(ctx context.Context, serviceID s
lastErr = err
continue
}
return true, "header-user", proxyauth.MethodHeader
return true, proxyauth.HeaderUserID, proxyauth.MethodHeader
}
if lastErr != nil {

View File

@@ -5,7 +5,7 @@ import (
"errors"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"

View File

@@ -7,7 +7,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"

View File

@@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/management/internals/controllers/network_map"

View File

@@ -1,6 +1,6 @@
package account
//go:generate go run github.com/golang/mock/mockgen -package account -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
//go:generate go tool mockgen -package account -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
import (
"context"

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/prometheus/client_golang/prometheus/push"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
nbdns "github.com/netbirdio/netbird/dns"

View File

@@ -11,7 +11,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -12,7 +12,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"

View File

@@ -10,7 +10,7 @@ import (
"net/mail"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -13,9 +13,8 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/gorilla/mux"
ugomock "go.uber.org/mock/gomock"
"go.uber.org/mock/gomock"
"golang.org/x/exp/maps"
"github.com/netbirdio/netbird/management/internals/controllers/network_map"
@@ -106,7 +105,7 @@ func initTestMetaData(t *testing.T, peers ...*nbpeer.Peer) *Handler {
},
}
ctrl := ugomock.NewController(t)
ctrl := gomock.NewController(t)
networkMapController := network_map.NewMockController(ctrl)
networkMapController.EXPECT().

View File

@@ -10,7 +10,7 @@ import (
"path/filepath"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"

View File

@@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -5,7 +5,7 @@ import (
"errors"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -12,7 +12,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"

View File

@@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
pb "github.com/golang/protobuf/proto" //nolint
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -4,7 +4,7 @@ import (
"context"
"testing"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/stretchr/testify/require"
reverseproxy "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/service"

View File

@@ -16,7 +16,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"

View File

@@ -1,6 +1,6 @@
package permissions
//go:generate go run github.com/golang/mock/mockgen -package permissions -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
//go:generate go tool mockgen -package permissions -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
import (
"context"

View File

@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./manager.go
//
// Generated by this command:
//
// mockgen -package permissions -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
//
// Package permissions is a generated GoMock package.
package permissions
@@ -8,18 +13,19 @@ import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
account "github.com/netbirdio/netbird/management/server/account"
modules "github.com/netbirdio/netbird/management/server/permissions/modules"
operations "github.com/netbirdio/netbird/management/server/permissions/operations"
roles "github.com/netbirdio/netbird/management/server/permissions/roles"
types "github.com/netbirdio/netbird/management/server/types"
gomock "go.uber.org/mock/gomock"
)
// MockManager is a mock of Manager interface.
type MockManager struct {
ctrl *gomock.Controller
recorder *MockManagerMockRecorder
isgomock struct{}
}
// MockManagerMockRecorder is the mock recorder for MockManager.
@@ -49,7 +55,7 @@ func (m *MockManager) GetPermissionsByRole(ctx context.Context, role types.UserR
}
// GetPermissionsByRole indicates an expected call of GetPermissionsByRole.
func (mr *MockManagerMockRecorder) GetPermissionsByRole(ctx, role interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetPermissionsByRole(ctx, role any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPermissionsByRole", reflect.TypeOf((*MockManager)(nil).GetPermissionsByRole), ctx, role)
}
@@ -61,7 +67,7 @@ func (m *MockManager) SetAccountManager(accountManager account.Manager) {
}
// SetAccountManager indicates an expected call of SetAccountManager.
func (mr *MockManagerMockRecorder) SetAccountManager(accountManager interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) SetAccountManager(accountManager any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccountManager", reflect.TypeOf((*MockManager)(nil).SetAccountManager), accountManager)
}
@@ -76,7 +82,7 @@ func (m *MockManager) ValidateAccountAccess(ctx context.Context, accountID strin
}
// ValidateAccountAccess indicates an expected call of ValidateAccountAccess.
func (mr *MockManagerMockRecorder) ValidateAccountAccess(ctx, accountID, user, allowOwnerAndAdmin interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) ValidateAccountAccess(ctx, accountID, user, allowOwnerAndAdmin any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAccountAccess", reflect.TypeOf((*MockManager)(nil).ValidateAccountAccess), ctx, accountID, user, allowOwnerAndAdmin)
}
@@ -90,7 +96,7 @@ func (m *MockManager) ValidateRoleModuleAccess(ctx context.Context, accountID st
}
// ValidateRoleModuleAccess indicates an expected call of ValidateRoleModuleAccess.
func (mr *MockManagerMockRecorder) ValidateRoleModuleAccess(ctx, accountID, role, module, operation interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) ValidateRoleModuleAccess(ctx, accountID, role, module, operation any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRoleModuleAccess", reflect.TypeOf((*MockManager)(nil).ValidateRoleModuleAccess), ctx, accountID, role, module, operation)
}
@@ -106,7 +112,7 @@ func (m *MockManager) ValidateUserPermissions(ctx context.Context, accountID, us
}
// ValidateUserPermissions indicates an expected call of ValidateUserPermissions.
func (mr *MockManagerMockRecorder) ValidateUserPermissions(ctx, accountID, userID, module, operation interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) ValidateUserPermissions(ctx, accountID, userID, module, operation any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateUserPermissions", reflect.TypeOf((*MockManager)(nil).ValidateUserPermissions), ctx, accountID, userID, module, operation)
}

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
"github.com/rs/xid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

View File

@@ -1,6 +1,6 @@
package settings
//go:generate go run github.com/golang/mock/mockgen -package settings -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
//go:generate go tool mockgen -package settings -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
import (
"context"

View File

@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./manager.go
//
// Generated by this command:
//
// mockgen -package settings -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
//
// Package settings is a generated GoMock package.
package settings
@@ -9,15 +14,16 @@ import (
netip "net/netip"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
extra_settings "github.com/netbirdio/netbird/management/server/integrations/extra_settings"
types "github.com/netbirdio/netbird/management/server/types"
gomock "go.uber.org/mock/gomock"
)
// MockManager is a mock of Manager interface.
type MockManager struct {
ctrl *gomock.Controller
recorder *MockManagerMockRecorder
isgomock struct{}
}
// MockManagerMockRecorder is the mock recorder for MockManager.
@@ -37,6 +43,22 @@ func (m *MockManager) EXPECT() *MockManagerMockRecorder {
return m.recorder
}
// GetEffectiveNetworkRanges mocks base method.
func (m *MockManager) GetEffectiveNetworkRanges(ctx context.Context, accountID string) (netip.Prefix, netip.Prefix, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetEffectiveNetworkRanges", ctx, accountID)
ret0, _ := ret[0].(netip.Prefix)
ret1, _ := ret[1].(netip.Prefix)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// GetEffectiveNetworkRanges indicates an expected call of GetEffectiveNetworkRanges.
func (mr *MockManagerMockRecorder) GetEffectiveNetworkRanges(ctx, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEffectiveNetworkRanges", reflect.TypeOf((*MockManager)(nil).GetEffectiveNetworkRanges), ctx, accountID)
}
// GetExtraSettings mocks base method.
func (m *MockManager) GetExtraSettings(ctx context.Context, accountID string) (*types.ExtraSettings, error) {
m.ctrl.T.Helper()
@@ -47,7 +69,7 @@ func (m *MockManager) GetExtraSettings(ctx context.Context, accountID string) (*
}
// GetExtraSettings indicates an expected call of GetExtraSettings.
func (mr *MockManagerMockRecorder) GetExtraSettings(ctx, accountID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetExtraSettings(ctx, accountID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetExtraSettings", reflect.TypeOf((*MockManager)(nil).GetExtraSettings), ctx, accountID)
}
@@ -76,7 +98,7 @@ func (m *MockManager) GetSettings(ctx context.Context, accountID, userID string)
}
// GetSettings indicates an expected call of GetSettings.
func (mr *MockManagerMockRecorder) GetSettings(ctx, accountID, userID interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) GetSettings(ctx, accountID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSettings", reflect.TypeOf((*MockManager)(nil).GetSettings), ctx, accountID, userID)
}
@@ -91,23 +113,7 @@ func (m *MockManager) UpdateExtraSettings(ctx context.Context, accountID, userID
}
// UpdateExtraSettings indicates an expected call of UpdateExtraSettings.
func (mr *MockManagerMockRecorder) UpdateExtraSettings(ctx, accountID, userID, extraSettings interface{}) *gomock.Call {
func (mr *MockManagerMockRecorder) UpdateExtraSettings(ctx, accountID, userID, extraSettings any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateExtraSettings", reflect.TypeOf((*MockManager)(nil).UpdateExtraSettings), ctx, accountID, userID, extraSettings)
}
// GetEffectiveNetworkRanges mocks base method.
func (m *MockManager) GetEffectiveNetworkRanges(ctx context.Context, accountID string) (netip.Prefix, netip.Prefix, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetEffectiveNetworkRanges", ctx, accountID)
ret0, _ := ret[0].(netip.Prefix)
ret1, _ := ret[1].(netip.Prefix)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// GetEffectiveNetworkRanges indicates an expected call of GetEffectiveNetworkRanges.
func (mr *MockManagerMockRecorder) GetEffectiveNetworkRanges(ctx, accountID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEffectiveNetworkRanges", reflect.TypeOf((*MockManager)(nil).GetEffectiveNetworkRanges), ctx, accountID)
}

View File

@@ -1,6 +1,6 @@
package store
//go:generate go run github.com/golang/mock/mockgen -package store -destination=store_mock.go -source=./store.go -build_flags=-mod=mod
//go:generate go tool mockgen -package store -destination=store_mock.go -source=./store.go -build_flags=-mod=mod
import (
"context"

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,12 @@ const (
SessionJWTIssuer = "netbird-management"
)
// HeaderUserID is the synthetic user id recorded for header-authenticated
// requests. Header auth validates a per-service secret and resolves no user
// record, so proxy access logs and management-minted session tokens both
// attribute the request to this id.
const HeaderUserID = "header-user"
// ResolveProto determines the protocol scheme based on the forwarded proto
// configuration. When set to "http" or "https" the value is used directly.
// Otherwise TLS state is used: if conn is non-nil "https" is returned, else "http".

View File

@@ -1,36 +1,32 @@
package auth
import (
"errors"
"fmt"
"crypto/sha256"
"net/http"
"sync"
"github.com/netbirdio/netbird/proxy/auth"
"github.com/netbirdio/netbird/proxy/internal/types"
"github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/shared/hash/argon2id"
)
// ErrHeaderAuthFailed indicates that the header was present but the
// credential did not validate. Callers should return 401 instead of
// falling through to other auth schemes.
var ErrHeaderAuthFailed = errors.New("header authentication failed")
// Header implements header-based authentication. The proxy checks for the
// configured header in each request and validates its value via gRPC.
// Header implements header-based authentication. The service mapping carries
// the argon2id hash of every value accepted for the header, so the proxy
// verifies the credential locally rather than round-tripping to management.
type Header struct {
id types.ServiceID
accountId types.AccountID
headerName string
client authenticator
hashes []string
verified *verifiedValues
}
// NewHeader creates a Header authentication scheme for the given header name.
func NewHeader(client authenticator, id types.ServiceID, accountId types.AccountID, headerName string) Header {
// NewHeader creates a Header authentication scheme accepting any value whose
// argon2id hash appears in hashes. An empty hashes slice rejects every request
// carrying the header, so a mapping that arrived without its hashes fails
// closed instead of leaving the service unprotected.
func NewHeader(headerName string, hashes []string) Header {
return Header{
id: id,
accountId: accountId,
headerName: headerName,
client: client,
headerName: http.CanonicalHeaderKey(headerName),
hashes: hashes,
verified: &verifiedValues{seen: make(map[[32]byte]struct{}, len(hashes))},
}
}
@@ -39,31 +35,55 @@ func (Header) Type() auth.Method {
return auth.MethodHeader
}
// Authenticate checks for the configured header in the request. If absent,
// returns empty (unauthenticated). If present, validates via gRPC.
func (h Header) Authenticate(r *http.Request) (string, string, error) {
// Authenticate satisfies Scheme. Header credentials are resolved by Verify
// before the scheme loop runs, so a request that reaches here never carries
// the header and there is no credential to prompt for.
func (Header) Authenticate(*http.Request) (string, string, error) {
return "", "", nil
}
// Verify reports whether the request carries the configured header and, when
// it does, whether the value matches one of the service's hashes.
func (h Header) Verify(r *http.Request) (present, matched bool) {
value := r.Header.Get(h.headerName)
if value == "" {
return "", "", nil
return false, false
}
res, err := h.client.Authenticate(r.Context(), &proto.AuthenticateRequest{
Id: string(h.id),
AccountId: string(h.accountId),
Request: &proto.AuthenticateRequest_HeaderAuth{
HeaderAuth: &proto.HeaderAuthRequest{
HeaderValue: value,
HeaderName: h.headerName,
},
},
})
if err != nil {
return "", "", fmt.Errorf("authenticate header: %w", err)
digest := sha256.Sum256([]byte(value))
if h.verified.has(digest) {
return true, true
}
if res.GetSuccess() {
return res.GetSessionToken(), "", nil
for _, hash := range h.hashes {
if argon2id.Verify(value, hash) == nil {
h.verified.add(digest)
return true, true
}
}
return "", "", ErrHeaderAuthFailed
return true, false
}
// verifiedValues remembers which header values already passed argon2id
// verification. argon2id is deliberately expensive (19 MiB, two passes) and
// header credentials repeat on every request, so re-deriving per request would
// dominate the hot path. The set cannot outgrow the number of configured
// hashes, and a mapping update builds a fresh scheme with an empty set.
// Values are keyed by digest so the plaintext credential is not retained.
type verifiedValues struct {
mu sync.Mutex
seen map[[32]byte]struct{}
}
func (v *verifiedValues) has(digest [32]byte) bool {
v.mu.Lock()
defer v.mu.Unlock()
_, ok := v.seen[digest]
return ok
}
func (v *verifiedValues) add(digest [32]byte) {
v.mu.Lock()
defer v.mu.Unlock()
v.seen[digest] = struct{}{}
}

View File

@@ -146,7 +146,7 @@ func (mw *Middleware) Protect(next http.Handler) http.Handler {
return
}
if mw.forwardWithHeaderAuth(w, r, host, config, next) {
if mw.forwardWithHeaderAuth(w, r, config, next) {
return
}
@@ -436,14 +436,14 @@ func isTunnelSourceIP(ip netip.Addr) bool {
// forwardWithHeaderAuth checks for a Header auth scheme. If the header validates,
// the request is forwarded directly (no redirect), which is important for API clients.
func (mw *Middleware) forwardWithHeaderAuth(w http.ResponseWriter, r *http.Request, host string, config DomainConfig, next http.Handler) bool {
func (mw *Middleware) forwardWithHeaderAuth(w http.ResponseWriter, r *http.Request, config DomainConfig, next http.Handler) bool {
for _, scheme := range config.Schemes {
hdr, ok := scheme.(Header)
if !ok {
continue
}
handled := mw.tryHeaderScheme(w, r, host, config, hdr, next)
handled := mw.tryHeaderScheme(w, r, hdr, next)
if handled {
return true
}
@@ -451,40 +451,27 @@ func (mw *Middleware) forwardWithHeaderAuth(w http.ResponseWriter, r *http.Reque
return false
}
func (mw *Middleware) tryHeaderScheme(w http.ResponseWriter, r *http.Request, host string, config DomainConfig, hdr Header, next http.Handler) bool {
token, _, err := hdr.Authenticate(r)
if err != nil {
return mw.handleHeaderAuthError(w, r, err)
}
if token == "" {
// tryHeaderScheme verifies the credential against the hashes the service
// mapping carries. No session token is issued: the credential travels on
// every request, so there is nothing for a cookie to save.
func (mw *Middleware) tryHeaderScheme(w http.ResponseWriter, r *http.Request, hdr Header, next http.Handler) bool {
present, matched := hdr.Verify(r)
if !present {
return false
}
result, err := mw.validateSessionToken(r.Context(), host, token, config.SessionPublicKey, auth.MethodHeader)
if err != nil {
if !matched {
mw.logger.WithFields(log.Fields{
"host": r.Host,
"header": hdr.headerName,
}).Debug("header auth rejected: value does not match any configured hash")
setHeaderCapturedData(r.Context(), "", "", nil, nil)
status := http.StatusBadRequest
msg := "invalid session token"
if errors.Is(err, errValidationUnavailable) {
status = http.StatusBadGateway
msg = "authentication service unavailable"
}
http.Error(w, msg, status)
return true
}
if !result.Valid {
setHeaderCapturedData(r.Context(), result.UserID, result.UserEmail, result.Groups, result.GroupNames)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return true
}
setSessionCookie(w, token, config.SessionExpiration)
if cd := proxy.CapturedDataFromContext(r.Context()); cd != nil {
cd.SetUserID(result.UserID)
cd.SetUserEmail(result.UserEmail)
cd.SetUserGroups(result.Groups)
cd.SetUserGroupNames(result.GroupNames)
cd.SetUserID(auth.HeaderUserID)
cd.SetAuthMethod(auth.MethodHeader.String())
}
@@ -492,20 +479,6 @@ func (mw *Middleware) tryHeaderScheme(w http.ResponseWriter, r *http.Request, ho
return true
}
func (mw *Middleware) handleHeaderAuthError(w http.ResponseWriter, r *http.Request, err error) bool {
if errors.Is(err, ErrHeaderAuthFailed) {
setHeaderCapturedData(r.Context(), "", "", nil, nil)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return true
}
mw.logger.WithField("scheme", "header").Warnf("header auth infrastructure error: %v", err)
if cd := proxy.CapturedDataFromContext(r.Context()); cd != nil {
cd.SetOrigin(proxy.OriginAuth)
}
http.Error(w, "authentication service unavailable", http.StatusBadGateway)
return true
}
func setHeaderCapturedData(ctx context.Context, userID, userEmail string, groups, groupNames []string) {
cd := proxy.CapturedDataFromContext(ctx)
if cd == nil {

View File

@@ -25,6 +25,7 @@ import (
"github.com/netbirdio/netbird/proxy/internal/proxy"
"github.com/netbirdio/netbird/proxy/internal/restrict"
"github.com/netbirdio/netbird/proxy/internal/types"
"github.com/netbirdio/netbird/shared/hash/argon2id"
"github.com/netbirdio/netbird/shared/management/proto"
)
@@ -1023,38 +1024,24 @@ func TestProtect_OIDCWithOtherMethodShowsLoginPage(t *testing.T) {
assert.Equal(t, http.StatusUnauthorized, rec.Code, "should show login page when multiple methods exist")
}
// mockAuthenticator is a minimal mock for the authenticator gRPC interface
// used by the Header scheme.
type mockAuthenticator struct {
fn func(ctx context.Context, req *proto.AuthenticateRequest) (*proto.AuthenticateResponse, error)
}
func (m *mockAuthenticator) Authenticate(ctx context.Context, in *proto.AuthenticateRequest, _ ...grpc.CallOption) (*proto.AuthenticateResponse, error) {
return m.fn(ctx, in)
}
// newHeaderSchemeWithToken creates a Header scheme backed by a mock that
// returns a signed session token when the expected header value is provided.
func newHeaderSchemeWithToken(t *testing.T, kp *sessionkey.KeyPair, headerName, expectedValue string) Header {
// newHeaderScheme creates a Header scheme accepting each of the given values,
// hashed the way management hashes them before putting them on the mapping.
func newHeaderScheme(t *testing.T, headerName string, acceptedValues ...string) Header {
t.Helper()
token, err := sessionkey.SignToken(kp.PrivateKey, "header-user", "", "example.com", auth.MethodHeader, nil, nil, time.Hour)
require.NoError(t, err)
mock := &mockAuthenticator{fn: func(_ context.Context, req *proto.AuthenticateRequest) (*proto.AuthenticateResponse, error) {
ha := req.GetHeaderAuth()
if ha != nil && ha.GetHeaderValue() == expectedValue {
return &proto.AuthenticateResponse{Success: true, SessionToken: token}, nil
}
return &proto.AuthenticateResponse{Success: false}, nil
}}
return NewHeader(mock, "svc1", "acc1", headerName)
hashes := make([]string, 0, len(acceptedValues))
for _, v := range acceptedValues {
hash, err := argon2id.Hash(v)
require.NoError(t, err, "hashing an accepted header value must succeed")
hashes = append(hashes, hash)
}
return NewHeader(headerName, hashes)
}
func TestProtect_HeaderAuth_ForwardsOnSuccess(t *testing.T) {
mw := NewMiddleware(log.StandardLogger(), nil, nil)
kp := generateTestKeyPair(t)
hdr := newHeaderSchemeWithToken(t, kp, "X-API-Key", "secret-key")
hdr := newHeaderScheme(t, "X-API-Key", "secret-key")
require.NoError(t, mw.AddDomain("example.com", []Scheme{hdr}, kp.PublicKey, time.Hour, "acc1", "svc1", nil, false))
var backendCalled bool
@@ -1075,19 +1062,12 @@ func TestProtect_HeaderAuth_ForwardsOnSuccess(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "ok", rec.Body.String())
// Session cookie should be set.
var sessionCookie *http.Cookie
// The credential rides on every request, so no session cookie is issued.
for _, c := range rec.Result().Cookies() {
if c.Name == auth.SessionCookieName {
sessionCookie = c
break
}
assert.NotEqual(t, auth.SessionCookieName, c.Name, "header auth must not issue a session cookie")
}
require.NotNil(t, sessionCookie, "session cookie should be set after successful header auth")
assert.True(t, sessionCookie.HttpOnly)
assert.True(t, sessionCookie.Secure)
assert.Equal(t, "header-user", capturedData.GetUserID())
assert.Equal(t, auth.HeaderUserID, capturedData.GetUserID())
assert.Equal(t, "header", capturedData.GetAuthMethod())
}
@@ -1095,7 +1075,7 @@ func TestProtect_HeaderAuth_MissingHeaderFallsThrough(t *testing.T) {
mw := NewMiddleware(log.StandardLogger(), nil, nil)
kp := generateTestKeyPair(t)
hdr := newHeaderSchemeWithToken(t, kp, "X-API-Key", "secret-key")
hdr := newHeaderScheme(t, "X-API-Key", "secret-key")
// Also add a PIN scheme so we can verify fallthrough behavior.
pinScheme := &stubScheme{method: auth.MethodPIN, promptID: "pin"}
require.NoError(t, mw.AddDomain("example.com", []Scheme{hdr, pinScheme}, kp.PublicKey, time.Hour, "acc1", "svc1", nil, false))
@@ -1114,10 +1094,7 @@ func TestProtect_HeaderAuth_WrongValueReturns401(t *testing.T) {
mw := NewMiddleware(log.StandardLogger(), nil, nil)
kp := generateTestKeyPair(t)
mock := &mockAuthenticator{fn: func(_ context.Context, _ *proto.AuthenticateRequest) (*proto.AuthenticateResponse, error) {
return &proto.AuthenticateResponse{Success: false}, nil
}}
hdr := NewHeader(mock, "svc1", "acc1", "X-API-Key")
hdr := newHeaderScheme(t, "X-API-Key", "secret-key")
require.NoError(t, mw.AddDomain("example.com", []Scheme{hdr}, kp.PublicKey, time.Hour, "acc1", "svc1", nil, false))
capturedData := proxy.NewCapturedData("")
@@ -1131,93 +1108,113 @@ func TestProtect_HeaderAuth_WrongValueReturns401(t *testing.T) {
assert.Equal(t, http.StatusUnauthorized, rec.Code)
assert.Equal(t, "header", capturedData.GetAuthMethod())
assert.Empty(t, hdr.verified.seen, "a rejected value must not be memoized")
}
func TestProtect_HeaderAuth_InfraErrorReturns502(t *testing.T) {
// TestProtect_HeaderAuth_NoHashesFailsClosed covers a mapping that names a
// header but carries no hash for it: the check cannot be evaluated, so the
// request must be denied rather than let through unauthenticated.
func TestProtect_HeaderAuth_NoHashesFailsClosed(t *testing.T) {
mw := NewMiddleware(log.StandardLogger(), nil, nil)
kp := generateTestKeyPair(t)
mock := &mockAuthenticator{fn: func(_ context.Context, _ *proto.AuthenticateRequest) (*proto.AuthenticateResponse, error) {
return nil, errors.New("gRPC unavailable")
}}
hdr := NewHeader(mock, "svc1", "acc1", "X-API-Key")
require.NoError(t, mw.AddDomain("example.com", []Scheme{hdr}, kp.PublicKey, time.Hour, "acc1", "svc1", nil, false))
handler := mw.Protect(newPassthroughHandler())
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
req.Header.Set("X-API-Key", "some-key")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusBadGateway, rec.Code)
}
func TestProtect_HeaderAuth_SubsequentRequestUsesSessionCookie(t *testing.T) {
mw := NewMiddleware(log.StandardLogger(), nil, nil)
kp := generateTestKeyPair(t)
hdr := newHeaderSchemeWithToken(t, kp, "X-API-Key", "secret-key")
hdr := NewHeader("X-API-Key", nil)
require.NoError(t, mw.AddDomain("example.com", []Scheme{hdr}, kp.PublicKey, time.Hour, "acc1", "svc1", nil, false))
var backendCalled bool
handler := mw.Protect(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
backendCalled = true
w.WriteHeader(http.StatusOK)
}))
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
req.Header.Set("X-API-Key", "any-key")
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
assert.Equal(t, http.StatusUnauthorized, rec.Code)
assert.False(t, backendCalled, "a header auth with no hashes must not admit the request")
}
// TestProtect_HeaderAuth_SubsequentRequestRequiresHeader verifies that header
// auth grants no ambient session: a follow-up request that drops the header is
// treated as unauthenticated.
func TestProtect_HeaderAuth_SubsequentRequestRequiresHeader(t *testing.T) {
mw := NewMiddleware(log.StandardLogger(), nil, nil)
kp := generateTestKeyPair(t)
hdr := newHeaderScheme(t, "X-API-Key", "secret-key")
require.NoError(t, mw.AddDomain("example.com", []Scheme{hdr}, kp.PublicKey, time.Hour, "acc1", "svc1", nil, false))
var backendCalls int
handler := mw.Protect(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
backendCalls++
w.WriteHeader(http.StatusOK)
}))
// First request with header auth.
req1 := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
req1.Header.Set("X-API-Key", "secret-key")
req1 = req1.WithContext(proxy.WithCapturedData(req1.Context(), proxy.NewCapturedData("")))
rec1 := httptest.NewRecorder()
handler.ServeHTTP(rec1, req1)
require.Equal(t, http.StatusOK, rec1.Code)
require.Equal(t, 1, backendCalls)
// Extract session cookie.
var sessionCookie *http.Cookie
for _, c := range rec1.Result().Cookies() {
if c.Name == auth.SessionCookieName {
sessionCookie = c
break
}
}
require.NotNil(t, sessionCookie)
// Second request with only the session cookie (no header).
capturedData2 := proxy.NewCapturedData("")
// Same client, second request, header omitted: no cookie was handed out, so
// there is nothing to carry the earlier success forward.
req2 := httptest.NewRequest(http.MethodGet, "http://example.com/other", nil)
req2.AddCookie(sessionCookie)
req2 = req2.WithContext(proxy.WithCapturedData(req2.Context(), capturedData2))
for _, c := range rec1.Result().Cookies() {
req2.AddCookie(c)
}
rec2 := httptest.NewRecorder()
handler.ServeHTTP(rec2, req2)
assert.Equal(t, http.StatusOK, rec2.Code)
assert.Equal(t, "header-user", capturedData2.GetUserID())
assert.Equal(t, "header", capturedData2.GetAuthMethod())
assert.Equal(t, http.StatusUnauthorized, rec2.Code, "dropping the header must revoke access")
assert.Equal(t, 1, backendCalls, "backend must not be reached without the header")
}
// TestProtect_HeaderAuth_MultipleValuesSameHeader verifies that the proxy
// correctly handles multiple valid credentials for the same header name.
// In production, the mgmt gRPC authenticateHeader iterates all configured
// header auths and accepts if any hash matches (OR semantics). The proxy
// creates one Header scheme per entry, but a single gRPC call checks all.
// TestProtect_HeaderAuth_RepeatedValueIsMemoized verifies the KDF is run once
// per distinct accepted value. argon2id is deliberately expensive, so a
// credential that repeats on every request must not be re-derived each time.
func TestProtect_HeaderAuth_RepeatedValueIsMemoized(t *testing.T) {
mw := NewMiddleware(log.StandardLogger(), nil, nil)
kp := generateTestKeyPair(t)
hdr := newHeaderScheme(t, "X-API-Key", "key-a", "key-b")
require.NoError(t, mw.AddDomain("example.com", []Scheme{hdr}, kp.PublicKey, time.Hour, "acc1", "svc1", nil, false))
handler := mw.Protect(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
get := func(value string) int {
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
req.Header.Set("X-API-Key", value)
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
return rec.Code
}
require.Equal(t, http.StatusOK, get("key-a"))
require.Equal(t, http.StatusOK, get("key-a"))
assert.Len(t, hdr.verified.seen, 1, "the same value must be memoized once")
require.Equal(t, http.StatusOK, get("key-b"))
assert.Len(t, hdr.verified.seen, 2, "each accepted value gets its own entry")
require.Equal(t, http.StatusUnauthorized, get("key-c"))
assert.Len(t, hdr.verified.seen, 2, "rejected values must not grow the set")
}
// TestProtect_HeaderAuth_MultipleValuesSameHeader verifies that a service with
// several accepted credentials for one header name accepts any of them.
// Management applied these OR semantics while it still validated the value; the
// proxy preserves them by carrying every hash for a name on one scheme.
func TestProtect_HeaderAuth_MultipleValuesSameHeader(t *testing.T) {
mw := NewMiddleware(log.StandardLogger(), nil, nil)
kp := generateTestKeyPair(t)
// Mock simulates mgmt behavior: accepts either token-a or token-b.
accepted := map[string]bool{"Bearer token-a": true, "Bearer token-b": true}
mock := &mockAuthenticator{fn: func(_ context.Context, req *proto.AuthenticateRequest) (*proto.AuthenticateResponse, error) {
ha := req.GetHeaderAuth()
if ha != nil && accepted[ha.GetHeaderValue()] {
token, err := sessionkey.SignToken(kp.PrivateKey, "header-user", "", "example.com", auth.MethodHeader, nil, nil, time.Hour)
require.NoError(t, err)
return &proto.AuthenticateResponse{Success: true, SessionToken: token}, nil
}
return &proto.AuthenticateResponse{Success: false}, nil
}}
// Single Header scheme (as if one entry existed), but the mock checks both values.
hdr := NewHeader(mock, "svc1", "acc1", "Authorization")
hdr := newHeaderScheme(t, "Authorization", "Bearer token-a", "Bearer token-b")
require.NoError(t, mw.AddDomain("example.com", []Scheme{hdr}, kp.PublicKey, time.Hour, "acc1", "svc1", nil, false))
var backendCalled bool

View File

@@ -20,6 +20,7 @@ import (
"net/url"
"path/filepath"
"reflect"
"slices"
"sync"
"time"
@@ -2062,9 +2063,7 @@ func (s *Server) updateMapping(ctx context.Context, mapping *proto.ProxyMapping)
if mapping.GetAuth().GetOidc() {
schemes = append(schemes, auth.NewOIDC(s.mgmtClient, svcID, accountID, s.ForwardedProto))
}
for _, ha := range mapping.GetAuth().GetHeaderAuths() {
schemes = append(schemes, auth.NewHeader(s.mgmtClient, svcID, accountID, ha.GetHeader()))
}
schemes = append(schemes, headerAuthSchemes(mapping.GetAuth().GetHeaderAuths())...)
ipRestrictions := s.parseRestrictions(mapping)
s.warnIfGeoUnavailable(mapping.GetDomain(), mapping.GetAccessRestrictions())
@@ -2080,6 +2079,34 @@ func (s *Server) updateMapping(ctx context.Context, mapping *proto.ProxyMapping)
return nil
}
// headerAuthSchemes builds one scheme per canonical header name, carrying every
// hash configured for that name so any of them is accepted — the OR semantics
// management applied while it still validated the credential itself. A name
// whose entries arrive without a hash yields a scheme with none, which rejects
// the header rather than leaving the service unprotected.
func headerAuthSchemes(headerAuths []*proto.HeaderAuth) []auth.Scheme {
names := make([]string, 0, len(headerAuths))
hashes := make(map[string][]string, len(headerAuths))
for _, ha := range headerAuths {
name := http.CanonicalHeaderKey(ha.GetHeader())
if name == "" {
continue
}
if !slices.Contains(names, name) {
names = append(names, name)
}
if hash := ha.GetHashedValue(); hash != "" {
hashes[name] = append(hashes[name], hash)
}
}
schemes := make([]auth.Scheme, 0, len(names))
for _, name := range names {
schemes = append(schemes, auth.NewHeader(name, hashes[name]))
}
return schemes
}
// initMiddlewareManager wires the middleware subsystem at boot. It configures
// the per-process FactoryContext concrete middlewares consult, installs the
// live-service check, and binds the resolver to the registry concrete

View File

@@ -6,6 +6,8 @@ import (
"fmt"
"io"
"net"
"net/http"
"net/http/httptest"
"testing"
"time"
@@ -15,8 +17,10 @@ import (
"go.opentelemetry.io/otel/metric/noop"
"google.golang.org/grpc"
"github.com/netbirdio/netbird/proxy/internal/auth"
proxymetrics "github.com/netbirdio/netbird/proxy/internal/metrics"
"github.com/netbirdio/netbird/proxy/internal/types"
"github.com/netbirdio/netbird/shared/hash/argon2id"
"github.com/netbirdio/netbird/shared/management/proto"
)
@@ -209,6 +213,50 @@ func TestRedactMappingForLog_HandlesEmptyOrNilFields(t *testing.T) {
assert.Empty(t, redacted.Path, "empty Path must remain empty")
}
// headerSchemeAccepts reports whether the scheme admits value for headerName.
func headerSchemeAccepts(t *testing.T, scheme auth.Scheme, headerName, value string) bool {
t.Helper()
hdr, ok := scheme.(auth.Header)
require.True(t, ok, "header auths must produce Header schemes")
req := httptest.NewRequest(http.MethodGet, "http://example.com/", nil)
req.Header.Set(headerName, value)
_, matched := hdr.Verify(req)
return matched
}
func TestHeaderAuthSchemes_GroupsValuesByCanonicalHeaderName(t *testing.T) {
hashOf := func(v string) string {
hash, err := argon2id.Hash(v)
require.NoError(t, err)
return hash
}
schemes := headerAuthSchemes([]*proto.HeaderAuth{
{Header: "Authorization", HashedValue: hashOf("Bearer a")},
{Header: "authorization", HashedValue: hashOf("Bearer b")},
{Header: "X-Api-Key", HashedValue: hashOf("key-1")},
})
require.Len(t, schemes, 2, "entries differing only in header-name case must collapse into one scheme")
assert.True(t, headerSchemeAccepts(t, schemes[0], "Authorization", "Bearer a"), "first value for the header must be accepted")
assert.True(t, headerSchemeAccepts(t, schemes[0], "Authorization", "Bearer b"), "second value for the same header must be accepted")
assert.False(t, headerSchemeAccepts(t, schemes[0], "Authorization", "Bearer c"), "unconfigured value must be rejected")
assert.True(t, headerSchemeAccepts(t, schemes[1], "X-Api-Key", "key-1"), "a second header name keeps its own scheme")
}
// TestHeaderAuthSchemes_MissingHashFailsClosed covers a mapping that names a
// header but carries no hash for it. Dropping the scheme would leave a service
// whose only auth is that header wide open, so the scheme is kept and denies.
func TestHeaderAuthSchemes_MissingHashFailsClosed(t *testing.T) {
schemes := headerAuthSchemes([]*proto.HeaderAuth{{Header: "X-Api-Key"}})
require.Len(t, schemes, 1, "a header without a hash must still register a scheme")
assert.False(t, headerSchemeAccepts(t, schemes[0], "X-Api-Key", "anything"),
"a header auth without a hash must reject every value")
}
type statusUpdateOnlyClient struct {
proto.ProxyServiceClient
}

View File

@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"go.uber.org/mock/gomock"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"