{"id":13499359,"url":"https://github.com/moul/sshportal","last_synced_at":"2025-05-14T07:08:29.477Z","repository":{"id":27458528,"uuid":"108840022","full_name":"moul/sshportal","owner":"moul","description":":tophat: simple, fun and transparent SSH (and telnet) bastion server","archived":false,"fork":false,"pushed_at":"2025-05-07T18:51:27.000Z","size":4888,"stargazers_count":1858,"open_issues_count":82,"forks_count":136,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-05-09T20:25:45.359Z","etag":null,"topics":["bastion","devops","fun","security","ssh","ssh-server"],"latest_commit_sha":null,"homepage":"https://manfred.life/sshportal","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moul.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["moul"],"patreon":"moul","open_collective":"moul","custom":["https://manfred.life/donate"]}},"created_at":"2017-10-30T11:13:36.000Z","updated_at":"2025-05-08T03:24:17.000Z","dependencies_parsed_at":"2024-01-17T18:57:35.778Z","dependency_job_id":"c038dc16-3939-4e26-a410-c37d0e0ab013","html_url":"https://github.com/moul/sshportal","commit_stats":{"total_commits":378,"total_committers":39,"mean_commits":9.692307692307692,"dds":0.4470899470899471,"last_synced_commit":"f9c8f60365e9861905a52b24c296e11dbddd1444"},"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moul%2Fsshportal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moul%2Fsshportal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moul%2Fsshportal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moul%2Fsshportal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moul","download_url":"https://codeload.github.com/moul/sshportal/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254084587,"owners_count":22011906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bastion","devops","fun","security","ssh","ssh-server"],"created_at":"2024-07-31T22:00:32.468Z","updated_at":"2025-05-14T07:08:29.450Z","avatar_url":"https://github.com/moul.png","language":"Go","readme":"# sshportal\n\n[![CircleCI](https://circleci.com/gh/moul/sshportal.svg?style=svg)](https://circleci.com/gh/moul/sshportal)\n[![Go Report Card](https://goreportcard.com/badge/moul.io/sshportal)](https://goreportcard.com/report/moul.io/sshportal)\n[![GoDoc](https://godoc.org/moul.io/sshportal?status.svg)](https://godoc.org/moul.io/sshportal)\n[![Financial Contributors on Open Collective](https://opencollective.com/sshportal/all/badge.svg?label=financial+contributors)](https://opencollective.com/sshportal) [![License](https://img.shields.io/github/license/moul/sshportal.svg)](https://github.com/moul/sshportal/blob/master/LICENSE)\n[![GitHub release](https://img.shields.io/github/release/moul/sshportal.svg)](https://github.com/moul/sshportal/releases)\n\u003c!-- temporarily broken? [![Docker Build Status](https://img.shields.io/docker/build/moul/sshportal.svg)](https://hub.docker.com/r/moul/sshportal/) --\u003e\n\nJump host/Jump server without the jump, a.k.a Transparent SSH bastion\n\n\u003cimg src=\"https://raw.githubusercontent.com/moul/sshportal/master/.assets/bastion.jpg\" width=\"50%\"\u003e\n\nFeatures include: independence of users and hosts, convenient user invite system, connecting to servers that don't support SSH keys, various levels of access, and many more. Easy to install, run and configure.\n\n![Flow Diagram](https://raw.githubusercontent.com/moul/sshportal/master/.assets/flow-diagram.png)\n\n---\n\n## Contents\n\n\u003c!-- toc --\u003e\n\n- [Installation and usage](#installation-and-usage)\n- [Use cases](#use-cases)\n- [Features and limitations](#features-and-limitations)\n- [Docker](#docker)\n- [Manual Install](#manual-install)\n- [Backup / Restore](#backup--restore)\n- [built-in shell](#built-in-shell)\n- [Demo data](#demo-data)\n- [Shell commands](#shell-commands)\n- [Healthcheck](#healthcheck)\n- [portal alias (.ssh/config)](#portal-alias-sshconfig)\n- [Scaling](#scaling)\n- [Under the hood](#under-the-hood)\n- [Testing](#testing)\n\n\u003c!-- tocstop --\u003e\n\n---\n\n## Installation and usage\n\nStart the server\n\n```console\n$ sshportal server\n2017/11/13 10:58:35 Admin user created, use the user 'invite:BpLnfgDsc2WD8F2q' to associate a public key with this account\n2017/11/13 10:58:35 SSH Server accepting connections on :2222\n```\n\nLink your SSH key with the admin account\n\n```console\n$ ssh localhost -p 2222 -l invite:BpLnfgDsc2WD8F2q\nWelcome admin!\n\nYour key is now associated with the user \"admin@sshportal\".\nShared connection to localhost closed.\n$\n```\n\nIf the association fails and you are prompted for a password, verify that the host you're connecting from has a SSH key set up or generate one with ```ssh-keygen -t rsa```\n\nDrop an interactive administrator shell\n\n```console\nssh localhost -p 2222 -l admin\n\n\n    __________ _____           __       __\n   / __/ __/ // / _ \\___  ____/ /____ _/ /\n  _\\ \\_\\ \\/ _  / ___/ _ \\/ __/ __/ _ '/ /\n /___/___/_//_/_/   \\___/_/  \\__/\\_,_/_/\n\n\nconfig\u003e\n```\n\nCreate your first host\n\n```console\nconfig\u003e host create bart@foo.example.org\n1\nconfig\u003e\n```\n\nList hosts\n\n```console\nconfig\u003e host ls\n  ID | NAME |           URL           |   KEY   | PASS | GROUPS  | COMMENT\n+----+------+-------------------------+---------+------+---------+---------+\n   1 | foo  | bart@foo.example.org:22 | default |      | default |\nTotal: 1 hosts.\nconfig\u003e\n```\n\nAdd the key to the server\n\n```console\n$ ssh bart@foo.example.org \"$(ssh localhost -p 2222 -l admin key setup default)\"\n$\n```\n\nProfit\n\n```console\nssh localhost -p 2222 -l foo\nbart@foo\u003e\n```\n\nInvite friends\n\n*This command doesn't create a user on the remote server, it only creates an account in the sshportal database.*\n\n```console\nconfig\u003e user invite bob@example.com\nUser 2 created.\nTo associate this account with a key, use the following SSH user: 'invite:NfHK5a84jjJkwzDk'.\nconfig\u003e\n```\n\nDemo gif:\n![sshportal demo](https://github.com/moul/sshportal/raw/master/.assets/demo.gif)\n\n---\n\n## Use cases\n\nUsed by educators to provide temporary access to students. [Feedback from a teacher](https://github.com/moul/sshportal/issues/64). The author is using it in one of his projects, *pathwar*, to dynamically configure hosts and users, so that he can give temporary accesses for educational purposes.\n\n*vptech*, the vente-privee.com technical team (a group of over 6000 people) is using it internally to manage access to servers/routers, saving hours on configuration management and not having to share the configuration information.\n\nThere are companies who use a jump host to monitor connections at a single point.\n\nA hosting company is using SSHportal for its “logging” feature, among others. As every session is logged and introspectable, they have a detailed history of who performed which action. This company made its own contribution to the project, allowing the support of [more than 65.000 sessions in the database](https://github.com/moul/sshportal/pull/76).\n\nThe project has also received [multiple contributions from a security researcher](https://github.com/moul/sshportal/pulls?q=is%3Apr+author%3Asabban+sort%3Aupdated-desc) that made a thesis on quantum cryptography. This person uses SSHportal in their security-hardened hosting company.\n\nIf you need to invite multiple people to an event (hackathon, course, etc), the day before the event you can create multiple accounts at once, print the invite, and distribute the paper.\n\n---\n\n## Features and limitations\n\n* Single autonomous binary (~10-20Mb) with no runtime dependencies (embeds ssh server and client)\n* Portable / Cross-platform (regularly tested on linux and OSX/darwin)\n* Store data in [Sqlite3](https://www.sqlite.org/) or [MySQL](https://www.mysql.com) (probably easy to add postgres, mssql thanks to gorm)\n* Stateless -\u003e horizontally scalable when using [MySQL](https://www.mysql.com) as the backend\n* Connect to remote host using key or password\n* Admin commands can be run directly or in an interactive shell\n* Host management\n* User management (invite, group, stats)\n* Host Key management (create, remove, update, import)\n* Automatic remote host key learning\n* User Key management (multiple keys per user)\n* ACL management (acl+user-groups+host-groups)\n* User roles (admin, trusted, standard, ...)\n* User invitations (no more \"give me your public ssh key please\")\n* Easy server installation (generate shell command to setup `authorized_keys`)\n* Sensitive data encryption\n* Session management (see active connections, history, stats, stop)\n* Audit log (logging every user action)\n* Record TTY Session (with [ttyrec](https://en.wikipedia.org/wiki/Ttyrec) format, use `ttyplay` for replay)\n* Tunnels logging\n* Host Keys verifications shared across users\n* Healthcheck user (replying OK to any user)\n* SSH compatibility\n  * ipv4 and ipv6 support\n  * [`scp`](https://linux.die.net/man/1/scp) support\n  * [`rsync`](https://linux.die.net/man/1/rsync) support\n  * [tunneling](https://www.ssh.com/ssh/tunneling/example) (local forward, remote forward, dynamic forward) support\n  * [`sftp`](https://www.ssh.com/ssh/sftp/) support\n  * [`ssh-agent`](https://www.ssh.com/ssh/agent) support\n  * [`X11 forwarding`](http://en.tldp.org/HOWTO/XDMCP-HOWTO/ssh.html) support\n  * Git support (can be used to easily use multiple user keys on GitHub, or access your own firewalled gitlab server)\n  * Do not require any SSH client modification or custom `.ssh/config`, works with every tested SSH programming libraries and every tested SSH clients\n* SSH to non-SSH proxy\n  * [Telnet](https://www.ssh.com/ssh/telnet) support\n\n**(Known) limitations**\n\n* Does not work (yet?) with [`mosh`](https://mosh.org/)\n* It is not possible for a user to access a host with the same name as the user. This is easily circumvented by changing the user name, especially since the most common use cases does not expose it.\n* It is not possible to access a host named `healthcheck` as this is a built-in command.\n\n---\n\n## Docker\n\nDocker is the recommended way to run sshportal.\n\nAn [automated build is setup on the Docker Hub](https://hub.docker.com/r/moul/sshportal/tags/).\n\n```console\n# Start a server in background\n#   mount `pwd` to persist the sqlite database file\ndocker run -p 2222:2222 -d --name=sshportal -v \"$(pwd):$(pwd)\" -w \"$(pwd)\" moul/sshportal:v1.10.0\n\n# check logs (mandatory on first run to get the administrator invite token)\ndocker logs -f sshportal\n```\n\nThe easier way to upgrade sshportal is to do the following:\n\n```sh\n# we consider you were using an old version and you want to use the new version v1.10.0\n\n# stop and rename the last working container + backup the database\ndocker stop sshportal\ndocker rename sshportal sshportal_old\ncp sshportal.db sshportal.db.bkp\n\n# run the new version\ndocker run -p 2222:2222 -d --name=sshportal -v \"$(pwd):$(pwd)\" -w \"$(pwd)\" moul/sshportal:v1.10.0\n# check the logs for migration or cross-version incompatibility errors\ndocker logs -f sshportal\n```\n\nNow you can test ssh-ing to sshportal to check if everything looks OK.\n\nIn case of problem, you can rollback to the latest working version with the latest working backup, using:\n\n```sh\ndocker stop sshportal\ndocker rm sshportal\ncp sshportal.db.bkp sshportal.db\ndocker rename sshportal_old sshportal\ndocker start sshportal\ndocker logs -f sshportal\n```\n\n---\n\n## Manual Install\n\nGet the latest version using GO.\n\n```sh\nGO111MODULE=on go get -u moul.io/sshportal\n```\n\n---\n\n## Backup / Restore\n\nsshportal embeds built-in backup/restore methods which basically import/export JSON objects:\n\n```sh\n# Backup\nssh portal config backup  \u003e sshportal.bkp\n\n# Restore\nssh portal config restore \u003c sshportal.bkp\n```\n\nThis method is particularly useful as it should be resistant against future DB schema changes (expected during development phase).\n\nI suggest you to be careful during this development phase, and use an additional backup method, for example:\n\n```sh\n# sqlite dump\nsqlite3 sshportal.db .dump \u003e sshportal.sql.bkp\n\n# or just the immortal cp\ncp sshportal.db sshportal.db.bkp\n```\n\n---\n\n## built-in shell\n\n`sshportal` embeds a configuration CLI.\n\nBy default, the configuration user is `admin`, (can be changed using `--config-user=\u003cvalue\u003e` when starting the server. The shell is also accessible through `ssh [username]@portal.example.org`.\n\nEach command can be run directly by using this syntax: `ssh admin@portal.example.org \u003ccommand\u003e [args]`:\n\n```\nssh admin@portal.example.org host inspect toto\n```\n\nYou can enter in interactive mode using this syntax: `ssh admin@portal.example.org`\n\n![sshportal overview](https://raw.github.com/moul/sshportal/master/.assets/overview.png)\n\n---\n\n## Demo data\n\nThe following servers are freely available, without external registration,\nit makes it easier to quickly test `sshportal` without configuring your own servers to accept sshportal connections.\n\n```\nssh portal host create new@sdf.org\nssh sdf@portal\n\nssh portal host create test@whoami.filippo.io\nssh whoami@portal\n\nssh portal host create test@chat.shazow.net\nssh chat@portal\n```\n\n---\n\n## Shell commands\n\n```sh\n# acl management\nacl help\nacl create [-h] [--hostgroup=HOSTGROUP...] [--usergroup=USERGROUP...] [--pattern=\u003cvalue\u003e] [--comment=\u003cvalue\u003e] [--action=\u003cvalue\u003e] [--weight=value]\nacl inspect [-h] ACL...\nacl ls [-h] [--latest] [--quiet]\nacl rm [-h] ACL...\nacl update [-h] [--comment=\u003cvalue\u003e] [--action=\u003cvalue\u003e] [--weight=\u003cvalue\u003e] [--assign-hostgroup=HOSTGROUP...] [--unassign-hostgroup=HOSTGROUP...] [--assign-usergroup=USERGROUP...] [--unassign-usergroup=USERGROUP...] ACL...\n\n# config management\nconfig help\nconfig backup [-h] [--indent] [--decrypt]\nconfig restore [-h] [--confirm] [--decrypt]\n\n# event management\nevent help\nevent ls [-h] [--latest] [--quiet]\nevent inspect [-h] EVENT...\n\n# host management\nhost help\nhost create [-h] [--name=\u003cvalue\u003e] [--password=\u003cvalue\u003e] [--comment=\u003cvalue\u003e] [--key=KEY] [--group=HOSTGROUP...] [--hop=HOST] [--logging=MODE] \u003cusername\u003e[:\u003cpassword\u003e]@\u003chost\u003e[:\u003cport\u003e]\nhost inspect [-h] [--decrypt] HOST...\nhost ls [-h] [--latest] [--quiet]\nhost rm [-h] HOST...\nhost update [-h] [--name=\u003cvalue\u003e] [--comment=\u003cvalue\u003e] [--key=KEY] [--assign-group=HOSTGROUP...] [--unassign-group=HOSTGROUP...] [--logging-MODE] [--set-hop=HOST] [--unset-hop] HOST...\n\n# hostgroup management\nhostgroup help\nhostgroup create [-h] [--name=\u003cvalue\u003e] [--comment=\u003cvalue\u003e]\nhostgroup inspect [-h] HOSTGROUP...\nhostgroup ls [-h] [--latest] [--quiet]\nhostgroup rm [-h] HOSTGROUP...\n\n# key management\nkey help\nkey create [-h] [--name=\u003cvalue\u003e] [--type=\u003cvalue\u003e] [--length=\u003cvalue\u003e] [--comment=\u003cvalue\u003e]\nkey import [-h] [--name=\u003cvalue\u003e] [--comment=\u003cvalue\u003e]\nkey inspect [-h] [--decrypt] KEY...\nkey ls [-h] [--latest] [--quiet]\nkey rm [-h] KEY...\nkey setup [-h] KEY\nkey show [-h] KEY\n\n# session management\nsession help\nsession ls [-h] [--latest] [--quiet]\nsession inspect [-h] SESSION...\n\n# user management\nuser help\nuser invite [-h] [--name=\u003cvalue\u003e] [--comment=\u003cvalue\u003e] [--group=USERGROUP...] \u003cemail\u003e\nuser inspect [-h] USER...\nuser ls [-h] [--latest] [--quiet]\nuser rm [-h] USER...\nuser update [-h] [--name=\u003cvalue\u003e] [--email=\u003cvalue\u003e] [--set-admin] [--unset-admin] [--assign-group=USERGROUP...] [--unassign-group=USERGROUP...] USER...\n\n# usergroup management\nusergroup help\nusergroup create [-h] [--name=\u003cvalue\u003e] [--comment=\u003cvalue\u003e]\nusergroup inspect [-h] USERGROUP...\nusergroup ls [-h] [--latest] [--quiet]\nusergroup rm [-h] USERGROUP...\n\n# other\nexit [-h]\nhelp, h\ninfo [-h]\nversion [-h]\n```\n\n---\n\n## Healthcheck\n\nBy default, `sshportal` will return `OK` to anyone sshing using the `healthcheck` user without checking for authentication.\n\n```console\n$ ssh healthcheck@sshportal\nOK\n$\n```\n\nthe `healtcheck` user can be changed using the `healthcheck-user` option.\n\n---\n\nAlternatively, you can run the built-in healthcheck helper (requiring no ssh client nor ssh key):\n\nUsage: `sshportal healthcheck [--addr=host:port] [--wait] [--quiet]\n\n```console\n$ sshportal healthcheck --addr=localhost:2222; echo $?\n$ 0\n```\n\n---\n\nWait for sshportal to be healthy, then connect\n\n```console\n$ sshportal healthcheck --wait \u0026\u0026 ssh sshportal -l admin\nconfig\u003e\n```\n\n---\n\n## portal alias (.ssh/config)\n\nEdit your `~/.ssh/config` file (create it first if needed)\n\n```ini\nHost portal\n  User      admin\n  Port      2222       # portal port\n  HostName  127.0.0.1  # portal hostname\n```\n\n```bash\n# you can now run a shell using this:\nssh portal\n# instead of this:\nssh localhost -p 2222 -l admin\n\n# or connect to hosts using this:\nssh hostname@portal\n# instead of this:\nssh localhost -p 2222 -l hostname\n```\n\n---\n\n## Scaling\n\n`sshportal` is stateless but relies on a database to store configuration and logs.\n\nBy default, `sshportal` uses a local [sqlite](https://www.sqlite.org/) database which isn't scalable by design.\n\nYou can run multiple instances of `sshportal` sharing the same [MySQL](https://www.mysql.com) database, using `sshportal --db-conn=user:pass@host/dbname?parseTime=true --db-driver=mysql`.\n\n![sshportal cluster with MySQL backend](https://raw.github.com/moul/sshportal/master/.assets/cluster-mysql.png)\n\nSee [examples/mysql](http://github.com/moul/sshportal/tree/master/examples/mysql).\n\n---\n\n## Under the hood\n\n* Docker first (used in dev, tests, by the CI and in production)\n* Backed by (see [dep graph](https://godoc.org/github.com/moul/sshportal?import-graph\u0026hide=2)):\n  * SSH\n    * https://github.com/gliderlabs/ssh: SSH server made easy (well-designed golang library to build SSH servers)\n    * https://godoc.org/golang.org/x/crypto/ssh: both client and server SSH protocol and helpers\n  * Database\n    * https://github.com/jinzhu/gorm/: SQL orm\n    * https://github.com/go-gormigrate/gormigrate: Database migration system\n  * Built-in shell\n    * https://github.com/olekukonko/tablewriter: Ascii tables\n    * https://github.com/asaskevich/govalidator: Valide user inputs\n    * https://github.com/dustin/go-humanize: Human-friendly representation of technical data (time ago, bytes, ...)\n    * https://github.com/mgutz/ansi: Terminal color helpers\n    * https://github.com/urfave/cli: CLI flag parsing with subcommands support\n\n![sshportal data model](https://raw.github.com/moul/sshportal/master/.assets/sql-schema.png)\n\n---\n\n## Testing\n\n[Install golangci-lint](https://golangci-lint.run/usage/install/#local-installation) and run this in project root: \n```\ngolangci-lint run\n```\n---\nPerform integration tests\n```\nmake integration\n```\n---\nPerform unit tests\n```\nmake unittest\n```\n---\n\n## Contributors\n\n### Code Contributors\n\nThis project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].\n\u003ca href=\"https://github.com/moul/sshportal/graphs/contributors\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/contributors.svg?width=890\u0026button=false\" /\u003e\u003c/a\u003e\n\n### Financial Contributors\n\nBecome a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/sshportal/contribute)]\n\n#### Individuals\n\n\u003ca href=\"https://opencollective.com/sshportal\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/individuals.svg?width=890\"\u003e\u003c/a\u003e\n\n#### Organizations\n\nSupport this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/sshportal/contribute)]\n\n\u003ca href=\"https://opencollective.com/sshportal/organization/0/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/0/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/sshportal/organization/1/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/1/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/sshportal/organization/2/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/2/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/sshportal/organization/3/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/3/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/sshportal/organization/4/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/4/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/sshportal/organization/5/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/5/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/sshportal/organization/6/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/6/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/sshportal/organization/7/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/7/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/sshportal/organization/8/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/8/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/sshportal/organization/9/website\"\u003e\u003cimg src=\"https://opencollective.com/sshportal/organization/9/avatar.svg\"\u003e\u003c/a\u003e\n\n### Stargazers over time\n\n[![Stargazers over time](https://starchart.cc/moul/sshportal.svg)](https://starchart.cc/moul/sshportal)\n","funding_links":["https://github.com/sponsors/moul","https://patreon.com/moul","https://opencollective.com/moul","https://manfred.life/donate","https://opencollective.com/sshportal","https://opencollective.com/sshportal/contribute","https://opencollective.com/sshportal/organization/0/website","https://opencollective.com/sshportal/organization/1/website","https://opencollective.com/sshportal/organization/2/website","https://opencollective.com/sshportal/organization/3/website","https://opencollective.com/sshportal/organization/4/website","https://opencollective.com/sshportal/organization/5/website","https://opencollective.com/sshportal/organization/6/website","https://opencollective.com/sshportal/organization/7/website","https://opencollective.com/sshportal/organization/8/website","https://opencollective.com/sshportal/organization/9/website"],"categories":["Go","Bastion Host","Apps","HarmonyOS","security","devops"],"sub_categories":["Servers","Windows Manager"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoul%2Fsshportal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoul%2Fsshportal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoul%2Fsshportal/lists"}