Skip to content

Commit 99920f8

Browse files
committed
Updated documentation
1 parent cd5bebf commit 99920f8

16 files changed

+223
-190
lines changed

README.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ I can use an SSH command on my laptop like so to forward the connection:
9696
ssh -R 2222:localhost:22 ssi.sh
9797
```
9898
99-
I can use the forwarded connection access my laptop from anywhere:
99+
I can use the forwarded connection to then access my laptop from anywhere:
100100
101101
```bash
102102
ssh -p 2222 ssi.sh
@@ -117,7 +117,7 @@ ssh -R mylaptop:22:localhost:22 ssi.sh
117117

118118
sish won't publish port 22 or 2222 to the rest of the world anymore, instead it'll retain a pointer saying that TCP connections
119119
made from within SSH after a user has authenticated to `mylaptop:22` should be forwarded to the forwarded TCP tunnel.
120-
And then access then I can use the forwarded connection access my laptop from anywhere using:
120+
Then I can use the forwarded connection access my laptop from anywhere using:
121121

122122
```bash
123123
ssh -o ProxyCommand="ssh -W %h:%p ssi.sh" mylaptop
@@ -126,7 +126,7 @@ ssh -o ProxyCommand="ssh -W %h:%p ssi.sh" mylaptop
126126
Shorthand for which is this with newer SSH versions:
127127

128128
```bash
129-
ssh -J mylaptop:22 ssi.sh
129+
ssh -J ssi.sh mylaptop
130130
```
131131

132132
## Authentication
@@ -161,18 +161,18 @@ sish=SSHKEYFINGERPRINT
161161
```
162162

163163
Where `SSHKEYFINGERPRINT` is the fingerprint of the key used for logging into the server. You can set multiple TXT
164-
records and sish will check all of them to ensure at least matches. You can retrieve your key fingerprint by running:
164+
records and sish will check all of them to ensure at least one is a match. You can retrieve your key fingerprint by running:
165165

166166
```bash
167167
ssh-keygen -lf ~/.ssh/id_rsa | awk '{print $2}'
168168
```
169169

170-
## Loadbalancing
170+
## Load balancing
171171

172172
sish can load balance any type of forwarded connection, but this needs to be enabled when starting sish using the `--http-load-balancer`,
173-
`--http-load-balancer`, and `--http-load-balancer` flags. Let's say you have a few edge nodes (raspberry pis) that
173+
`--tcp-load-balancer`, and `--alias-load-balancer` flags. Let's say you have a few edge nodes (raspberry pis) that
174174
are running a service internally but you want to be able to balance load across these devices from the outside world.
175-
By enabling loadbalancing in sish, this happens automatically when a device with the same forwarded TCP port, alias,
175+
By enabling load balancing in sish, this happens automatically when a device with the same forwarded TCP port, alias,
176176
or HTTP subdomain connects to sish. Connections will then be evenly distributed to whatever nodes are connected to
177177
sish that match the forwarded connection.
178178
@@ -210,7 +210,7 @@ or on [freenode IRC #sish](https://kiwiirc.com/client/chat.freenode.net:6697/#si
210210
## CLI Flags
211211
212212
```text
213-
sish is a command line utility that implements an SSH server that can handle HTTP(S)/WS(S)/TCP multiplexing, forwarding and loadbalancing.
213+
sish is a command line utility that implements an SSH server that can handle HTTP(S)/WS(S)/TCP multiplexing, forwarding and load balancing.
214214
It can handle multiple vhosting and reverse tunneling endpoints for a large number of clients.
215215
216216
Usage:

cmd/sish.go

+13-15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Package cmd implements the sish CLI command.
12
package cmd
23

34
import (
@@ -17,35 +18,29 @@ import (
1718
)
1819

1920
var (
20-
// Version describes the version of the current build
21+
// Version describes the version of the current build.
2122
Version = "dev"
2223

23-
// Commit describes the commit of the current build
24+
// Commit describes the commit of the current build.
2425
Commit = "none"
2526

26-
// Date describes the date of the current build
27+
// Date describes the date of the current build.
2728
Date = "unknown"
2829

30+
// configFile holds the location of the config file from CLI flags.
2931
configFile string
3032

33+
// rootCmd is the root cobra command.
3134
rootCmd = &cobra.Command{
3235
Use: "sish",
3336
Short: "The sish command initializes and runs the sish ssh multiplexer",
34-
Long: "sish is a command line utility that implements an SSH server that can handle HTTP(S)/WS(S)/TCP multiplexing, forwarding and loadbalancing.\nIt can handle multiple vhosting and reverse tunneling endpoints for a large number of clients.",
37+
Long: "sish is a command line utility that implements an SSH server that can handle HTTP(S)/WS(S)/TCP multiplexing, forwarding and load balancing.\nIt can handle multiple vhosting and reverse tunneling endpoints for a large number of clients.",
3538
Run: runCommand,
3639
Version: Version,
3740
}
3841
)
3942

40-
type logWriter struct {
41-
TimeFmt string
42-
MultiWriter io.Writer
43-
}
44-
45-
func (w logWriter) Write(bytes []byte) (int, error) {
46-
return fmt.Fprintf(w.MultiWriter, "%v | %s", time.Now().Format(w.TimeFmt), string(bytes))
47-
}
48-
43+
// init initializes flags used by the root command.
4944
func init() {
5045
cobra.OnInitialize(initConfig)
5146

@@ -118,6 +113,8 @@ func init() {
118113
rootCmd.PersistentFlags().DurationP("cleanup-unbound-timeout", "", 5*time.Second, "Duration to wait before cleaning up an unbound (unforwarded) connection")
119114
}
120115

116+
// initConfig initializes the configuration and loads needed
117+
// values. It initializes logging and other vars.
121118
func initConfig() {
122119
viper.SetConfigFile(configFile)
123120

@@ -156,7 +153,7 @@ func initConfig() {
156153
log.Println("Reloaded configuration file.")
157154

158155
log.SetFlags(0)
159-
log.SetOutput(logWriter{
156+
log.SetOutput(utils.LogWriter{
160157
TimeFmt: viper.GetString("time-format"),
161158
MultiWriter: multiWriter,
162159
})
@@ -167,7 +164,7 @@ func initConfig() {
167164
})
168165

169166
log.SetFlags(0)
170-
log.SetOutput(logWriter{
167+
log.SetOutput(utils.LogWriter{
171168
TimeFmt: viper.GetString("time-format"),
172169
MultiWriter: multiWriter,
173170
})
@@ -186,6 +183,7 @@ func Execute() error {
186183
return rootCmd.Execute()
187184
}
188185

186+
// runCommand is used to start the root muxer.
189187
func runCommand(cmd *cobra.Command, args []string) {
190188
sshmuxer.Start()
191189
}

httpmuxer/httpmuxer.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// Package httpmuxer handles all of the HTTP connections made
2+
// to sish. This implements the http multiplexing necessary for
3+
// sish's core feature.
14
package httpmuxer
25

36
import (
@@ -20,7 +23,7 @@ import (
2023
"github.com/gin-gonic/gin"
2124
)
2225

23-
// Start initializes the HTTP service
26+
// Start initializes the HTTP service.
2427
func Start(state *utils.State) {
2528
releaseMode := gin.ReleaseMode
2629
if viper.GetBool("debug") {
@@ -33,14 +36,18 @@ func Start(state *utils.State) {
3336
r := gin.New()
3437
r.LoadHTMLGlob("templates/*")
3538
r.Use(func(c *gin.Context) {
39+
// startTime is used for calculating latencies.
3640
c.Set("startTime", time.Now())
41+
42+
// Here is where we check whether or not an IP is blocked.
3743
clientIPAddr, _, err := net.SplitHostPort(c.Request.RemoteAddr)
3844
if state.IPFilter.Blocked(c.ClientIP()) || state.IPFilter.Blocked(clientIPAddr) || err != nil {
3945
c.AbortWithStatus(http.StatusForbidden)
4046
return
4147
}
4248
c.Next()
4349
}, gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
50+
// Here is the logger we use to format each incoming request.
4451
var statusColor, methodColor, resetColor string
4552
if param.IsOutputColor() {
4653
statusColor = param.StatusCodeColor()
@@ -77,12 +84,12 @@ func Start(state *utils.State) {
7784
loc, ok := state.HTTPListeners.Load(hostname)
7885
if ok {
7986
proxyHolder := loc.(*utils.HTTPHolder)
80-
sshConnTmp, ok := proxyHolder.SSHConns.Load(param.Keys["proxySocket"])
87+
sshConnTmp, ok := proxyHolder.SSHConnections.Load(param.Keys["proxySocket"])
8188
if ok {
8289
sshConn := sshConnTmp.(*utils.SSHConnection)
8390
sshConn.SendMessage(strings.TrimSpace(logLine), true)
8491
} else {
85-
proxyHolder.SSHConns.Range(func(key, val interface{}) bool {
92+
proxyHolder.SSHConnections.Range(func(key, val interface{}) bool {
8693
sshConn := val.(*utils.SSHConnection)
8794
sshConn.SendMessage(strings.TrimSpace(logLine), true)
8895
return true
@@ -93,6 +100,7 @@ func Start(state *utils.State) {
93100

94101
return logLine
95102
}), gin.Recovery(), func(c *gin.Context) {
103+
// Return a 404 for the favicon.
96104
if strings.HasPrefix(c.Request.URL.Path, "/favicon.ico") {
97105
c.AbortWithStatus(http.StatusNotFound)
98106
return
@@ -138,6 +146,9 @@ func Start(state *utils.State) {
138146
gin.WrapH(proxyHolder.Balancer)(c)
139147
})
140148

149+
// If HTTPS is enabled, setup certmagic to allow us to provision HTTPS certs on the fly.
150+
// You can use sish without a wildcard cert, but you really should. If you get a lot of clients
151+
// with many random subdomains, you'll burn through your Let's Encrypt quota. Be careful!
141152
if viper.GetBool("https") {
142153
certManager := certmagic.NewDefault()
143154

httpmuxer/proxy.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import (
1818
"github.com/spf13/viper"
1919
)
2020

21-
// RoundTripper returns the specific handler for unix connections
21+
// RoundTripper returns the specific handler for unix connections. This
22+
// will allow us to use our created sockets cleanly.
2223
func RoundTripper() *http.Transport {
2324
dialer := func(network, addr string) (net.Conn, error) {
2425
realAddr, err := base64.StdEncoding.DecodeString(strings.Split(addr, ":")[0])
@@ -39,7 +40,9 @@ func RoundTripper() *http.Transport {
3940
}
4041
}
4142

42-
// ResponseModifier implements a response modifier for the specified request
43+
// ResponseModifier implements a response modifier for the specified request.
44+
// We don't actually modify any requests, but we do want to record the request
45+
// so we can send it to the web console.
4346
func ResponseModifier(state *utils.State, hostname string, reqBody []byte, c *gin.Context) func(*http.Response) error {
4447
return func(response *http.Response) error {
4548
if viper.GetBool("admin-console") || viper.GetBool("service-console") {

main.go

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Package main represents the main entrypoint of the sish application.
12
package main
23

34
import (
@@ -6,6 +7,7 @@ import (
67
"github.com/antoniomika/sish/cmd"
78
)
89

10+
// main will start the sish command lifecycle and spawn the sish services.
911
func main() {
1012
err := cmd.Execute()
1113
if err != nil {

sshmuxer/aliashandler.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"github.com/logrusorgru/aurora"
1313
)
1414

15+
// handleAliasListener handles the creation of the aliasHandler
16+
// (or addition for load balancing) and set's up the underlying listeners.
1517
func handleAliasListener(check *channelForwardMsg, stringPort string, requestMessages string, listenerHolder *utils.ListenerHolder, state *utils.State, sshConn *utils.SSHConnection) (*utils.AliasHolder, *url.URL, string, string, error) {
1618
validAlias, aH := utils.GetOpenAlias(check.Addr, stringPort, state, sshConn)
1719

@@ -24,15 +26,15 @@ func handleAliasListener(check *channelForwardMsg, stringPort string, requestMes
2426
}
2527

2628
aH = &utils.AliasHolder{
27-
AliasHost: validAlias,
28-
SSHConns: &sync.Map{},
29-
Balancer: lb,
29+
AliasHost: validAlias,
30+
SSHConnections: &sync.Map{},
31+
Balancer: lb,
3032
}
3133

3234
state.AliasListeners.Store(validAlias, aH)
3335
}
3436

35-
aH.SSHConns.Store(listenerHolder.Addr().String(), sshConn)
37+
aH.SSHConnections.Store(listenerHolder.Addr().String(), sshConn)
3638

3739
serverURL := &url.URL{
3840
Host: base64.StdEncoding.EncodeToString([]byte(listenerHolder.Addr().String())),

sshmuxer/channels.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ import (
1414
"golang.org/x/crypto/ssh"
1515
)
1616

17+
// proxyProtoPrefix is used when deciding what proxy protocol
18+
// version to use.
1719
var proxyProtoPrefix = "proxyproto:"
1820

21+
// handleSession handles the channel when a user requests a session.
22+
// This is how we send console messages.
1923
func handleSession(newChannel ssh.NewChannel, sshConn *utils.SSHConnection, state *utils.State) {
2024
connection, requests, err := newChannel.Accept()
2125
if err != nil {
@@ -89,6 +93,7 @@ func handleSession(newChannel ssh.NewChannel, sshConn *utils.SSHConnection, stat
8993
}()
9094
}
9195

96+
// handleAlias is used when handling a SSH connection to attach to an alias listener.
9297
func handleAlias(newChannel ssh.NewChannel, sshConn *utils.SSHConnection, state *utils.State) {
9398
connection, requests, err := newChannel.Accept()
9499
if err != nil {
@@ -140,7 +145,7 @@ func handleAlias(newChannel ssh.NewChannel, sshConn *utils.SSHConnection, state
140145
log.Println(logLine)
141146

142147
if viper.GetBool("log-to-client") {
143-
aH.SSHConns.Range(func(key, val interface{}) bool {
148+
aH.SSHConnections.Range(func(key, val interface{}) bool {
144149
sshConn := val.(*utils.SSHConnection)
145150

146151
sshConn.Listeners.Range(func(key, val interface{}) bool {
@@ -178,13 +183,15 @@ func handleAlias(newChannel ssh.NewChannel, sshConn *utils.SSHConnection, state
178183
}
179184
}
180185

186+
// writeToSession is where we write to the underlying session channel.
181187
func writeToSession(connection ssh.Channel, c string) {
182188
_, err := connection.Write(append([]byte(c), []byte{'\r', '\n'}...))
183189
if err != nil && viper.GetBool("debug") {
184190
log.Println("Error trying to write message to socket:", err)
185191
}
186192
}
187193

194+
// getProxyProtoVersion returns the proxy proto version selected by the client.
188195
func getProxyProtoVersion(proxyProtoUserVersion string) byte {
189196
if viper.GetString("proxy-protocol-version") != "userdefined" {
190197
proxyProtoUserVersion = viper.GetString("proxy-protocol-version")

sshmuxer/handle.go

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"golang.org/x/crypto/ssh"
1111
)
1212

13+
// handleRequests handles incoming requests from an SSH connection.
1314
func handleRequests(reqs <-chan *ssh.Request, sshConn *utils.SSHConnection, state *utils.State) {
1415
for req := range reqs {
1516
if viper.GetBool("debug") {
@@ -19,6 +20,7 @@ func handleRequests(reqs <-chan *ssh.Request, sshConn *utils.SSHConnection, stat
1920
}
2021
}
2122

23+
// handleRequest handles a incoming request from a SSH connection.
2224
func handleRequest(newRequest *ssh.Request, sshConn *utils.SSHConnection, state *utils.State) {
2325
switch req := newRequest.Type; req {
2426
case "tcpip-forward":
@@ -37,6 +39,7 @@ func handleRequest(newRequest *ssh.Request, sshConn *utils.SSHConnection, state
3739
}
3840
}
3941

42+
// checkSession will check a session to see that it has a session.
4043
func checkSession(newRequest *ssh.Request, sshConn *utils.SSHConnection, state *utils.State) {
4144
if sshConn.CleanupHandler {
4245
return
@@ -55,6 +58,7 @@ func checkSession(newRequest *ssh.Request, sshConn *utils.SSHConnection, state *
5558
}
5659
}
5760

61+
// handleChannels handles a SSH connection's channel requests.
5862
func handleChannels(chans <-chan ssh.NewChannel, sshConn *utils.SSHConnection, state *utils.State) {
5963
for newChannel := range chans {
6064
if viper.GetBool("debug") {
@@ -64,6 +68,7 @@ func handleChannels(chans <-chan ssh.NewChannel, sshConn *utils.SSHConnection, s
6468
}
6569
}
6670

71+
// handleChannel handles a SSH connection's channel request.
6772
func handleChannel(newChannel ssh.NewChannel, sshConn *utils.SSHConnection, state *utils.State) {
6873
switch channel := newChannel.ChannelType(); channel {
6974
case "session":

sshmuxer/httphandler.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/spf13/viper"
1616
)
1717

18+
// handleHTTPListener handles the creation of the httpHandler
19+
// (or addition for load balancing) and set's up the underlying listeners.
1820
func handleHTTPListener(check *channelForwardMsg, stringPort string, requestMessages string, listenerHolder *utils.ListenerHolder, state *utils.State, sshConn *utils.SSHConnection) (*utils.HTTPHolder, *url.URL, string, string, error) {
1921
scheme := "http"
2022
if stringPort == "443" {
@@ -45,17 +47,17 @@ func handleHTTPListener(check *channelForwardMsg, stringPort string, requestMess
4547
}
4648

4749
pH = &utils.HTTPHolder{
48-
HTTPHost: host,
49-
Scheme: scheme,
50-
SSHConns: &sync.Map{},
51-
Forward: fwd,
52-
Balancer: lb,
50+
HTTPHost: host,
51+
Scheme: scheme,
52+
SSHConnections: &sync.Map{},
53+
Forward: fwd,
54+
Balancer: lb,
5355
}
5456

5557
state.HTTPListeners.Store(host, pH)
5658
}
5759

58-
pH.SSHConns.Store(listenerHolder.Addr().String(), sshConn)
60+
pH.SSHConnections.Store(listenerHolder.Addr().String(), sshConn)
5961

6062
serverURL := &url.URL{
6163
Host: base64.StdEncoding.EncodeToString([]byte(listenerHolder.Addr().String())),

0 commit comments

Comments
 (0)