Tunneled Mesh Reverse Proxy Server with Access Control
+
Tunneled Reverse Proxy Server with Access Control
_Your own self-hosted zero trust tunnel._
@@ -96,19 +96,19 @@ _Resources page of Pangolin dashboard (dark mode) showing multiple resources ava
- Deploy the Docker Compose stack onto a VPS hosted on a cloud platform like RackNerd, Amazon EC2, DigitalOcean Droplet, or similar. There are many cheap VPS hosting options available to suit your needs.
> [!TIP]
-> Many of our users have had a great experience with [RackNerd](https://my.racknerd.com/aff.php?aff=13788). Depending on promotions, you can likely get a **VPS with 1 vCPU, 1GB RAM, and ~20GB SSD for just around $12/year**. That's a great deal!
+> Many of our users have had a great experience with [RackNerd](https://my.racknerd.com/aff.php?aff=13788). Depending on promotions, you can get a [**VPS with 1 vCPU, 1GB RAM, and ~20GB SSD for just around $12/year**](https://my.racknerd.com/aff.php?aff=13788&pid=912). That's a great deal!
> We are part of the [RackNerd](https://my.racknerd.com/aff.php?aff=13788) affiliate program, so if you purchase through [our link](https://my.racknerd.com/aff.php?aff=13788), we receive a small commission which helps us maintain the project and keep it free for everyone.
-2. **Domain Configuration**:
+1. **Domain Configuration**:
- Point your domain name to the VPS and configure Pangolin with your preferred settings.
-3. **Connect Private Sites**:
+2. **Connect Private Sites**:
- Install Newt or use another WireGuard client on private sites.
- Automatically establish a connection from these sites to the central server.
-4. **Expose Resources**:
+3. **Expose Resources**:
- Add resources to the central server and configure access control rules.
- Access these resources securely from anywhere.
diff --git a/install/main.go b/install/main.go
index a0d74a43..9f235e71 100644
--- a/install/main.go
+++ b/install/main.go
@@ -17,6 +17,7 @@ import (
"time"
"unicode"
"math/rand"
+ "strconv"
"golang.org/x/term"
)
@@ -397,7 +398,7 @@ func installDocker() error {
return fmt.Errorf("failed to detect Linux distribution: %v", err)
}
osRelease := string(output)
-
+
// Detect system architecture
archCmd := exec.Command("uname", "-m")
archOutput, err := archCmd.Output()
@@ -405,7 +406,7 @@ func installDocker() error {
return fmt.Errorf("failed to detect system architecture: %v", err)
}
arch := strings.TrimSpace(string(archOutput))
-
+
// Map architecture to Docker's architecture naming
var dockerArch string
switch arch {
@@ -438,11 +439,31 @@ func installDocker() error {
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
`, dockerArch))
case strings.Contains(osRelease, "ID=fedora"):
- installCmd = exec.Command("bash", "-c", `
+ // Detect Fedora version to handle DNF 5 changes
+ versionCmd := exec.Command("bash", "-c", "grep VERSION_ID /etc/os-release | cut -d'=' -f2 | tr -d '\"'")
+ versionOutput, err := versionCmd.Output()
+ var fedoraVersion int
+ if err == nil {
+ if v, parseErr := strconv.Atoi(strings.TrimSpace(string(versionOutput))); parseErr == nil {
+ fedoraVersion = v
+ }
+ }
+
+ // Use appropriate DNF syntax based on version
+ var repoCmd string
+ if fedoraVersion >= 42 {
+ // DNF 5 syntax for Fedora 42+
+ repoCmd = "dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo"
+ } else {
+ // DNF 4 syntax for Fedora < 42
+ repoCmd = "dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo"
+ }
+
+ installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
dnf -y install dnf-plugins-core &&
- dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo &&
+ %s &&
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
- `)
+ `, repoCmd))
case strings.Contains(osRelease, "ID=opensuse") || strings.Contains(osRelease, "ID=\"opensuse-"):
installCmd = exec.Command("bash", "-c", `
zypper install -y docker docker-compose &&
@@ -466,6 +487,7 @@ func installDocker() error {
default:
return fmt.Errorf("unsupported Linux distribution")
}
+
installCmd.Stdout = os.Stdout
installCmd.Stderr = os.Stderr
return installCmd.Run()
diff --git a/public/logo/pangolin_black.svg b/public/logo/pangolin_black.svg
index fd2b02ac..89f5a622 100644
--- a/public/logo/pangolin_black.svg
+++ b/public/logo/pangolin_black.svg
@@ -1,22 +1,21 @@
+
+
+ inkscape:document-units="mm"
+ showgrid="false" />
+
+
+
+
+
diff --git a/public/logo/pangolin_orange.svg b/public/logo/pangolin_orange.svg
index a8823c9d..5e81a57f 100644
--- a/public/logo/pangolin_orange.svg
+++ b/public/logo/pangolin_orange.svg
@@ -1,39 +1,22 @@
+
+
+ xmlns:svg="http://www.w3.org/2000/svg">
+
+
+
+
+
diff --git a/public/logo/pangolin_orange_192x192.png b/public/logo/pangolin_orange_192x192.png
index 52e8659b..33fbf7b0 100644
Binary files a/public/logo/pangolin_orange_192x192.png and b/public/logo/pangolin_orange_192x192.png differ
diff --git a/public/logo/pangolin_orange_512x512.png b/public/logo/pangolin_orange_512x512.png
index 21f27644..ceed7e55 100644
Binary files a/public/logo/pangolin_orange_512x512.png and b/public/logo/pangolin_orange_512x512.png differ
diff --git a/public/logo/pangolin_orange_96x96.png b/public/logo/pangolin_orange_96x96.png
index 6d3821c2..76f23b9d 100644
Binary files a/public/logo/pangolin_orange_96x96.png and b/public/logo/pangolin_orange_96x96.png differ
diff --git a/public/logo/pangolin_profile_picture.png b/public/logo/pangolin_profile_picture.png
new file mode 100644
index 00000000..20c5f72b
Binary files /dev/null and b/public/logo/pangolin_profile_picture.png differ
diff --git a/public/logo/word_mark.png b/public/logo/word_mark.png
index d75a047c..27944d9c 100644
Binary files a/public/logo/word_mark.png and b/public/logo/word_mark.png differ
diff --git a/public/logo/word_mark_black.png b/public/logo/word_mark_black.png
new file mode 100644
index 00000000..ba6fb84e
Binary files /dev/null and b/public/logo/word_mark_black.png differ
diff --git a/public/logo/word_mark_white.png b/public/logo/word_mark_white.png
new file mode 100644
index 00000000..fb7a252d
Binary files /dev/null and b/public/logo/word_mark_white.png differ
diff --git a/src/app/auth/login/DashboardLoginForm.tsx b/src/app/auth/login/DashboardLoginForm.tsx
index b15dd518..0691b343 100644
--- a/src/app/auth/login/DashboardLoginForm.tsx
+++ b/src/app/auth/login/DashboardLoginForm.tsx
@@ -45,8 +45,8 @@ export default function DashboardLoginForm({
diff --git a/src/app/favicon.ico b/src/app/favicon.ico
index 0ffb1c54..1e7b36cd 100644
Binary files a/src/app/favicon.ico and b/src/app/favicon.ico differ
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
index e0925525..d8078f58 100644
--- a/src/components/Layout.tsx
+++ b/src/components/Layout.tsx
@@ -1,6 +1,6 @@
"use client";
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
import { SidebarNav } from "@app/components/SidebarNav";
import { OrgSelector } from "@app/components/OrgSelector";
import { cn } from "@app/lib/cn";
@@ -23,6 +23,7 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import { useUserContext } from "@app/hooks/useUserContext";
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
+import { useTheme } from "next-themes";
interface LayoutProps {
children: React.ReactNode;
@@ -61,6 +62,30 @@ export function Layout({
const { user } = useUserContext();
const { isUnlocked } = useLicenseStatusContext();
+ const { theme } = useTheme();
+ const [path, setPath] = useState(""); // Default logo path
+
+ useEffect(() => {
+ function getPath() {
+ let lightOrDark = theme;
+
+ if (theme === "system" || !theme) {
+ lightOrDark = window.matchMedia("(prefers-color-scheme: dark)")
+ .matches
+ ? "dark"
+ : "light";
+ }
+
+ if (lightOrDark === "light") {
+ return "/logo/word_mark_black.png";
+ }
+
+ return "/logo/word_mark_white.png";
+ }
+
+ setPath(getPath());
+ }, [theme, env]);
+
return (