mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-04-12 23:27:17 +03:00
Banhammer!
This commit is contained in:
parent
8f408b7674
commit
af20d755e7
2
Makefile
2
Makefile
@ -19,7 +19,7 @@ $(KEY):
|
||||
ssh-keygen -f $(KEY) -P ''
|
||||
|
||||
run: $(BINARY) $(KEY)
|
||||
./$(BINARY) -i $(KEY) -b ":$(PORT)" -vv
|
||||
./$(BINARY) -i $(KEY) --bind ":$(PORT)" -vv
|
||||
|
||||
test:
|
||||
go test .
|
||||
|
27
client.go
27
client.go
@ -86,6 +86,10 @@ func (c *Client) Rename(name string) {
|
||||
c.term.SetPrompt(fmt.Sprintf("[%s] ", name))
|
||||
}
|
||||
|
||||
func (c *Client) Fingerprint() string {
|
||||
return c.Conn.Permissions.Extensions["fingerprint"]
|
||||
}
|
||||
|
||||
func (c *Client) handleShell(channel ssh.Channel) {
|
||||
defer channel.Close()
|
||||
|
||||
@ -129,13 +133,34 @@ func (c *Client) handleShell(channel ssh.Channel) {
|
||||
case "/whois":
|
||||
if len(parts) == 2 {
|
||||
client := c.Server.Who(parts[1])
|
||||
c.Msg <- fmt.Sprintf("-> %s is %s via %s", client.Name, client.Conn.RemoteAddr(), client.Conn.ClientVersion())
|
||||
if client != nil {
|
||||
c.Msg <- fmt.Sprintf("-> %s is %s via %s", client.Name, client.Fingerprint(), client.Conn.ClientVersion())
|
||||
} else {
|
||||
c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
|
||||
}
|
||||
} else {
|
||||
c.Msg <- fmt.Sprintf("-> Missing $NAME from: /whois $NAME")
|
||||
}
|
||||
case "/list":
|
||||
names := c.Server.List(nil)
|
||||
c.Msg <- fmt.Sprintf("-> %d connected: %s", len(names), strings.Join(names, ", "))
|
||||
case "/ban":
|
||||
if !c.Server.IsOp(c) {
|
||||
c.Msg <- fmt.Sprintf("-> You're not an admin.")
|
||||
} else if len(parts) != 2 {
|
||||
c.Msg <- fmt.Sprintf("-> Missing $NAME from: /ban $NAME")
|
||||
} else {
|
||||
client := c.Server.Who(parts[1])
|
||||
if client == nil {
|
||||
c.Msg <- fmt.Sprintf("-> No such name: %s", parts[1])
|
||||
} else {
|
||||
fingerprint := client.Fingerprint()
|
||||
client.Write(fmt.Sprintf("-> Banned by %s.", c.Name))
|
||||
c.Server.Ban(fingerprint, nil)
|
||||
client.Conn.Close()
|
||||
c.Server.Broadcast(fmt.Sprintf("* %s was banned by %s", parts[1], c.Name), nil)
|
||||
}
|
||||
}
|
||||
default:
|
||||
c.Msg <- fmt.Sprintf("-> Invalid command: %s", line)
|
||||
}
|
||||
|
7
cmd.go
7
cmd.go
@ -13,8 +13,9 @@ import (
|
||||
|
||||
type Options struct {
|
||||
Verbose []bool `short:"v" long:"verbose" description:"Show verbose logging."`
|
||||
Bind string `short:"b" long:"bind" description:"Host and port to listen on." default:"0.0.0.0:22"`
|
||||
Identity string `short:"i" long:"identity" description:"Private key to identify server with." default:"~/.ssh/id_rsa"`
|
||||
Bind string `long:"bind" description:"Host and port to listen on." default:"0.0.0.0:22"`
|
||||
Admin string `long:"admin" description:"Fingerprint of pubkey to mark as admin."`
|
||||
}
|
||||
|
||||
var logLevels = []log.Level{
|
||||
@ -66,6 +67,10 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if options.Admin != "" {
|
||||
server.Op(options.Admin)
|
||||
}
|
||||
|
||||
<-sig // Wait for ^C signal
|
||||
logger.Warningf("Interrupt signal detected, shutting down.")
|
||||
server.Stop()
|
||||
|
85
server.go
85
server.go
@ -1,11 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
@ -19,12 +21,13 @@ type Clients map[string]*Client
|
||||
|
||||
type Server struct {
|
||||
sshConfig *ssh.ServerConfig
|
||||
sshSigner *ssh.Signer
|
||||
done chan struct{}
|
||||
clients Clients
|
||||
lock sync.Mutex
|
||||
count int
|
||||
history *History
|
||||
admins map[string]struct{} // fingerprint lookup
|
||||
banned map[string]*time.Time // fingerprint lookup
|
||||
}
|
||||
|
||||
func NewServer(privateKey []byte) (*Server, error) {
|
||||
@ -33,26 +36,30 @@ func NewServer(privateKey []byte) (*Server, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
server := Server{
|
||||
done: make(chan struct{}),
|
||||
clients: Clients{},
|
||||
count: 0,
|
||||
history: NewHistory(HISTORY_LEN),
|
||||
admins: map[string]struct{}{},
|
||||
banned: map[string]*time.Time{},
|
||||
}
|
||||
|
||||
config := ssh.ServerConfig{
|
||||
NoClientAuth: false,
|
||||
PasswordCallback: func(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
|
||||
return nil, nil
|
||||
},
|
||||
// Auth-related things should be constant-time to avoid timing attacks.
|
||||
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
||||
// fingerprint := md5.Sum(key.Marshal())
|
||||
return nil, nil
|
||||
fingerprint := Fingerprint(key)
|
||||
if server.IsBanned(fingerprint) {
|
||||
return nil, fmt.Errorf("Banned.")
|
||||
}
|
||||
perm := &ssh.Permissions{Extensions: map[string]string{"fingerprint": fingerprint}}
|
||||
return perm, nil
|
||||
},
|
||||
}
|
||||
config.AddHostKey(signer)
|
||||
|
||||
server := Server{
|
||||
sshConfig: &config,
|
||||
sshSigner: &signer,
|
||||
done: make(chan struct{}),
|
||||
clients: Clients{},
|
||||
count: 0,
|
||||
history: NewHistory(HISTORY_LEN),
|
||||
}
|
||||
server.sshConfig = &config
|
||||
|
||||
return &server, nil
|
||||
}
|
||||
@ -160,6 +167,50 @@ func (s *Server) Who(name string) *Client {
|
||||
return s.clients[name]
|
||||
}
|
||||
|
||||
func (s *Server) Op(fingerprint string) {
|
||||
logger.Infof("Adding admin: %s", fingerprint)
|
||||
s.lock.Lock()
|
||||
s.admins[fingerprint] = struct{}{}
|
||||
s.lock.Unlock()
|
||||
}
|
||||
|
||||
func (s *Server) IsOp(client *Client) bool {
|
||||
_, r := s.admins[client.Fingerprint()]
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *Server) IsBanned(fingerprint string) bool {
|
||||
ban, hasBan := s.banned[fingerprint]
|
||||
if !hasBan {
|
||||
return false
|
||||
}
|
||||
if ban == nil {
|
||||
return true
|
||||
}
|
||||
if ban.Before(time.Now()) {
|
||||
s.Unban(fingerprint)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Server) Ban(fingerprint string, duration *time.Duration) {
|
||||
var until *time.Time
|
||||
s.lock.Lock()
|
||||
if duration != nil {
|
||||
when := time.Now().Add(*duration)
|
||||
until = &when
|
||||
}
|
||||
s.banned[fingerprint] = until
|
||||
s.lock.Unlock()
|
||||
}
|
||||
|
||||
func (s *Server) Unban(fingerprint string) {
|
||||
s.lock.Lock()
|
||||
delete(s.banned, fingerprint)
|
||||
s.lock.Unlock()
|
||||
}
|
||||
|
||||
func (s *Server) Start(laddr string) error {
|
||||
// Once a ServerConfig has been configured, connections can be
|
||||
// accepted.
|
||||
@ -214,3 +265,9 @@ func (s *Server) Stop() {
|
||||
|
||||
close(s.done)
|
||||
}
|
||||
|
||||
func Fingerprint(k ssh.PublicKey) string {
|
||||
hash := md5.Sum(k.Marshal())
|
||||
r := fmt.Sprintf("% x", hash)
|
||||
return strings.Replace(r, " ", ":", -1)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user