{"id":18544742,"url":"https://github.com/131/mas4h","last_synced_at":"2026-02-28T13:09:02.347Z","repository":{"id":48531246,"uuid":"33628214","full_name":"131/mas4h","owner":"131","description":"Massive ssh bouncing server","archived":false,"fork":false,"pushed_at":"2024-06-04T00:08:14.000Z","size":94,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-04T04:44:47.574Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/131.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-04-08T19:40:36.000Z","updated_at":"2024-06-04T00:08:15.000Z","dependencies_parsed_at":"2025-04-10T12:08:37.171Z","dependency_job_id":"80ce47d7-fb09-4460-aa81-bb6563dd3486","html_url":"https://github.com/131/mas4h","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/131/mas4h","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/131%2Fmas4h","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/131%2Fmas4h/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/131%2Fmas4h/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/131%2Fmas4h/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/131","download_url":"https://codeload.github.com/131/mas4h/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/131%2Fmas4h/sbom","scorecard":{"id":1421,"data":{"date":"2025-08-11","repo":{"name":"github.com/131/mas4h","commit":"32b66020664347824d53f89180f85ef41fb970b9"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.1,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/131/mas4h/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/131/mas4h/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:7: update your workflow using https://app.stepsecurity.io/secureworkflow/131/mas4h/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/131/mas4h/test.yml/master?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/publish.yml:17","Warn: npmCommand not pinned by hash: .github/workflows/test.yml:12","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/publish.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 1/22 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":-1,"reason":"internal error: internal error: Client.Checks.ListCheckRunsForRef: error during graphqlHandler.setupCheckRuns: non-200 OK status code: 502 Bad Gateway body: \"\u003chtml\u003e\\r\\n\u003chead\u003e\u003ctitle\u003e502 Bad Gateway\u003c/title\u003e\u003c/head\u003e\\r\\n\u003cbody\u003e\\r\\n\u003ccenter\u003e\u003ch1\u003e502 Bad Gateway\u003c/h1\u003e\u003c/center\u003e\\r\\n\u003chr\u003e\u003ccenter\u003enginx\u003c/center\u003e\\r\\n\u003c/body\u003e\\r\\n\u003c/html\u003e\\r\\n\"","details":null,"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-14T12:37:12.548Z","repository_id":48531246,"created_at":"2025-08-14T12:37:12.548Z","updated_at":"2025-08-14T12:37:12.548Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29935036,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T13:00:17.143Z","status":"ssl_error","status_checked_at":"2026-02-28T12:59:13.669Z","response_time":90,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-11-06T20:17:27.618Z","updated_at":"2026-02-28T13:09:02.307Z","avatar_url":"https://github.com/131.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mas4h : Mass SSH revert bouncer\r\n\r\n[mas4sh](https://github.com/131/mas4sh) is a scalable server infrastructure to connect (a lot of) remote linux (i.e. SSH capable) devices to a central server (\u0026 boucing clusters) using reverse SSH tunnels. The ssh2 crazy magic is powered by the excellent [ssh2 library](https://github.com/mscdex/ssh2) by Brian White. mas4h (\u0026 dependencies) requires ES7 capabilities.\r\n\r\n\r\n[![Build Status](https://github.com/131/mas4h/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/131/mas4h/actions/workflows/test.yml)\r\n[![Version](https://img.shields.io/npm/v/mas4h.svg)](https://www.npmjs.com/package/mas4h)\r\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)\r\n\r\n\r\n# mas4h master \u0026 slaves\r\n\r\nmas4h master act as a dispatcher.\r\nRemote devices (clients) ask the master (via WS, REST call, whatever suits you) for a connexion slot.\r\nThe master will answer with the public IP of the less busy slave.\r\n\r\nPer design, it is possible for a client to connect and register reverse port forwarding on a slave (if you know it's public IP) regardless of the master dispatch.\r\n\r\nOnce a reverse port has been registered on a slave, the master keep tracks of the overall cluster status. The master expose a REST API to retrieve the cluster status \r\n\r\n\r\n# Minimal setup\r\n* a master instance \u0026 and at least a slave instance\r\n* It is possible to run the slave and the master on the same host.\r\n* It is also possible to run multiple slaves on the same host\r\n\r\n\r\n## Master overrides\r\nOn the master side, you'll have to implement how to lookup devices RSA public keys (and decide to accept them or not)\r\n```\r\n/**\r\n*  chain : function(err, device_details)\r\n*     device_details : {device_key : SOMEUNIQUESTRING [, whatever you want ]}\r\n*/\r\nasync validate_device(pmas4hey) {\r\n  return {}; //whatever you want but at least a \"client_key\" that will be used as unique ID for the client (i.e. pmas4hey signature)\r\n}\r\n```\r\nPer design, positive responses might be cached by mas4h slaves.\r\n\r\n\r\n# Performance considerations (tcp stack)\r\n* You'll have 2 file descriptor (sockets) per devices (initial connexion \u0026 listening port). As nodejs is single threaded, CPU is here the most limiting factor. In our prodution environnement, we try to have less than 2k devices per slave (using low cost virtual machine)\r\n\r\n\r\n# Client implementation recommandation\r\nKeep it as simple as you can ! Our production implementation include a local (lookup) port forwarding and a cron to check the tunnel through it (you can implement this behavior in your slave override). This is not mandatory as SSH already provide an internal keepalive ping (and this JS implementation is instantly notified when a connexion loss/ping timeout occurs) (insert some link of perpetual remote ssh bash here)\r\n\r\n\r\n# Security consideration\r\nOn slave, the SSH server is not bound to any bash or sh. It's just a TCP service that can run with no privilege.\r\n\r\n\r\n\r\n# Restarting a slave\r\nIf a slave instance crash or restart, all bounded client links of this slave are dropped. Slave reconnect as soon as possible to the master and accept incoming client links.\r\nNote : By the less busy cluster member policy, all dropped links are more enclined to reconnect to the slave they left once it comes back (as it will probably be the less busiest slave)\r\n\r\n# Todo\r\n* Bench \u0026 tests EC2\r\n\r\n\r\n# Credits\r\n* [131](https://github.com/131)\r\n* [idjem](https://github.com/idjem)\r\n\r\n\r\n\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F131%2Fmas4h","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F131%2Fmas4h","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F131%2Fmas4h/lists"}