Skip to content
Permalink
Browse files

Initial commit

  • Loading branch information...
antoniomika committed Feb 21, 2019
0 parents commit 2214393eed863f5025740c35313e8330b933a1c4
Showing with 880 additions and 0 deletions.
  1. +2 −0 .dockerignore
  2. +4 −0 .gitignore
  3. +28 −0 Dockerfile
  4. +52 −0 README.md
  5. +78 −0 channels.go
  6. +41 −0 cloudbuild.yaml
  7. +23 −0 go.mod
  8. +47 −0 go.sum
  9. +44 −0 handle.go
  10. +77 −0 http.go
  11. +135 −0 main.go
  12. +134 −0 requests.go
  13. +215 −0 utils.go
@@ -0,0 +1,2 @@
keys/
sish
@@ -0,0 +1,4 @@
keys/
pubkeys/
ssl/
sish
@@ -0,0 +1,28 @@
FROM golang:1.11.5-alpine3.9 as builder
LABEL maintainer="Antonio Mika <me@antoniomika.me>"

RUN apk add --no-cache git gcc musl-dev

ENV GOCACHE /gocache

WORKDIR /usr/local/go/src/github.com/antoniomika/sish

COPY go.mod .
COPY go.sum .

RUN go mod download

COPY . .

RUN go install
RUN go test -i ./...

FROM alpine
LABEL maintainer="Antonio Mika <me@antoniomika.me>"

COPY --from=builder /usr/local/go/src/github.com/antoniomika/sish /sish
COPY --from=builder /go/bin/sish /sish/sish

WORKDIR /sish

ENTRYPOINT ["/sish/sish"]
@@ -0,0 +1,52 @@
sish
====

An open source serveo/ngrok alternative.

## Deploy
Builds are made using Google Cloud Build. Feel free to either use the automated binaries or to build your own.

1. Pull the Docker image
- `docker pull gcr.io/sishio/sish:latest`
2. Run the image
- ```bash
docker run -itd -p 2222:2222 --name sish \
--restart always --network nginx gcr.io/sishio/sish:latest \
-sish.addr=":2222" -sish.http=":8080" -sish.domain="ssi.sh"```
3. SSH to your host to communicate with sish
- `ssh -p 2222 -R 80:localhost:8080 ssi.sh`

## How it works
SSH can normally forward local and remote ports. This service implements an SSH server that only does that and nothing else. The service supports multiplexing connections over HTTP/HTTPS with WebSocket support. Just assign a remote port as port `80` to proxy HTTP traffic and `443` to proxy HTTPS traffic. If you use any other remote port, the server will listen to the port for connections, but only if that port is available.

## CLI Flags
```
sh-3.2# ./sish -h
Usage of ./sish:
-sish.addr string
The address to listen for SSH connections (default "localhost:2222")
-sish.auth
Whether or not to require auth on the SSH service
-sish.debug
Whether or not to print debug information
-sish.domain string
The domain for HTTP(S) multiplexing (default "ssi.sh")
-sish.http string
The address to listen for HTTP connections (default "localhost:80")
-sish.https string
The address to listen for HTTPS connections (default "localhost:443")
-sish.httpsenabled
Whether or not to listen for HTTPS connections
-sish.httpspems string
The location of pem files for HTTPS (fullchain.pem and privkey.pem) (default "ssl/")
-sish.keysdir string
Directory for public keys for pubkey auth (default "pubkeys/")
-sish.password string
Password to use for password auth (default "S3Cr3tP4$$W0rD")
-sish.pkloc string
SSH server private key (default "keys/ssh_key")
-sish.pkpass string
Passphrase to use for the server private key (default "S3Cr3tP4$$phrAsE")
-sish.subdomainlen int
The length of the random subdomain to generate (default 3)
```
@@ -0,0 +1,78 @@
package main

import (
"io"
"log"

"golang.org/x/crypto/ssh"
)

func handleSession(newChannel ssh.NewChannel, sshConn *SSHConnection, state *State) {
connection, requests, err := newChannel.Accept()
if err != nil {
return
}

if *debug {
log.Println("Handling session for connection:", connection)
}

cleanUp := func() {
close(sshConn.Close)
close(sshConn.Messages)
sshConn.SSHConn.Close()
state.SSHConnections.Delete(sshConn.SSHConn.RemoteAddr())
log.Println("Closed SSH connection for:", sshConn.SSHConn.RemoteAddr(), "user:", sshConn.SSHConn.User())
}

go func() {
for {
select {
case c := <-sshConn.Messages:
connection.Write([]byte(c))
connection.Write([]byte{'\r', '\n'})
case <-sshConn.Close:
return
default:
break
}
}
}()

sshConn.Messages <- "Press Ctrl-C to close the session."

go func() {
for {
data := make([]byte, 4096)
dataRead, err := connection.Read(data)
if err != nil && err == io.EOF {
select {
case <-sshConn.Close:
break
default:
cleanUp()
}
break
}

if dataRead != 0 {
if data[0] == 3 {
cleanUp()
}
}
}
}()

go func() {
for req := range requests {
switch req.Type {
case "shell":
req.Reply(true, nil)
default:
if *debug {
log.Println("Sub Channel Type", req.Type, req.WantReply, string(req.Payload))
}
}
}
}()
}
@@ -0,0 +1,41 @@
steps:
- name: "gcr.io/cloud-builders/docker"
entrypoint: "bash"
args:
- "-c"
- |
docker pull gcr.io/$PROJECT_ID/sish:$BRANCH_NAME-builder-latest || true
docker pull gcr.io/$PROJECT_ID/sish:master-builder-latest || true
docker build -t gcr.io/$PROJECT_ID/sish:$BRANCH_NAME-builder-$SHORT_SHA \
-t gcr.io/$PROJECT_ID/sish:$BRANCH_NAME-builder-latest \
--cache-from gcr.io/$PROJECT_ID/sish:$BRANCH_NAME-builder-latest \
--cache-from gcr.io/$PROJECT_ID/sish:master-builder-latest \
--target builder .
- name: "gcr.io/$PROJECT_ID/sish:$BRANCH_NAME-builder-$SHORT_SHA"
entrypoint: "sh"
args:
- "-c"
- |
go test ./...
- name: "gcr.io/cloud-builders/docker"
entrypoint: "bash"
args:
- "-c"
- |
docker pull gcr.io/$PROJECT_ID/sish:$BRANCH_NAME-builder-latest || true
docker pull gcr.io/$PROJECT_ID/sish:master-builder-latest || true
docker build -t gcr.io/$PROJECT_ID/sish:$SHORT_SHA \
--cache-from gcr.io/$PROJECT_ID/sish:$BRANCH_NAME-builder-$SHORT_SHA \
--cache-from gcr.io/$PROJECT_ID/sish:master-builder-latest .
if [[ "$BRANCH_NAME" == "master" ]]; then
docker tag gcr.io/$PROJECT_ID/sish:$SHORT_SHA gcr.io/$PROJECT_ID/sish:latest
docker push gcr.io/$PROJECT_ID/sish:latest
else
exit 0
fi
images:
- "gcr.io/$PROJECT_ID/sish:$SHORT_SHA"
- "gcr.io/$PROJECT_ID/sish:$BRANCH_NAME-builder-latest"
23 go.mod
@@ -0,0 +1,23 @@
module github.com/antoniomika/sish

require (
github.com/fsnotify/fsnotify v1.4.7
github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74 // indirect
github.com/gin-gonic/gin v1.3.0
github.com/golang/protobuf v1.2.0 // indirect
github.com/gorilla/websocket v1.4.0
github.com/json-iterator/go v1.1.5 // indirect
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/stretchr/testify v1.3.0 // indirect
github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 // indirect
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3 // indirect
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
)
47 go.sum
@@ -0,0 +1,47 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74 h1:FaI7wNyesdMBSkIRVUuEEYEvmzufs7EqQvRAxfEXGbQ=
github.com/gin-contrib/sse v0.0.0-20190125020943-a7658810eb74/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs=
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c h1:N7A4JCA2G+j5fuFxCsJqjFU/sZe0mj8H0sSoSwbaikw=
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c/go.mod h1:Nn5wlyECw3iJrzi0AhIWg+AJUb4PlRQVW4/3XHH1LZA=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/ugorji/go v1.1.2 h1:JON3E2/GPW2iDNGoSAusl1KDf5TRQ8k8q7Tp097pZGs=
github.com/ugorji/go v1.1.2/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43 h1:BasDe+IErOQKrMVXab7UayvSlIpiyGwRvuX3EKYY7UA=
github.com/ugorji/go/codec v0.0.0-20190204201341-e444a5086c43/go.mod h1:iT03XoTwV7xq/+UGwKO3UbC1nNNlopQiY61beSdrtOA=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3 h1:czFLhve3vsQetD6JOJ8NZZvGQIXlnN3/yXxbT6/awxI=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -0,0 +1,44 @@
package main

import (
"fmt"
"log"

"golang.org/x/crypto/ssh"
)

func handleRequests(reqs <-chan *ssh.Request, sshConn *SSHConnection, state *State) {
for req := range reqs {
if *debug {
log.Println("Main Request Info", req.Type, req.WantReply, string(req.Payload))
}
go handleRequest(req, sshConn, state)
}
}

func handleRequest(newRequest *ssh.Request, sshConn *SSHConnection, state *State) {
switch req := newRequest.Type; req {
case "tcpip-forward":
handleRemoteForward(newRequest, sshConn, state)
default:
newRequest.Reply(false, nil)
}
}

func handleChannels(chans <-chan ssh.NewChannel, sshConn *SSHConnection, state *State) {
for newChannel := range chans {
if *debug {
log.Println("Main Channel Info", newChannel.ChannelType(), string(newChannel.ExtraData()))
}
go handleChannel(newChannel, sshConn, state)
}
}

func handleChannel(newChannel ssh.NewChannel, sshConn *SSHConnection, state *State) {
switch channel := newChannel.ChannelType(); channel {
case "session":
handleSession(newChannel, sshConn, state)
default:
newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", channel))
}
}
Oops, something went wrong.

0 comments on commit 2214393

Please sign in to comment.
You can’t perform that action at this time.