2021-05-07 10:27:53 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"log"
|
2021-08-26 11:45:00 -06:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2021-05-07 10:27:53 -06:00
|
|
|
"os"
|
2021-08-26 11:45:00 -06:00
|
|
|
"os/signal"
|
2021-05-07 10:27:53 -06:00
|
|
|
"path/filepath"
|
2021-08-26 13:30:02 -06:00
|
|
|
"regexp"
|
2021-08-26 11:45:00 -06:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2021-05-07 10:27:53 -06:00
|
|
|
|
|
|
|
"github.com/gliderlabs/ssh"
|
2021-08-26 11:45:00 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2021-08-27 11:03:58 -06:00
|
|
|
domainName = "prox.int"
|
|
|
|
portRange = "7000-7999"
|
|
|
|
bindHost = "[::1]"
|
2021-05-07 10:27:53 -06:00
|
|
|
)
|
|
|
|
|
2021-08-26 13:30:02 -06:00
|
|
|
var filterName = regexp.MustCompile("[^a-z0-9-]+")
|
|
|
|
|
2021-05-07 10:27:53 -06:00
|
|
|
func main() {
|
2021-08-26 11:45:00 -06:00
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
|
|
|
|
defer stop()
|
|
|
|
|
|
|
|
run(ctx)
|
2021-05-07 10:27:53 -06:00
|
|
|
}
|
|
|
|
|
2021-08-26 11:45:00 -06:00
|
|
|
func run(ctx context.Context) {
|
|
|
|
lis, err := net.Listen("tcp", envMust("SSH_LISTEN"))
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err.Error())
|
|
|
|
}
|
|
|
|
|
2021-05-07 10:27:53 -06:00
|
|
|
var opts []ssh.Option
|
|
|
|
opts = append(
|
|
|
|
opts,
|
|
|
|
ssh.NoPty(),
|
|
|
|
)
|
|
|
|
|
2021-08-27 11:03:58 -06:00
|
|
|
hostKeys := envMust("SSH_HOSTKEYS")
|
2023-03-27 15:29:04 -06:00
|
|
|
files, err := os.ReadDir(hostKeys)
|
2021-05-07 10:27:53 -06:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2021-08-27 11:03:58 -06:00
|
|
|
for _, f := range files {
|
|
|
|
opts = append(opts, ssh.HostKeyFile(filepath.Join(hostKeys, f.Name())))
|
|
|
|
}
|
2021-05-07 10:27:53 -06:00
|
|
|
|
2023-03-27 15:29:04 -06:00
|
|
|
loadTemplates()
|
|
|
|
|
2021-08-26 11:45:00 -06:00
|
|
|
srv := &server{
|
|
|
|
bindHost: envDefault("SSH_HOST", bindHost),
|
|
|
|
domainName: envDefault("SSH_DOMAIN", domainName),
|
2021-08-27 11:03:58 -06:00
|
|
|
domainSuffix: envDefault("SSH_DOMAIN_SUFFIX", "."+domainName),
|
2021-05-07 10:27:53 -06:00
|
|
|
}
|
2021-08-26 11:45:00 -06:00
|
|
|
opts = append(opts, srv.optAuthUser()...)
|
|
|
|
|
|
|
|
http.HandleFunc("/", srv.handleHTTP)
|
|
|
|
mux := New(lis, srv.serveHTTP(ctx), srv.serveSSH(ctx, opts...))
|
2021-05-07 10:27:53 -06:00
|
|
|
|
2021-08-26 11:45:00 -06:00
|
|
|
listen := mux.Listener.Addr().String()
|
|
|
|
if idx := strings.LastIndex(listen, ":"); idx >= 0 {
|
|
|
|
if i, err := strconv.Atoi(listen[idx+1:]); err == nil {
|
|
|
|
srv.listenPort = uint32(i)
|
|
|
|
}
|
2021-05-07 10:27:53 -06:00
|
|
|
}
|
|
|
|
|
2021-08-27 11:03:58 -06:00
|
|
|
if r := envDefault("SSH_PORTRANGE", portRange); r != "" {
|
|
|
|
sp := strings.SplitN(r, "-", 2)
|
|
|
|
if len(sp) == 1 {
|
|
|
|
log.Fatal("SSH_PORTRANGE should have start and end like 7000-7999")
|
|
|
|
}
|
|
|
|
|
|
|
|
var p uint64
|
|
|
|
if p, err = strconv.ParseUint(sp[0], 10, 32); err != nil {
|
|
|
|
log.Fatal("SSH_PORTRANGE start port invalid:", sp[0])
|
|
|
|
}
|
|
|
|
srv.portStart = uint32(p)
|
|
|
|
|
|
|
|
if p, err = strconv.ParseUint(sp[1], 10, 32); err != nil {
|
|
|
|
log.Fatal("SSH_PORTRANGE end port invalid:", sp[1])
|
|
|
|
}
|
|
|
|
srv.portEnd = uint32(p)
|
|
|
|
|
|
|
|
if srv.portStart > srv.portEnd {
|
|
|
|
log.Fatalf("SSH_PORTRANGE is reversed %d > %d", srv.portStart, srv.portEnd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = mux.Serve(ctx); err != nil {
|
|
|
|
log.Fatal()
|
|
|
|
}
|
2021-05-07 10:27:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func envMust(s string) string {
|
|
|
|
v := os.Getenv(s)
|
|
|
|
if v == "" {
|
|
|
|
log.Fatal("missing env ", s)
|
|
|
|
}
|
2021-08-26 11:45:00 -06:00
|
|
|
log.Println("env", s, "==", v)
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
func envDefault(s, d string) string {
|
|
|
|
v := os.Getenv(s)
|
|
|
|
if v == "" {
|
2021-08-27 11:03:58 -06:00
|
|
|
v = d
|
2021-08-26 11:45:00 -06:00
|
|
|
}
|
|
|
|
log.Println("env", s, "==", v)
|
2021-05-07 10:27:53 -06:00
|
|
|
return v
|
|
|
|
}
|