Add project files

This commit is contained in:
ChaoticByte 2024-06-29 09:38:07 +02:00
parent d93b571440
commit 2dd8f72714
No known key found for this signature in database
9 changed files with 260 additions and 1 deletions

3
.gitignore vendored
View file

@ -20,3 +20,6 @@
# Go workspace file
go.work
go.work.sum
# build
ass

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 Julian Müller
Copyright (c) 2024 Julian Müller (ChaoticByte)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

30
args.go Normal file
View file

@ -0,0 +1,30 @@
package main
// Copyright (c) 2024 Julian Müller (ChaoticByte)
// License: MIT
import (
"flag"
)
var configFilepath string
var privateKeyFilepath string
var logFlag bool
func ParseCommandline() bool {
flag.StringVar(&configFilepath, "config", "", "The path to the config file (required)")
flag.StringVar(&privateKeyFilepath, "pkey", "", "The path to the private key file (required)")
flag.BoolVar(&logFlag, "log", false, "Enable logging of messages")
flag.Parse()
missing := false
if configFilepath == "" {
missing = true
}
if privateKeyFilepath == "" {
missing = true
}
if missing {
flag.Usage()
}
return !missing
}

8
config.example.yml Normal file
View file

@ -0,0 +1,8 @@
# Example server config file
host: ''
port: 8022
clients:
# This are just examples. Please generate your own keypair for each client you want to add.
# username: public-key
example1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEg9AzPuPe/zv0HszL+aRdJgZ8DPwVLV59FJoUHe7kWH
example2: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIQH6ESwEHVvlbxZJd7JhTPWXy3yQ5Zaf8qVBxzf6Td/

24
config.go Normal file
View file

@ -0,0 +1,24 @@
package main
// Copyright (c) 2024 Julian Müller (ChaoticByte)
// License: MIT
import (
"log"
"os"
"github.com/goccy/go-yaml"
)
var config struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Clients map[string]string `yaml:"clients"`
}
func ParseConfig(filepath string) {
data, err := os.ReadFile(filepath)
if err != nil { log.Fatal(err) }
err = yaml.Unmarshal(data, &config)
if err != nil { log.Fatal(err) }
}

17
go.mod Normal file
View file

@ -0,0 +1,17 @@
module github.com/ChaoticByte/ass
go 1.22.4
require (
github.com/gliderlabs/ssh v0.3.7
github.com/goccy/go-yaml v1.11.3
)
require (
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
)

43
go.sum Normal file
View file

@ -0,0 +1,43 @@
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=

15
main.go Normal file
View file

@ -0,0 +1,15 @@
package main
// Copyright (c) 2024 Julian Müller (ChaoticByte)
// License: MIT
import "os"
func main() {
if ParseCommandline() {
ParseConfig(configFilepath)
RunServer()
} else {
os.Exit(1)
}
}

119
ssh.go Normal file
View file

@ -0,0 +1,119 @@
package main
// Copyright (c) 2024 Julian Müller (ChaoticByte)
// License: MIT
import (
"bufio"
"fmt"
"log"
"slices"
"sync"
"github.com/gliderlabs/ssh"
)
var sessionListMutex = &sync.Mutex{}
var sessionList = []*ssh.Session{}
func PubkeyAuth(ctx ssh.Context, key ssh.PublicKey) bool {
// verify public key for auth
u := ctx.User()
pubkey := config.Clients[u]
if pubkey == "" { // username not in config.Clients
log.Printf("Authentication failure: Unknown user %s", u)
return false
}
k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubkey))
if err != nil {
log.Printf("Authentication failure: Could not parse public key for %s", u)
}
if ssh.KeysEqual(k, key) {
return true // bassd
} else {
log.Printf("Authentication failure: Invalid public key for user %s", u)
return false
}
}
func AddToSessionList(s *ssh.Session) int {
// add new session to list
var idx int = -1
sessionListMutex.Lock()
defer sessionListMutex.Unlock()
lenClientList := len(sessionList)
// try to reuse an existing nil position in the slice
for i := range(lenClientList) {
if sessionList[i] == nil {
// we found one!
sessionList[i] = s
idx = i
break
}
}
if idx == -1 {
// we have to append
sessionList = append(sessionList, s)
idx = lenClientList
}
return idx
}
func RemoveFromSessionList(idx int) {
sessionListMutex.Lock()
defer sessionListMutex.Unlock()
// we have to set it to nil instead of constructing a new
// slice, so that the other session handlers have still
// the correct index for their own removal
sessionList[idx] = nil // session associated with the pointer reference should be freed automatically
}
func BroadcastLine(line []byte) {
if logFlag { log.Printf("%s", line) } // output message
sessions := sessionList // create snapshot of client list
for i := range(len(sessions)) {
session := sessions[i]
if session != nil {
(*session).Write(line)
}
}
}
func HandleSession(session ssh.Session) {
// get username
user := session.Context().User()
msgPrefix := []byte(fmt.Sprintf("%s: ", user))
// Add client to list
sessionListIdx := AddToSessionList(&session)
defer func () { // cleanup tasks
RemoveFromSessionList(sessionListIdx)
log.Printf("User %s disconnected.\n", user)
BroadcastLine([]byte(fmt.Sprintf("[disconnected] %s\n", user)))
}()
// helo
log.Printf("User %s connected.\n", user)
BroadcastLine([]byte(fmt.Sprintf("[connected] %s\n", user)))
// IO
linebuf := bufio.NewReader(session)
for {
line, err := linebuf.ReadBytes('\n')
if err != nil {
if err.Error() != "EOF" {
log.Println("An error occurred! ", user, " ", err)
}
break // disconnect
} else {
BroadcastLine(slices.Concat(msgPrefix, line))
}
}
}
func RunServer() {
log.Printf("Starting SSH server on %s:%v", config.Host, config.Port)
log.Fatal(ssh.ListenAndServe(
fmt.Sprintf("%s:%v", config.Host, config.Port),
HandleSession,
ssh.HostKeyFile(privateKeyFilepath),
ssh.PublicKeyAuth(PubkeyAuth),
ssh.NoPty()))
}