diff --git a/install/fs/docker-compose.yml b/install/fs/docker-compose.yml index 2adcee58..ab6528d0 100644 --- a/install/fs/docker-compose.yml +++ b/install/fs/docker-compose.yml @@ -11,6 +11,7 @@ services: timeout: "3s" retries: 5 +{{if .InstallGerbil}} gerbil: image: fosrl/gerbil:{{.GerbilVersion}} container_name: gerbil @@ -32,12 +33,20 @@ services: - 51820:51820/udp - 443:443 # Port for traefik because of the network_mode - 80:80 # Port for traefik because of the network_mode +{{end}} traefik: image: traefik:v3.1 container_name: traefik restart: unless-stopped +{{if .InstallGerbil}} network_mode: service:gerbil # Ports appear on the gerbil service +{{end}} +{{if not .InstallGerbil}} + ports: + - 443:443 + - 80:80 +{{end}} depends_on: pangolin: condition: service_healthy diff --git a/install/main.go b/install/main.go index 486137a1..2c479dd3 100644 --- a/install/main.go +++ b/install/main.go @@ -41,6 +41,7 @@ type Config struct { EmailSMTPUser string EmailSMTPPass string EmailNoReply string + InstallGerbil bool } func main() { @@ -64,7 +65,7 @@ func main() { } if !isDockerInstalled() && runtime.GOOS == "linux" { - if shouldInstallDocker() { + if readBool(reader, "Docker is not installed. Would you like to install it?", true) { installDocker() } } @@ -140,6 +141,7 @@ func collectUserInput(reader *bufio.Reader) Config { config.BaseDomain = readString(reader, "Enter your base domain (no subdomain e.g. example.com)", "") config.DashboardDomain = readString(reader, "Enter the domain for the Pangolin dashboard", "pangolin."+config.BaseDomain) config.LetsEncryptEmail = readString(reader, "Enter email for Let's Encrypt certificates", "") + config.InstallGerbil = readBool(reader, "Do you want to use Gerbil to allow tunned connections", true) // Admin user configuration fmt.Println("\n=== Admin User Configuration ===") @@ -340,13 +342,6 @@ func createConfigFiles(config Config) error { return nil } -func shouldInstallDocker() bool { - reader := bufio.NewReader(os.Stdin) - fmt.Print("Would you like to install Docker? (yes/no): ") - response, _ := reader.ReadString('\n') - return strings.ToLower(strings.TrimSpace(response)) == "yes" -} - func installDocker() error { // Detect Linux distribution cmd := exec.Command("cat", "/etc/os-release") diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index e7ae3aca..1376ab0a 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -123,88 +123,100 @@ export async function createTarget( ); } - // make sure the target is within the site subnet - if ( - site.type == "wireguard" && - !isIpInCidr(targetData.ip, site.subnet!) - ) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - `Target IP is not within the site subnet` - ) - ); - } - - // Fetch resources for this site - const resourcesRes = await db.query.resources.findMany({ - where: eq(resources.siteId, site.siteId) - }); - - // TODO: is this all inefficient? - // Fetch targets for all resources of this site - let targetIps: string[] = []; - let targetInternalPorts: number[] = []; - await Promise.all( - resourcesRes.map(async (resource) => { - const targetsRes = await db.query.targets.findMany({ - where: eq(targets.resourceId, resource.resourceId) - }); - targetsRes.forEach((target) => { - targetIps.push(`${target.ip}/32`); - if (target.internalPort) { - targetInternalPorts.push(target.internalPort); - } - }); - }) - ); - - let internalPort!: number; - // pick a port - for (let i = 40000; i < 65535; i++) { - if (!targetInternalPorts.includes(i)) { - internalPort = i; - break; + let newTarget: Target[] = []; + if (site.type == "local") { + newTarget = await db + .insert(targets) + .values({ + resourceId, + protocol: "tcp", // hard code for now + ...targetData + }) + .returning(); + } else { + // make sure the target is within the site subnet + if ( + site.type == "wireguard" && + !isIpInCidr(targetData.ip, site.subnet!) + ) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + `Target IP is not within the site subnet` + ) + ); } - } - if (!internalPort) { - return next( - createHttpError( - HttpCode.BAD_REQUEST, - `No available internal port` - ) + // Fetch resources for this site + const resourcesRes = await db.query.resources.findMany({ + where: eq(resources.siteId, site.siteId) + }); + + // TODO: is this all inefficient? + // Fetch targets for all resources of this site + let targetIps: string[] = []; + let targetInternalPorts: number[] = []; + await Promise.all( + resourcesRes.map(async (resource) => { + const targetsRes = await db.query.targets.findMany({ + where: eq(targets.resourceId, resource.resourceId) + }); + targetsRes.forEach((target) => { + targetIps.push(`${target.ip}/32`); + if (target.internalPort) { + targetInternalPorts.push(target.internalPort); + } + }); + }) ); - } - const newTarget = await db - .insert(targets) - .values({ - resourceId, - protocol: "tcp", // hard code for now - internalPort, - ...targetData - }) - .returning(); + let internalPort!: number; + // pick a port + for (let i = 40000; i < 65535; i++) { + if (!targetInternalPorts.includes(i)) { + internalPort = i; + break; + } + } - // add the new target to the targetIps array - targetIps.push(`${targetData.ip}/32`); + if (!internalPort) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + `No available internal port` + ) + ); + } - if (site.pubKey) { - if (site.type == "wireguard") { - await addPeer(site.exitNodeId!, { - publicKey: site.pubKey, - allowedIps: targetIps.flat() - }); - } else if (site.type == "newt") { - // get the newt on the site by querying the newt table for siteId - const [newt] = await db - .select() - .from(newts) - .where(eq(newts.siteId, site.siteId)) - .limit(1); + newTarget = await db + .insert(targets) + .values({ + resourceId, + protocol: "tcp", // hard code for now + internalPort, + ...targetData + }) + .returning(); - addTargets(newt.newtId, newTarget); + // add the new target to the targetIps array + targetIps.push(`${targetData.ip}/32`); + + if (site.pubKey) { + if (site.type == "wireguard") { + await addPeer(site.exitNodeId!, { + publicKey: site.pubKey, + allowedIps: targetIps.flat() + }); + } else if (site.type == "newt") { + // get the newt on the site by querying the newt table for siteId + const [newt] = await db + .select() + .from(newts) + .where(eq(newts.siteId, site.siteId)) + .limit(1); + + addTargets(newt.newtId, newTarget); + } } } diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 829a3a93..e51f8872 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -151,6 +151,16 @@ export async function traefikConfigProvider( ], }, }; + } else if (site.type === "local") { + http.services![serviceName] = { + loadBalancer: { + servers: [ + { + url: `${target.method}://${target.ip}:${target.port}`, + }, + ], + }, + }; } }