diff --git a/README.md b/README.md index 81dcfb9f..61e1a689 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 14da644b..d75a047c 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 7d2ef642..8073e166 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 8a790a2e..87cebf7d 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 8991a7e4..ffb6bf40 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 5e973273..612a1790 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 33c54313..a30bab12 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() )} ))}