diff --git a/README.md b/README.md index 81dcfb9fb..61e1a6894 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,13 @@

Tunneled Mesh Reverse Proxy Server with Access Control

+
-Pangolin is a self-hosted tunneled reverse proxy management server with identity and access control, designed to securely expose private resources on distributed networks. With Pangolin, you retain full control over your infrastructure while providing a user-friendly and feature-rich solution for managing proxies, authentication, and access, while simplifying complex network setups. +_Your own self-hosted zero trust tunnel._ + +
+ +Pangolin is a self-hosted tunneled reverse proxy server with identity and access control, designed to securely expose private resources on distributed networks. Acting as a central hub, it connects isolated networks — even those behind restrictive firewalls — through encrypted tunnels, enabling easy access to remote services without opening ports. Preview @@ -38,12 +43,13 @@ _Sites page of Pangolin dashboard (dark mode) showing multiple tunnels connected - Built-in support for any WireGuard client. - Automated **SSL certificates** (https) via [LetsEncrypt](https://letsencrypt.org/). - Support for HTTP/HTTPS and **raw TCP/UDP services**. +- Load balancing. ### Identity & Access Management - Centralized authentication system using platform SSO. **Users will only have to manage one login.** -- **Rules based access control for resources.** -- Totp with backup codes for two-factor authentication. +- **Define access control rules for IPs, IP ranges, and URL paths per resource.** +- TOTP with backup codes for two-factor authentication. - Create organizations, each with multiple sites, users, and roles. - **Role-based access control** to manage resource access permissions. - Additional authentication options include: @@ -61,9 +67,9 @@ _Sites page of Pangolin dashboard (dark mode) showing multiple tunnels connected ### Easy Deployment +- Run on any cloud provider or on-premises. - Docker Compose based setup for simplified deployment. - Future-proof installation script for streamlined setup and feature additions. -- Run on any VPS. - Use your preferred WireGuard client to connect, or use Newt, our custom user space client for the best experience. ### Modular Design @@ -126,17 +132,18 @@ _Sites page of Pangolin dashboard (dark mode) showing multiple tunnels connected ## Similar Projects and Inspirations -Pangolin was inspired by several existing projects and concepts: - -- **Cloudflare Tunnels**: +**Cloudflare Tunnels**: A similar approach to proxying private resources securely, but Pangolin is a self-hosted alternative, giving you full control over your infrastructure. -- **Authentik and Authelia**: +**Authentik and Authelia**: These projects inspired Pangolin’s centralized authentication system for proxies, enabling robust user and role management. ## Project Development / Roadmap -Pangolin is under active development, and we are continuously adding new features and improvements. View the [project board](https://github.com/orgs/fosrl/projects/1) for more detailed info. +> [!NOTE] +> Pangolin is under heavy development. The roadmap is subject to change as we fix bugs, add new features, and make improvements. + +View the [project board](https://github.com/orgs/fosrl/projects/1) for more detailed info. ## Licensing diff --git a/public/logo/word_mark.png b/public/logo/word_mark.png index 14da644ba..d75a047c1 100644 Binary files a/public/logo/word_mark.png and b/public/logo/word_mark.png differ diff --git a/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx b/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx index 7d2ef6426..8073e166c 100644 --- a/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx +++ b/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx @@ -9,7 +9,7 @@ import { SortingState, getSortedRowModel, ColumnFiltersState, - getFilteredRowModel, + getFilteredRowModel } from "@tanstack/react-table"; import { Table, @@ -18,7 +18,7 @@ import { TableContainer, TableHead, TableHeader, - TableRow, + TableRow } from "@/components/ui/table"; import { Button } from "@app/components/ui/button"; import { useState } from "react"; @@ -35,7 +35,7 @@ interface DataTableProps { export function RolesDataTable({ addRole, columns, - data, + data }: DataTableProps) { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); @@ -50,13 +50,15 @@ export function RolesDataTable({ onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), initialState: { - sorting, - columnFilters, pagination: { pageSize: 20, - pageIndex: 0, - }, + pageIndex: 0 + } }, + state: { + sorting, + columnFilters + } }); return ( @@ -102,7 +104,7 @@ export function RolesDataTable({ : flexRender( header.column.columnDef .header, - header.getContext(), + header.getContext() )} ); @@ -123,7 +125,7 @@ export function RolesDataTable({ {flexRender( cell.column.columnDef.cell, - cell.getContext(), + cell.getContext() )} ))} diff --git a/src/app/[orgId]/settings/access/users/UsersDataTable.tsx b/src/app/[orgId]/settings/access/users/UsersDataTable.tsx index 8a790a2e9..87cebf7de 100644 --- a/src/app/[orgId]/settings/access/users/UsersDataTable.tsx +++ b/src/app/[orgId]/settings/access/users/UsersDataTable.tsx @@ -9,7 +9,7 @@ import { SortingState, getSortedRowModel, ColumnFiltersState, - getFilteredRowModel, + getFilteredRowModel } from "@tanstack/react-table"; import { Table, @@ -18,7 +18,7 @@ import { TableContainer, TableHead, TableHeader, - TableRow, + TableRow } from "@/components/ui/table"; import { Button } from "@app/components/ui/button"; import { useState } from "react"; @@ -35,7 +35,7 @@ interface DataTableProps { export function UsersDataTable({ inviteUser, columns, - data, + data }: DataTableProps) { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); @@ -50,13 +50,15 @@ export function UsersDataTable({ onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), initialState: { - sorting, - columnFilters, pagination: { pageSize: 20, - pageIndex: 0, - }, + pageIndex: 0 + } }, + state: { + sorting, + columnFilters + } }); return ( @@ -102,7 +104,7 @@ export function UsersDataTable({ : flexRender( header.column.columnDef .header, - header.getContext(), + header.getContext() )} ); @@ -123,7 +125,7 @@ export function UsersDataTable({ {flexRender( cell.column.columnDef.cell, - cell.getContext(), + cell.getContext() )} ))} diff --git a/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx b/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx index 8991a7e43..ffb6bf405 100644 --- a/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx +++ b/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx @@ -9,7 +9,7 @@ import { SortingState, getSortedRowModel, ColumnFiltersState, - getFilteredRowModel, + getFilteredRowModel } from "@tanstack/react-table"; import { @@ -19,7 +19,7 @@ import { TableContainer, TableHead, TableHeader, - TableRow, + TableRow } from "@/components/ui/table"; import { Button } from "@app/components/ui/button"; import { useState } from "react"; @@ -36,7 +36,7 @@ interface ResourcesDataTableProps { export function ResourcesDataTable({ addResource, columns, - data, + data }: ResourcesDataTableProps) { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); @@ -51,13 +51,15 @@ export function ResourcesDataTable({ onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), initialState: { - sorting, - columnFilters, pagination: { pageSize: 20, - pageIndex: 0, - }, + pageIndex: 0 + } }, + state: { + sorting, + columnFilters + } }); return ( @@ -103,7 +105,7 @@ export function ResourcesDataTable({ : flexRender( header.column.columnDef .header, - header.getContext(), + header.getContext() )} ); @@ -124,7 +126,7 @@ export function ResourcesDataTable({ {flexRender( cell.column.columnDef.cell, - cell.getContext(), + cell.getContext() )} ))} diff --git a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx index 5e973273e..612a17903 100644 --- a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx +++ b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx @@ -51,12 +51,14 @@ export function ShareLinksDataTable({ onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), initialState: { - sorting, - columnFilters, pagination: { pageSize: 20, pageIndex: 0 } + }, + state: { + sorting, + columnFilters } }); diff --git a/src/app/[orgId]/settings/sites/SitesDataTable.tsx b/src/app/[orgId]/settings/sites/SitesDataTable.tsx index 33c543136..a30bab129 100644 --- a/src/app/[orgId]/settings/sites/SitesDataTable.tsx +++ b/src/app/[orgId]/settings/sites/SitesDataTable.tsx @@ -9,7 +9,7 @@ import { SortingState, getSortedRowModel, ColumnFiltersState, - getFilteredRowModel, + getFilteredRowModel } from "@tanstack/react-table"; import { @@ -19,7 +19,7 @@ import { TableContainer, TableHead, TableHeader, - TableRow, + TableRow } from "@/components/ui/table"; import { Button } from "@app/components/ui/button"; import { useState } from "react"; @@ -36,7 +36,7 @@ interface DataTableProps { export function SitesDataTable({ addSite, columns, - data, + data }: DataTableProps) { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); @@ -51,13 +51,15 @@ export function SitesDataTable({ onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), initialState: { - sorting, - columnFilters, pagination: { - pageSize: 100, - pageIndex: 0, - }, + pageSize: 20, + pageIndex: 0 + } }, + state: { + sorting, + columnFilters + } }); return ( @@ -103,7 +105,7 @@ export function SitesDataTable({ : flexRender( header.column.columnDef .header, - header.getContext(), + header.getContext() )} ); @@ -124,7 +126,7 @@ export function SitesDataTable({ {flexRender( cell.column.columnDef.cell, - cell.getContext(), + cell.getContext() )} ))}