mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-06-09 03:42:25 +03:00
use a key loader function to move file reading out of auth
This commit is contained in:
parent
be26ace545
commit
27997bcdf6
104
auth.go
104
auth.go
@ -1,14 +1,12 @@
|
|||||||
package sshchat
|
package sshchat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -18,6 +16,10 @@ import (
|
|||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// KeyLoader loads public keys, e.g. from an authorized_keys file.
|
||||||
|
// It must return a nil slice on error.
|
||||||
|
type KeyLoader func() ([]ssh.PublicKey, error)
|
||||||
|
|
||||||
// ErrNotAllowlisted Is the error returned when a key is checked that is not allowlisted,
|
// ErrNotAllowlisted Is the error returned when a key is checked that is not allowlisted,
|
||||||
// when allowlisting is enabled.
|
// when allowlisting is enabled.
|
||||||
var ErrNotAllowlisted = errors.New("not allowlisted")
|
var ErrNotAllowlisted = errors.New("not allowlisted")
|
||||||
@ -53,16 +55,17 @@ func newAuthAddr(addr net.Addr) string {
|
|||||||
// Auth stores lookups for bans, allowlists, and ops. It implements the sshd.Auth interface.
|
// Auth stores lookups for bans, allowlists, and ops. It implements the sshd.Auth interface.
|
||||||
// If the contained passphrase is not empty, it complements a allowlist.
|
// If the contained passphrase is not empty, it complements a allowlist.
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
passphraseHash []byte
|
passphraseHash []byte
|
||||||
allowlistModeMu sync.RWMutex
|
bannedAddr *set.Set
|
||||||
|
bannedClient *set.Set
|
||||||
|
banned *set.Set
|
||||||
|
allowlist *set.Set
|
||||||
|
ops *set.Set
|
||||||
|
|
||||||
|
settingsMu sync.RWMutex
|
||||||
allowlistMode bool
|
allowlistMode bool
|
||||||
bannedAddr *set.Set
|
opLoader KeyLoader
|
||||||
bannedClient *set.Set
|
allowlistLoader KeyLoader
|
||||||
banned *set.Set
|
|
||||||
allowlist *set.Set
|
|
||||||
ops *set.Set
|
|
||||||
opFile string
|
|
||||||
allowlistFile string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAuth creates a new empty Auth.
|
// NewAuth creates a new empty Auth.
|
||||||
@ -77,14 +80,14 @@ func NewAuth() *Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Auth) AllowlistMode() bool {
|
func (a *Auth) AllowlistMode() bool {
|
||||||
a.allowlistModeMu.RLock()
|
a.settingsMu.RLock()
|
||||||
defer a.allowlistModeMu.RUnlock()
|
defer a.settingsMu.RUnlock()
|
||||||
return a.allowlistMode
|
return a.allowlistMode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Auth) SetAllowlistMode(value bool) {
|
func (a *Auth) SetAllowlistMode(value bool) {
|
||||||
a.allowlistModeMu.Lock()
|
a.settingsMu.Lock()
|
||||||
defer a.allowlistModeMu.Unlock()
|
defer a.settingsMu.Unlock()
|
||||||
a.allowlistMode = value
|
a.allowlistMode = value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,10 +177,19 @@ func (a *Auth) IsOp(key ssh.PublicKey) bool {
|
|||||||
return a.ops.In(authkey)
|
return a.ops.In(authkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadOpsFromFile reads a file in authorized_keys format and makes public keys operators
|
// LoadOps sets the public keys form loader to operators and saves the loader for later use
|
||||||
func (a *Auth) LoadOpsFromFile(path string) error {
|
func (a *Auth) LoadOps(loader KeyLoader) error {
|
||||||
a.opFile = path
|
a.settingsMu.Lock()
|
||||||
return fromFile(path, func(key ssh.PublicKey) { a.Op(key, 0) })
|
a.opLoader = loader
|
||||||
|
a.settingsMu.Unlock()
|
||||||
|
return a.ReloadOps()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReloadOps sets the public keys from a loader saved in the last call to operators
|
||||||
|
func (a *Auth) ReloadOps() error {
|
||||||
|
a.settingsMu.RLock()
|
||||||
|
defer a.settingsMu.RUnlock()
|
||||||
|
return addFromLoader(a.opLoader, a.Op)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allowlist will set a public key as a allowlisted user.
|
// Allowlist will set a public key as a allowlisted user.
|
||||||
@ -199,10 +211,30 @@ func (a *Auth) Allowlist(key ssh.PublicKey, d time.Duration) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadAllowlistFromFile reads a file in authorized_keys format and allowlists public keys
|
// LoadAllowlist adds the public keys from the loader to the allowlist and saves the loader for later use
|
||||||
func (a *Auth) LoadAllowlistFromFile(path string) error {
|
func (a *Auth) LoadAllowlist(loader KeyLoader) error {
|
||||||
a.allowlistFile = path
|
a.settingsMu.Lock()
|
||||||
return fromFile(path, func(key ssh.PublicKey) { a.Allowlist(key, 0) })
|
a.allowlistLoader = loader
|
||||||
|
a.settingsMu.Unlock()
|
||||||
|
return a.ReloadAllowlist()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadAllowlist adds the public keys from a loader saved in a previous call to the allowlist
|
||||||
|
func (a *Auth) ReloadAllowlist() error {
|
||||||
|
a.settingsMu.RLock()
|
||||||
|
defer a.settingsMu.RUnlock()
|
||||||
|
return addFromLoader(a.allowlistLoader, a.Allowlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFromLoader(loader KeyLoader, adder func(ssh.PublicKey, time.Duration)) error {
|
||||||
|
if loader == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
keys, err := loader()
|
||||||
|
for _, key := range keys {
|
||||||
|
adder(key, 0)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ban will set a public key as banned.
|
// Ban will set a public key as banned.
|
||||||
@ -309,29 +341,3 @@ func (a *Auth) BanQuery(q string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromFile(path string, handler func(ssh.PublicKey)) error {
|
|
||||||
if path == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
for scanner.Scan() {
|
|
||||||
key, _, _, _, err := ssh.ParseAuthorizedKey(scanner.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "ssh: no key found" {
|
|
||||||
// TODO: do we really want to always ignore this?
|
|
||||||
continue // Skip line
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
handler(key)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
"github.com/alexcesaro/log"
|
"github.com/alexcesaro/log"
|
||||||
"github.com/alexcesaro/log/golog"
|
"github.com/alexcesaro/log/golog"
|
||||||
flags "github.com/jessevdk/go-flags"
|
flags "github.com/jessevdk/go-flags"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
sshchat "github.com/shazow/ssh-chat"
|
sshchat "github.com/shazow/ssh-chat"
|
||||||
"github.com/shazow/ssh-chat/chat"
|
"github.com/shazow/ssh-chat/chat"
|
||||||
@ -139,12 +141,12 @@ func main() {
|
|||||||
auth.SetPassphrase(options.Passphrase)
|
auth.SetPassphrase(options.Passphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = auth.LoadOpsFromFile(options.Admin)
|
err = auth.LoadOps(loaderFromFile(options.Admin, logger))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fail(5, "Failed to load admins: %v\n", err)
|
fail(5, "Failed to load admins: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = auth.LoadAllowlistFromFile(options.Allowlist)
|
err = auth.LoadAllowlist(loaderFromFile(options.Allowlist, logger))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fail(6, "Failed to load allowlist: %v\n", err)
|
fail(6, "Failed to load allowlist: %v\n", err)
|
||||||
}
|
}
|
||||||
@ -188,3 +190,33 @@ func main() {
|
|||||||
<-sig // Wait for ^C signal
|
<-sig // Wait for ^C signal
|
||||||
fmt.Fprintln(os.Stderr, "Interrupt signal detected, shutting down.")
|
fmt.Fprintln(os.Stderr, "Interrupt signal detected, shutting down.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loaderFromFile(path string, logger *golog.Logger) sshchat.KeyLoader {
|
||||||
|
if path == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return func() ([]ssh.PublicKey, error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var keys []ssh.PublicKey
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
key, _, _, _, err := ssh.ParseAuthorizedKey(scanner.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() == "ssh: no key found" {
|
||||||
|
continue // Skip line
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
if keys == nil {
|
||||||
|
logger.Warning("file", path, "contained no keys")
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2
host.go
2
host.go
@ -798,7 +798,7 @@ func (h *Host) InitCommands(c *chat.Commands) {
|
|||||||
if args[0] == "flush" {
|
if args[0] == "flush" {
|
||||||
h.auth.allowlist.Clear()
|
h.auth.allowlist.Clear()
|
||||||
}
|
}
|
||||||
return h.auth.LoadAllowlistFromFile(h.auth.allowlistFile)
|
return h.auth.ReloadAllowlist()
|
||||||
}
|
}
|
||||||
|
|
||||||
allowlistReverify := func(room *chat.Room) []string {
|
allowlistReverify := func(room *chat.Room) []string {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user