mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-05 15:26:35 +00:00
Compare commits
12 Commits
crowdin_de
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9a509be53 | ||
|
|
7fa1180d10 | ||
|
|
769d36e289 | ||
|
|
a7a41b820e | ||
|
|
8b50f1fb65 | ||
|
|
2d78a4b628 | ||
|
|
527d4cc777 | ||
|
|
01361884eb | ||
|
|
6c4cbcab5d | ||
|
|
aac25f0a53 | ||
|
|
f617f93a94 | ||
|
|
51629247a5 |
@@ -38,7 +38,5 @@ flags:
|
||||
disable_user_create_org: false
|
||||
allow_raw_resources: true
|
||||
|
||||
{{if .IsPostgreSQL}}
|
||||
postgres:
|
||||
connection_string: postgresql://pangolin:{{.IsPostgreSQLPass}}@postgres:5432/pangolin
|
||||
{{end}}
|
||||
{{if .IsPostgreSQL}}postgres:
|
||||
connection_string: postgresql://pangolin:{{.IsPostgreSQLPass}}@postgres:5432/pangolin{{end}}
|
||||
|
||||
@@ -7,23 +7,17 @@ services:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1g
|
||||
memory: 2g
|
||||
reservations:
|
||||
memory: 256m
|
||||
{{if or .IsPostgreSQL .IsRedis}}
|
||||
depends_on:
|
||||
{{if .IsPostgreSQL}}
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
{{end}}
|
||||
{{if .IsRedis}}
|
||||
redis:
|
||||
condition: service_healthy
|
||||
{{end}}
|
||||
memory: 512m
|
||||
{{if or .IsPostgreSQL .IsRedis}}depends_on:
|
||||
{{if .IsPostgreSQL}}postgres:
|
||||
condition: service_healthy{{end}}
|
||||
{{if .IsRedis}}redis:
|
||||
condition: service_healthy{{end}}
|
||||
networks:
|
||||
- default
|
||||
- backend
|
||||
{{end}}
|
||||
- backend{{end}}
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
healthcheck:
|
||||
@@ -31,8 +25,8 @@ services:
|
||||
interval: "10s"
|
||||
timeout: "10s"
|
||||
retries: 15
|
||||
{{if .InstallGerbil}}
|
||||
gerbil:
|
||||
|
||||
{{if .InstallGerbil}}gerbil:
|
||||
image: docker.io/fosrl/gerbil:{{.GerbilVersion}}
|
||||
container_name: gerbil
|
||||
restart: unless-stopped
|
||||
@@ -53,17 +47,16 @@ services:
|
||||
- 21820:21820/udp
|
||||
- 443:443
|
||||
- 443:443/udp # For http3 QUIC if desired
|
||||
- 80:80
|
||||
{{end}}
|
||||
- 80:80{{end}}
|
||||
|
||||
traefik:
|
||||
image: docker.io/traefik:v3.6
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
{{if .InstallGerbil}} network_mode: service:gerbil # Ports appear on the gerbil service{{end}}{{if not .InstallGerbil}}
|
||||
{{if .InstallGerbil}}network_mode: service:gerbil # Ports appear on the gerbil service{{end}}{{if not .InstallGerbil}}
|
||||
ports:
|
||||
- 443:443
|
||||
- 80:80
|
||||
{{end}}
|
||||
- 80:80{{end}}
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
@@ -74,8 +67,7 @@ services:
|
||||
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
|
||||
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
|
||||
|
||||
{{if .IsPostgreSQL}}
|
||||
postgres:
|
||||
{{if .IsPostgreSQL}}postgres:
|
||||
image: postgres:18
|
||||
container_name: postgres
|
||||
restart: unless-stopped
|
||||
@@ -91,11 +83,9 @@ services:
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- backend
|
||||
{{end}}
|
||||
- backend{{end}}
|
||||
|
||||
{{if .IsRedis}}
|
||||
redis:
|
||||
{{if .IsRedis}}redis:
|
||||
image: redis:8-trixie
|
||||
container_name: redis
|
||||
restart: unless-stopped
|
||||
@@ -113,17 +103,14 @@ services:
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
networks:
|
||||
- backend
|
||||
{{end}}
|
||||
- backend{{end}}
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
name: pangolin_frontend
|
||||
{{if .EnableIPv6}} enable_ipv6: true{{end}}
|
||||
{{if or .IsPostgreSQL .IsRedis}}
|
||||
backend:
|
||||
{{if or .IsPostgreSQL .IsRedis}} backend:
|
||||
driver: bridge
|
||||
name: pangolin_backend
|
||||
internal: true
|
||||
{{end}}
|
||||
internal: true{{end}}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
{{if .IsRedis}}
|
||||
redis:
|
||||
{{if .IsRedis}}redis:
|
||||
host: "redis"
|
||||
port: 6379
|
||||
password: "{{.IsRedisPass}}"
|
||||
{{end}}
|
||||
password: "{{.IsRedisPass}}"{{end}}
|
||||
|
||||
@@ -71,9 +71,12 @@ const (
|
||||
Undefined SupportedContainer = "undefined"
|
||||
)
|
||||
|
||||
var redisFlag *bool
|
||||
|
||||
func main() {
|
||||
|
||||
crowdsecFlag := flag.Bool("crowdsec", false, "Enable the CrowdSec installation prompt")
|
||||
redisFlag = flag.Bool("redis", false, "Install Redis as cacheing solution. Required for HA. Not required for the Enterprise version.")
|
||||
flag.Parse()
|
||||
|
||||
// print a banner about prerequisites - opening port 80, 443, 51820, and 21820 on the VPS and firewall and pointing your domain to the VPS IP with a records. Docs are at http://localhost:3000/Getting%20Started/dns-networking
|
||||
@@ -491,13 +494,13 @@ func collectUserInput() Config {
|
||||
|
||||
config.IsEnterprise = readBoolNoDefault("Do you want to install the Enterprise version of Pangolin? The EE is free for personal use or for businesses making less than 100k USD annually.")
|
||||
if config.IsEnterprise {
|
||||
config.IsRedis = readBool("Do you want to run the Redis containers locally? Required for HA.")
|
||||
if config.IsRedis {
|
||||
if *redisFlag {
|
||||
config.IsRedis = true
|
||||
config.IsRedisPass = readPassword("Enter a unique password for the Redis service.")
|
||||
}
|
||||
}
|
||||
|
||||
config.IsPostgreSQL = readBool("Do you want to run the PostgreSQL containers locally? Otherwise, default to the local SQLite database only.", false)
|
||||
config.IsPostgreSQL = readBool("Do you want to use PostgreSQL (not recommended for most users)?", false)
|
||||
if config.IsPostgreSQL {
|
||||
config.IsPostgreSQLPass = readPassword("Enter a unique password for the PostgreSQL pangolin user.")
|
||||
}
|
||||
@@ -544,7 +547,7 @@ func collectUserInput() Config {
|
||||
fmt.Println("\n=== Advanced Configuration ===")
|
||||
|
||||
config.EnableIPv6 = readBool("Is your server IPv6 capable?", true)
|
||||
config.EnableMaxMind = readBool("Do you want to download the MaxMind GeoLite2 Country and ADN databases for blocking functionality?", true)
|
||||
config.EnableMaxMind = readBool("Do you want to download the MaxMind GeoLite2 Country and ASN databases for blocking functionality?", true)
|
||||
|
||||
if config.DashboardDomain == "" {
|
||||
fmt.Println("Error: Dashboard Domain name is required")
|
||||
|
||||
@@ -2046,6 +2046,7 @@
|
||||
"requireDeviceApproval": "Require Device Approvals",
|
||||
"requireDeviceApprovalDescription": "Users with this role need new devices approved by an admin before they can connect and access resources.",
|
||||
"sshSettings": "SSH Settings",
|
||||
"sshAccess": "SSH Access",
|
||||
"rdpSettings": "RDP Settings",
|
||||
"vncSettings": "VNC Settings",
|
||||
"sshServer": "SSH Server",
|
||||
|
||||
344
package-lock.json
generated
344
package-lock.json
generated
@@ -144,16 +144,16 @@
|
||||
"drizzle-kit": "0.31.10",
|
||||
"esbuild": "0.28.0",
|
||||
"esbuild-node-externals": "1.22.0",
|
||||
"eslint": "10.4.0",
|
||||
"eslint-config-next": "16.2.6",
|
||||
"eslint": "10.4.1",
|
||||
"eslint-config-next": "16.2.7",
|
||||
"postcss": "8.5.15",
|
||||
"prettier": "3.8.3",
|
||||
"react-email": "6.5.0",
|
||||
"tailwindcss": "4.3.0",
|
||||
"tsc-alias": "1.8.17",
|
||||
"tsx": "4.22.3",
|
||||
"tsx": "4.22.4",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.60.0"
|
||||
"typescript-eslint": "8.60.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@@ -1783,9 +1783,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz",
|
||||
"integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==",
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
|
||||
"integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -2076,9 +2076,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2095,9 +2092,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2114,9 +2108,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2133,9 +2124,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2152,9 +2140,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2187,9 +2172,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2222,9 +2204,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2247,9 +2226,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2272,9 +2248,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2297,9 +2270,6 @@
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2322,9 +2292,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2369,9 +2336,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2616,9 +2580,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.6.tgz",
|
||||
"integrity": "sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw==",
|
||||
"version": "16.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.7.tgz",
|
||||
"integrity": "sha512-VbS+QgMHqvIDMTIqD2xMBKK1otIpdAUKA8VLHFwR9h6OfU/mOm7w/69nQcvdmI8hCk99Wr2AsGLn/PJ/tMHw1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2664,9 +2628,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2683,9 +2644,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2941,9 +2899,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -2960,9 +2915,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -3200,9 +3152,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -3219,9 +3168,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -3582,9 +3528,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -3605,9 +3548,6 @@
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -3628,9 +3568,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -3651,9 +3588,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6873,9 +6807,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6892,9 +6823,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6911,9 +6839,6 @@
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -6930,9 +6855,6 @@
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "Apache-2.0 AND MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7200,9 +7122,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7220,9 +7139,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -7296,6 +7212,72 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.1",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.4",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@tybys/wasm-util": "^0.10.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emnapi/core": "^1.7.1",
|
||||
"@emnapi/runtime": "^1.7.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz",
|
||||
@@ -8113,17 +8095,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz",
|
||||
"integrity": "sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz",
|
||||
"integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.60.0",
|
||||
"@typescript-eslint/type-utils": "8.60.0",
|
||||
"@typescript-eslint/utils": "8.60.0",
|
||||
"@typescript-eslint/visitor-keys": "8.60.0",
|
||||
"@typescript-eslint/scope-manager": "8.60.1",
|
||||
"@typescript-eslint/type-utils": "8.60.1",
|
||||
"@typescript-eslint/utils": "8.60.1",
|
||||
"@typescript-eslint/visitor-keys": "8.60.1",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
@@ -8136,7 +8118,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.60.0",
|
||||
"@typescript-eslint/parser": "^8.60.1",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
@@ -8152,16 +8134,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.0.tgz",
|
||||
"integrity": "sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz",
|
||||
"integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.60.0",
|
||||
"@typescript-eslint/types": "8.60.0",
|
||||
"@typescript-eslint/typescript-estree": "8.60.0",
|
||||
"@typescript-eslint/visitor-keys": "8.60.0",
|
||||
"@typescript-eslint/scope-manager": "8.60.1",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/typescript-estree": "8.60.1",
|
||||
"@typescript-eslint/visitor-keys": "8.60.1",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -8177,14 +8159,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.0.tgz",
|
||||
"integrity": "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz",
|
||||
"integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.60.0",
|
||||
"@typescript-eslint/types": "^8.60.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.60.1",
|
||||
"@typescript-eslint/types": "^8.60.1",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -8199,14 +8181,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz",
|
||||
"integrity": "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz",
|
||||
"integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.60.0",
|
||||
"@typescript-eslint/visitor-keys": "8.60.0"
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/visitor-keys": "8.60.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -8217,9 +8199,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz",
|
||||
"integrity": "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz",
|
||||
"integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -8234,15 +8216,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz",
|
||||
"integrity": "sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz",
|
||||
"integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.60.0",
|
||||
"@typescript-eslint/typescript-estree": "8.60.0",
|
||||
"@typescript-eslint/utils": "8.60.0",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/typescript-estree": "8.60.1",
|
||||
"@typescript-eslint/utils": "8.60.1",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
@@ -8259,9 +8241,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.0.tgz",
|
||||
"integrity": "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz",
|
||||
"integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -8273,16 +8255,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz",
|
||||
"integrity": "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz",
|
||||
"integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.60.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.60.0",
|
||||
"@typescript-eslint/types": "8.60.0",
|
||||
"@typescript-eslint/visitor-keys": "8.60.0",
|
||||
"@typescript-eslint/project-service": "8.60.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.60.1",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/visitor-keys": "8.60.1",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -8301,16 +8283,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.0.tgz",
|
||||
"integrity": "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz",
|
||||
"integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.60.0",
|
||||
"@typescript-eslint/types": "8.60.0",
|
||||
"@typescript-eslint/typescript-estree": "8.60.0"
|
||||
"@typescript-eslint/scope-manager": "8.60.1",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/typescript-estree": "8.60.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -8325,13 +8307,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz",
|
||||
"integrity": "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz",
|
||||
"integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.60.0",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -8448,9 +8430,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8465,9 +8444,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8482,9 +8458,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8499,9 +8472,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8516,9 +8486,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -8533,9 +8500,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -11347,9 +11311,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz",
|
||||
"integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==",
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz",
|
||||
"integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -11358,7 +11322,7 @@
|
||||
"@eslint/config-array": "^0.23.5",
|
||||
"@eslint/config-helpers": "^0.6.0",
|
||||
"@eslint/core": "^1.2.1",
|
||||
"@eslint/plugin-kit": "^0.7.1",
|
||||
"@eslint/plugin-kit": "^0.7.2",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
@@ -11403,13 +11367,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.6.tgz",
|
||||
"integrity": "sha512-z2ELYSkyrrJ6cuunTU8vhsT/RpouPkjaSah06nVW6Rg2Hpg0Vs8s497/e5s8G8qtdp4ccsiovz5P1rv+5VSW2Q==",
|
||||
"version": "16.2.7",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.7.tgz",
|
||||
"integrity": "sha512-CQ2aNXkrsjaGA2oJBE1LYnlRdphIAQE9ZQfX9hSv1PNGPyiOMSaVeBfTIO29QxYz+ij/hZudK0cfpCG1HXWstg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/eslint-plugin-next": "16.2.6",
|
||||
"@next/eslint-plugin-next": "16.2.7",
|
||||
"eslint-import-resolver-node": "^0.3.6",
|
||||
"eslint-import-resolver-typescript": "^3.5.2",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
@@ -13767,9 +13731,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -13791,9 +13752,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15001,9 +14959,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -15020,9 +14975,6 @@
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -17658,9 +17610,9 @@
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.22.3",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.3.tgz",
|
||||
"integrity": "sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==",
|
||||
"version": "4.22.4",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz",
|
||||
"integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -17874,16 +17826,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint": {
|
||||
"version": "8.60.0",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.0.tgz",
|
||||
"integrity": "sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz",
|
||||
"integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "8.60.0",
|
||||
"@typescript-eslint/parser": "8.60.0",
|
||||
"@typescript-eslint/typescript-estree": "8.60.0",
|
||||
"@typescript-eslint/utils": "8.60.0"
|
||||
"@typescript-eslint/eslint-plugin": "8.60.1",
|
||||
"@typescript-eslint/parser": "8.60.1",
|
||||
"@typescript-eslint/typescript-estree": "8.60.1",
|
||||
"@typescript-eslint/utils": "8.60.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
||||
@@ -167,16 +167,16 @@
|
||||
"drizzle-kit": "0.31.10",
|
||||
"esbuild": "0.28.0",
|
||||
"esbuild-node-externals": "1.22.0",
|
||||
"eslint": "10.4.0",
|
||||
"eslint-config-next": "16.2.6",
|
||||
"eslint": "10.4.1",
|
||||
"eslint-config-next": "16.2.7",
|
||||
"postcss": "8.5.15",
|
||||
"prettier": "3.8.3",
|
||||
"react-email": "6.5.0",
|
||||
"tailwindcss": "4.3.0",
|
||||
"tsc-alias": "1.8.17",
|
||||
"tsx": "4.22.3",
|
||||
"tsx": "4.22.4",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.60.0"
|
||||
"typescript-eslint": "8.60.1"
|
||||
},
|
||||
"overrides": {
|
||||
"esbuild": "0.28.0",
|
||||
|
||||
@@ -665,7 +665,7 @@ export async function generateSubnetProxyTargetV2(
|
||||
return;
|
||||
}
|
||||
|
||||
let targets: SubnetProxyTargetV2[] = [];
|
||||
const targets: SubnetProxyTargetV2[] = [];
|
||||
|
||||
const portRange = [
|
||||
...parsePortRangeString(siteResource.tcpPortRangeString, "tcp"),
|
||||
|
||||
@@ -44,7 +44,8 @@ export async function getTraefikConfig(
|
||||
filterOutNamespaceDomains = false, // UNUSED BUT USED IN PRIVATE
|
||||
generateLoginPageRouters = false, // UNUSED BUT USED IN PRIVATE
|
||||
allowRawResources = true,
|
||||
allowMaintenancePage = true // UNUSED BUT USED IN PRIVATE
|
||||
allowMaintenancePage = true, // UNUSED BUT USED IN PRIVATE
|
||||
allowBrowserGatewayResources = true
|
||||
): Promise<any> {
|
||||
// Get resources with their targets and sites in a single optimized query
|
||||
// Start from sites on this exit node, then join to targets and resources
|
||||
@@ -240,7 +241,7 @@ export async function getTraefikConfig(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resource.http) {
|
||||
if (resource.mode === "http") {
|
||||
if (!resource.domainId || !resource.fullDomain) {
|
||||
continue;
|
||||
}
|
||||
@@ -572,7 +573,7 @@ export async function getTraefikConfig(
|
||||
serviceName
|
||||
].loadBalancer.serversTransport = transportName;
|
||||
}
|
||||
} else {
|
||||
} else if (resource.mode === "tcp" || resource.mode === "udp") {
|
||||
// Non-HTTP (TCP/UDP) configuration
|
||||
if (!resource.enableProxy || !resource.proxyPort) {
|
||||
continue;
|
||||
|
||||
322
server/middlewares/verifySiteAccess.test.ts
Normal file
322
server/middlewares/verifySiteAccess.test.ts
Normal file
@@ -0,0 +1,322 @@
|
||||
import { assertEquals } from "@test/assert";
|
||||
|
||||
/**
|
||||
* Tests for the cross-organization site binding prevention in verifySiteAccess.
|
||||
*
|
||||
* verifySiteAccess now includes a check: if req.userOrgId is already set by a
|
||||
* previous middleware (e.g. verifyResourceAccess or verifyTargetAccess), and the
|
||||
* loaded site's orgId differs from req.userOrgId, the request is rejected with
|
||||
* 403 Forbidden.
|
||||
*
|
||||
* Route stacks after fix:
|
||||
* PUT /resource/:resourceId/target
|
||||
* → verifyResourceAccess → verifySiteAccess → verifyLimits → ...
|
||||
* POST /target/:targetId
|
||||
* → verifyTargetAccess → verifySiteAccess → verifyLimits → ...
|
||||
*
|
||||
* verifyResourceAccess sets req.userOrgId to the resource's org.
|
||||
* verifyTargetAccess sets req.userOrgId to the target's resource org.
|
||||
* verifySiteAccess then checks site.orgId against req.userOrgId before
|
||||
* overwriting it with the site's org.
|
||||
*/
|
||||
|
||||
// --- Core org-matching logic (mirrors the check in verifySiteAccess) ---
|
||||
function siteOrgMatchesExpectedOrg(
|
||||
siteOrgId: string | null | undefined,
|
||||
expectedOrgId: string | null | undefined
|
||||
): boolean {
|
||||
if (!siteOrgId || !expectedOrgId) {
|
||||
return false;
|
||||
}
|
||||
return siteOrgId === expectedOrgId;
|
||||
}
|
||||
|
||||
// Simulates the condition check in verifySiteAccess:
|
||||
// if (req.userOrgId && site.orgId !== req.userOrgId) { reject }
|
||||
function shouldRejectCrossOrgSite(
|
||||
siteOrgId: string,
|
||||
reqUserOrgId: string | undefined
|
||||
): boolean {
|
||||
// The actual check in verifySiteAccess is:
|
||||
// if (req.userOrgId && site.orgId !== req.userOrgId) { reject }
|
||||
return !!(reqUserOrgId && siteOrgId !== reqUserOrgId);
|
||||
}
|
||||
|
||||
// --- Tests ---
|
||||
|
||||
function testSiteOrgMatchLogic() {
|
||||
console.log("Running verifySiteAccess org-match logic tests...");
|
||||
|
||||
// Test 1: Same org — should match
|
||||
{
|
||||
const result = siteOrgMatchesExpectedOrg(
|
||||
"org-attacker",
|
||||
"org-attacker"
|
||||
);
|
||||
assertEquals(result, true, "Same org should match");
|
||||
}
|
||||
|
||||
// Test 2: Different org — should NOT match (cross-org bypass scenario)
|
||||
{
|
||||
const result = siteOrgMatchesExpectedOrg("org-victim", "org-attacker");
|
||||
assertEquals(
|
||||
result,
|
||||
false,
|
||||
"Cross-org site should NOT match expected org"
|
||||
);
|
||||
}
|
||||
|
||||
// Test 3: Site orgId is null — should NOT match
|
||||
{
|
||||
const result = siteOrgMatchesExpectedOrg(null, "org-attacker");
|
||||
assertEquals(result, false, "Null site orgId should NOT match");
|
||||
}
|
||||
|
||||
// Test 4: Expected orgId is null — should NOT match
|
||||
{
|
||||
const result = siteOrgMatchesExpectedOrg("org-attacker", null);
|
||||
assertEquals(result, false, "Null expected orgId should NOT match");
|
||||
}
|
||||
|
||||
// Test 5: Both null — should NOT match
|
||||
{
|
||||
const result = siteOrgMatchesExpectedOrg(null, null);
|
||||
assertEquals(result, false, "Both null should NOT match");
|
||||
}
|
||||
|
||||
// Test 6: Empty string orgIds — should NOT match (empty string is falsy)
|
||||
{
|
||||
const result = siteOrgMatchesExpectedOrg("", "org-attacker");
|
||||
assertEquals(result, false, "Empty site orgId should NOT match");
|
||||
}
|
||||
|
||||
// Test 7: Undefined orgIds — should NOT match
|
||||
{
|
||||
const result = siteOrgMatchesExpectedOrg(undefined, "org-attacker");
|
||||
assertEquals(result, false, "Undefined site orgId should NOT match");
|
||||
}
|
||||
|
||||
console.log("All verifySiteAccess org-match logic tests passed.");
|
||||
}
|
||||
|
||||
function testShouldRejectCrossOrgSite() {
|
||||
console.log(
|
||||
"Running shouldRejectCrossOrgSite tests (mirrors verifySiteAccess check)..."
|
||||
);
|
||||
|
||||
// Test: No prior org context (undefined) — should NOT reject
|
||||
// This is the normal case for site-only routes (e.g. PUT /site/:siteId)
|
||||
// where verifySiteAccess runs without a prior verifyResourceAccess.
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite("org-victim", undefined);
|
||||
assertEquals(
|
||||
shouldReject,
|
||||
false,
|
||||
"No prior org context should NOT reject (normal site routes)"
|
||||
);
|
||||
}
|
||||
|
||||
// Test: Same org — should NOT reject
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite(
|
||||
"org-attacker",
|
||||
"org-attacker"
|
||||
);
|
||||
assertEquals(shouldReject, false, "Same org should NOT reject");
|
||||
}
|
||||
|
||||
// Test: Different org — should reject
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite(
|
||||
"org-victim",
|
||||
"org-attacker"
|
||||
);
|
||||
assertEquals(shouldReject, true, "Cross-org site should be rejected");
|
||||
}
|
||||
|
||||
// Test: Empty string userOrgId — should NOT reject (falsy, check is skipped)
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite("org-victim", "");
|
||||
assertEquals(
|
||||
shouldReject,
|
||||
false,
|
||||
"Empty string userOrgId should NOT reject (check is skipped)"
|
||||
);
|
||||
}
|
||||
|
||||
console.log("All shouldRejectCrossOrgSite tests passed.");
|
||||
}
|
||||
|
||||
// --- Route stack validation tests ---
|
||||
|
||||
function testRouteStackOrdering() {
|
||||
console.log("Running route stack ordering tests...");
|
||||
|
||||
const createTargetStack = [
|
||||
"verifyResourceAccess",
|
||||
"verifySiteAccess",
|
||||
"verifyLimits",
|
||||
"verifyUserHasAction",
|
||||
"logActionAudit",
|
||||
"createTarget"
|
||||
];
|
||||
|
||||
const updateTargetStack = [
|
||||
"verifyTargetAccess",
|
||||
"verifySiteAccess",
|
||||
"verifyLimits",
|
||||
"verifyUserHasAction",
|
||||
"logActionAudit",
|
||||
"updateTarget"
|
||||
];
|
||||
|
||||
// Verify verifySiteAccess comes after resource/target access middleware
|
||||
{
|
||||
const siteAccessIndex = createTargetStack.indexOf("verifySiteAccess");
|
||||
const resourceAccessIndex = createTargetStack.indexOf(
|
||||
"verifyResourceAccess"
|
||||
);
|
||||
assertEquals(
|
||||
siteAccessIndex > resourceAccessIndex,
|
||||
true,
|
||||
"verifySiteAccess must come after verifyResourceAccess in create target stack"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const siteAccessIndex = updateTargetStack.indexOf("verifySiteAccess");
|
||||
const targetAccessIndex =
|
||||
updateTargetStack.indexOf("verifyTargetAccess");
|
||||
assertEquals(
|
||||
siteAccessIndex > targetAccessIndex,
|
||||
true,
|
||||
"verifySiteAccess must come after verifyTargetAccess in update target stack"
|
||||
);
|
||||
}
|
||||
|
||||
// Verify verifySiteAccess comes before the handler
|
||||
{
|
||||
const siteAccessIndex = createTargetStack.indexOf("verifySiteAccess");
|
||||
const handlerIndex = createTargetStack.indexOf("createTarget");
|
||||
assertEquals(
|
||||
siteAccessIndex < handlerIndex,
|
||||
true,
|
||||
"verifySiteAccess must come before createTarget handler"
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const siteAccessIndex = updateTargetStack.indexOf("verifySiteAccess");
|
||||
const handlerIndex = updateTargetStack.indexOf("updateTarget");
|
||||
assertEquals(
|
||||
siteAccessIndex < handlerIndex,
|
||||
true,
|
||||
"verifySiteAccess must come before updateTarget handler"
|
||||
);
|
||||
}
|
||||
|
||||
console.log("All route stack ordering tests passed.");
|
||||
}
|
||||
|
||||
// --- Security scenario tests ---
|
||||
|
||||
function testSecurityScenarios() {
|
||||
console.log("Running security scenario tests...");
|
||||
|
||||
// Scenario 1: Attacker has resource access in org_attacker, but tries to
|
||||
// bind target to a site in org_victim.
|
||||
// verifyResourceAccess passes (sets req.userOrgId = "org_attacker").
|
||||
// verifySiteAccess loads site (org_victim), checks site.orgId !== req.userOrgId.
|
||||
// Expected: 403 Forbidden.
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite(
|
||||
"org_victim",
|
||||
"org_attacker"
|
||||
);
|
||||
assertEquals(
|
||||
shouldReject,
|
||||
true,
|
||||
"Scenario 1: Cross-org site binding must be rejected"
|
||||
);
|
||||
}
|
||||
|
||||
// Scenario 2: Attacker has resource access AND site access in another org.
|
||||
// Even though the user has site access, verifySiteAccess rejects because
|
||||
// the org-match check runs before the site access check.
|
||||
// Expected: 403 Forbidden (org mismatch caught before site access check).
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite(
|
||||
"org_victim",
|
||||
"org_attacker"
|
||||
);
|
||||
assertEquals(
|
||||
shouldReject,
|
||||
true,
|
||||
"Scenario 2: Cross-org site must be rejected even if user has site access"
|
||||
);
|
||||
}
|
||||
|
||||
// Scenario 3: Legitimate user creates target with site in same org.
|
||||
// verifyResourceAccess passes, verifySiteAccess org-match passes (same org),
|
||||
// verifySiteAccess site access passes.
|
||||
// Expected: 201 Created.
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite(
|
||||
"org_attacker",
|
||||
"org_attacker"
|
||||
);
|
||||
assertEquals(
|
||||
shouldReject,
|
||||
false,
|
||||
"Scenario 3: Same-org site must be allowed"
|
||||
);
|
||||
}
|
||||
|
||||
// Scenario 4: WireGuard site in victim org — org mismatch is caught before
|
||||
// any DB write, pickPort, addPeer, or addTargets side effect.
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite(
|
||||
"org_victim",
|
||||
"org_attacker"
|
||||
);
|
||||
assertEquals(
|
||||
shouldReject,
|
||||
true,
|
||||
"Scenario 4: WireGuard cross-org site must be rejected before addPeer"
|
||||
);
|
||||
}
|
||||
|
||||
// Scenario 5: Newt site in victim org — same as scenario 4 but for newt.
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite(
|
||||
"org_victim",
|
||||
"org_attacker"
|
||||
);
|
||||
assertEquals(
|
||||
shouldReject,
|
||||
true,
|
||||
"Scenario 5: Newt cross-org site must be rejected before addTargets"
|
||||
);
|
||||
}
|
||||
|
||||
// Scenario 6: Normal site-only route (e.g. PUT /site/:siteId) where
|
||||
// verifySiteAccess runs without a prior verifyResourceAccess.
|
||||
// req.userOrgId is undefined, so the org-match check is skipped.
|
||||
// Normal site access verification proceeds.
|
||||
{
|
||||
const shouldReject = shouldRejectCrossOrgSite("org_victim", undefined);
|
||||
assertEquals(
|
||||
shouldReject,
|
||||
false,
|
||||
"Scenario 6: Site-only routes should skip org-match check"
|
||||
);
|
||||
}
|
||||
|
||||
console.log("All security scenario tests passed.");
|
||||
}
|
||||
|
||||
// Run all tests
|
||||
testSiteOrgMatchLogic();
|
||||
testShouldRejectCrossOrgSite();
|
||||
testRouteStackOrdering();
|
||||
testSecurityScenarios();
|
||||
@@ -71,6 +71,15 @@ export async function verifySiteAccess(
|
||||
);
|
||||
}
|
||||
|
||||
if (req.userOrgId && site.orgId !== req.userOrgId) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
"User does not have access to this site"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!req.userOrg) {
|
||||
// Get user's role ID in the organization
|
||||
const userOrgRole = await db
|
||||
@@ -128,10 +137,7 @@ export async function verifySiteAccess(
|
||||
.where(
|
||||
and(
|
||||
eq(roleSites.siteId, site.siteId),
|
||||
inArray(
|
||||
roleSites.roleId,
|
||||
req.userOrgRoleIds!
|
||||
)
|
||||
inArray(roleSites.roleId, req.userOrgRoleIds!)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
|
||||
@@ -493,16 +493,29 @@ export async function getTraefikConfig(
|
||||
const transportName = `${key}-transport`;
|
||||
const headersMiddlewareName = `${key}-headers-middleware`;
|
||||
|
||||
logger.debug(
|
||||
`Processing resource ${resource.name} with domain ${fullDomain} and ${targets.length} targets`
|
||||
);
|
||||
|
||||
if (!resource.enabled) {
|
||||
logger.debug(
|
||||
`Resource ${resource.name} is disabled, skipping Traefik config`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resource.http) {
|
||||
if (resource.mode == "http") {
|
||||
if (!resource.domainId) {
|
||||
logger.debug(
|
||||
`Resource ${resource.name} does not have a domainId, skipping Traefik config`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!resource.fullDomain) {
|
||||
logger.debug(
|
||||
`Resource ${resource.name} does not have a fullDomain, skipping Traefik config`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -958,7 +971,7 @@ export async function getTraefikConfig(
|
||||
serviceName
|
||||
].loadBalancer.serversTransport = transportName;
|
||||
}
|
||||
} else {
|
||||
} else if (resource.mode == "tcp" || resource.mode == "udp") {
|
||||
// Non-HTTP (TCP/UDP) configuration
|
||||
if (!resource.enableProxy) {
|
||||
continue;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { randomInt } from "crypto";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
actionAuditLog,
|
||||
@@ -392,7 +393,7 @@ export async function signSshKey(
|
||||
if (existingUserWithSameName) {
|
||||
let foundUniqueUsername = false;
|
||||
for (let attempt = 0; attempt < 20; attempt++) {
|
||||
const randomNum = Math.floor(Math.random() * 101); // 0 to 100
|
||||
const randomNum = randomInt(0, 101); // 0 to 100
|
||||
const candidateUsername = `${usernameToUse}${randomNum}`;
|
||||
|
||||
const [existingUser] = await db
|
||||
|
||||
@@ -561,6 +561,7 @@ authenticated.delete(
|
||||
authenticated.put(
|
||||
"/resource/:resourceId/target",
|
||||
verifyResourceAccess,
|
||||
verifySiteAccess,
|
||||
verifyLimits,
|
||||
verifyUserHasAction(ActionsEnum.createTarget),
|
||||
logActionAudit(ActionsEnum.createTarget),
|
||||
@@ -612,6 +613,7 @@ authenticated.get(
|
||||
authenticated.post(
|
||||
"/target/:targetId",
|
||||
verifyTargetAccess,
|
||||
verifySiteAccess,
|
||||
verifyLimits,
|
||||
verifyUserHasAction(ActionsEnum.updateTarget),
|
||||
logActionAudit(ActionsEnum.updateTarget),
|
||||
@@ -1234,7 +1236,8 @@ export const authRouter = Router();
|
||||
unauthenticated.use("/auth", authRouter);
|
||||
authRouter.use(
|
||||
rateLimit({
|
||||
windowMs: config.getRawConfig().rate_limits.auth.window_minutes * 60 * 1000,
|
||||
windowMs:
|
||||
config.getRawConfig().rate_limits.auth.window_minutes * 60 * 1000,
|
||||
max: config.getRawConfig().rate_limits.auth.max_requests,
|
||||
keyGenerator: (req) =>
|
||||
`authRouterGlobal:${ipKeyGenerator(req.ip || "")}:${req.path}`,
|
||||
|
||||
@@ -103,7 +103,7 @@ export function ProxyResourceTargetsForm({
|
||||
// Notify parent of changes (create mode)
|
||||
useEffect(() => {
|
||||
onChange?.(targets);
|
||||
}, [targets]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [targets]);
|
||||
|
||||
// Poll health status only in edit mode
|
||||
const { data: polledTargets } = useQuery({
|
||||
|
||||
@@ -86,7 +86,7 @@ export default async function Page(props: {
|
||||
targetOrgId = lastOrgCookie;
|
||||
} else {
|
||||
let ownedOrg = orgs.find((org) => org.isOwner);
|
||||
let primaryOrg = orgs.find((org) => org.isPrimaryOrg);
|
||||
const primaryOrg = orgs.find((org) => org.isPrimaryOrg);
|
||||
if (!ownedOrg) {
|
||||
if (primaryOrg) {
|
||||
ownedOrg = primaryOrg;
|
||||
|
||||
@@ -16,9 +16,9 @@ export const metadata: Metadata = {
|
||||
export default async function MaintenanceScreen() {
|
||||
const t = await getTranslations();
|
||||
|
||||
let title = t("privateMaintenanceScreenTitle");
|
||||
let message = t("privateMaintenanceScreenMessage");
|
||||
let steps = t("privateMaintenanceScreenSteps");
|
||||
const title = t("privateMaintenanceScreenTitle");
|
||||
const message = t("privateMaintenanceScreenMessage");
|
||||
const steps = t("privateMaintenanceScreenSteps");
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center p-4">
|
||||
|
||||
@@ -17,7 +17,7 @@ export default async function RdpPage() {
|
||||
const hostname = host.split(":")[0];
|
||||
|
||||
let target: GetBrowserTargetResponse | null = null;
|
||||
let error: string | null = null;
|
||||
const error: string | null = null;
|
||||
|
||||
try {
|
||||
const res = await priv.get<AxiosResponse<GetBrowserTargetResponse>>(
|
||||
|
||||
@@ -180,7 +180,6 @@ export default function SshClient({
|
||||
certificate: signedKeyData.certificate
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
function connect(override?: ConnectCredentials) {
|
||||
|
||||
@@ -39,7 +39,6 @@ export default function VncClient({
|
||||
});
|
||||
|
||||
const [connected, setConnected] = useState(false);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const rfbRef = useRef<any>(null);
|
||||
const screenRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -59,7 +58,7 @@ export default function VncClient({
|
||||
// Clean up on unmount.
|
||||
useEffect(() => {
|
||||
return () => disconnect();
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const connect = async () => {
|
||||
if (!target) {
|
||||
@@ -115,7 +114,6 @@ export default function VncClient({
|
||||
options.credentials = { password: form.password };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const rfb: any = new RFB(screenRef.current, wsUrl, options);
|
||||
|
||||
rfb.scaleViewport = true;
|
||||
|
||||
@@ -17,7 +17,7 @@ export default async function VncPage() {
|
||||
const hostname = host.split(":")[0];
|
||||
|
||||
let target: GetBrowserTargetResponse | null = null;
|
||||
let error: string | null = null;
|
||||
const error: string | null = null;
|
||||
|
||||
try {
|
||||
const res = await priv.get<AxiosResponse<GetBrowserTargetResponse>>(
|
||||
|
||||
Reference in New Issue
Block a user