Initial commit with Dockerfile and Go code
This commit is contained in:
27
.github/workflows/build-and-push.yml
vendored
Normal file
27
.github/workflows/build-and-push.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: dcorral/ssh-remote:latest
|
||||
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM golang:1.21-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY main.go ./
|
||||
RUN go build -o sshserver main.go
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk add --no-cache ca-certificates
|
||||
|
||||
COPY --from=builder /app/sshserver /usr/local/bin/sshserver
|
||||
|
||||
EXPOSE 22
|
||||
|
||||
CMD ["/usr/local/bin/sshserver"]
|
||||
11
docker-compose.yml
Normal file
11
docker-compose.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
ssh-server:
|
||||
image: ssh-server
|
||||
build: .
|
||||
ports:
|
||||
- "22:22"
|
||||
volumes:
|
||||
- ../tui:/app/tui:ro
|
||||
cap_add:
|
||||
- SYS_CHROOT
|
||||
restart: unless-stopped
|
||||
10
go.mod
Normal file
10
go.mod
Normal file
@@ -0,0 +1,10 @@
|
||||
module sshserver
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/creack/pty/v2 v2.0.1
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
|
||||
)
|
||||
|
||||
require golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
|
||||
8
go.sum
Normal file
8
go.sum
Normal file
@@ -0,0 +1,8 @@
|
||||
github.com/creack/pty/v2 v2.0.1 h1:RDY1VY5b+7m2mfPsugucOYPIxMp+xal5ZheSyVzUA+k=
|
||||
github.com/creack/pty/v2 v2.0.1/go.mod h1:2dSssKp3b86qYEMwA/FPwc3ff+kYpDdQI8osU8J7gxQ=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
138
main.go
Normal file
138
main.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"github.com/creack/pty/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := &ssh.ServerConfig{
|
||||
NoClientAuth: true,
|
||||
}
|
||||
_, key, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
signer, err := ssh.NewSignerFromKey(key)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
config.AddHostKey(signer)
|
||||
listener, err := net.Listen("tcp", ":22")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("SSH server listening on :22")
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Println("Accept error:", err)
|
||||
continue
|
||||
}
|
||||
go handleConn(conn, config)
|
||||
}
|
||||
}
|
||||
|
||||
func handleConn(conn net.Conn, config *ssh.ServerConfig) {
|
||||
sshConn, chans, reqs, err := ssh.NewServerConn(conn, config)
|
||||
if err != nil {
|
||||
log.Println("ServerConn error:", err)
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
log.Println("New connection from", sshConn.RemoteAddr(), "user", sshConn.User())
|
||||
go ssh.DiscardRequests(reqs)
|
||||
for newChannel := range chans {
|
||||
if newChannel.ChannelType() != "session" {
|
||||
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
|
||||
continue
|
||||
}
|
||||
channel, requests, err := newChannel.Accept()
|
||||
if err != nil {
|
||||
log.Println("Channel accept error:", err)
|
||||
continue
|
||||
}
|
||||
go handleChannel(channel, requests)
|
||||
}
|
||||
sshConn.Wait()
|
||||
}
|
||||
|
||||
func handleChannel(channel ssh.Channel, requests <-chan *ssh.Request) {
|
||||
defer channel.Close()
|
||||
var ptmx *os.File
|
||||
for req := range requests {
|
||||
switch req.Type {
|
||||
case "pty-req":
|
||||
req.Reply(true, nil)
|
||||
case "window-change":
|
||||
if ptmx != nil {
|
||||
width := binary.BigEndian.Uint32(req.Payload)
|
||||
height := binary.BigEndian.Uint32(req.Payload[4:])
|
||||
pty.Setsize(ptmx, &pty.Winsize{Cols: uint16(width), Rows: uint16(height)})
|
||||
}
|
||||
req.Reply(true, nil)
|
||||
case "shell":
|
||||
req.Reply(true, nil)
|
||||
cmd := exec.Command("/app/tui")
|
||||
cmd.Env = []string{"PATH=/bin"}
|
||||
cmd.Dir = "/"
|
||||
var err error
|
||||
ptmx, err = pty.Start(cmd)
|
||||
if err != nil {
|
||||
log.Println("PTY start error:", err)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
defer ptmx.Close()
|
||||
go io.Copy(channel, ptmx)
|
||||
go io.Copy(ptmx, channel)
|
||||
cmd.Wait()
|
||||
channel.Close()
|
||||
}()
|
||||
case "exec":
|
||||
req.Reply(true, nil)
|
||||
command := string(req.Payload[4:])
|
||||
runCommand(channel, command)
|
||||
return
|
||||
default:
|
||||
req.Reply(false, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runCommand(channel ssh.Channel, command string) {
|
||||
cmd := exec.Command("/bin/bash", "-c", command)
|
||||
cmd.Env = []string{"PATH=/bin"}
|
||||
cmd.Dir = "/"
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
log.Println("StdinPipe error:", err)
|
||||
return
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
log.Println("StdoutPipe error:", err)
|
||||
return
|
||||
}
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
log.Println("StderrPipe error:", err)
|
||||
return
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Println("Start error:", err)
|
||||
return
|
||||
}
|
||||
go io.Copy(stdin, channel)
|
||||
go io.Copy(channel, stdout)
|
||||
go io.Copy(channel, stderr)
|
||||
cmd.Wait()
|
||||
}
|
||||
Reference in New Issue
Block a user