whitelist configs moved to it's own yml file

This commit is contained in:
0ceanSlim 2024-10-16 09:57:58 -04:00
parent 9df03646db
commit 5133c3a005
13 changed files with 226 additions and 178 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
/tmp
config.yml
whitelist.yml
relay_metadata.json
grain.exe
/build

View File

@ -63,19 +63,6 @@ rate_limit:
limit: 25
burst: 50
pubkey_whitelist:
enabled: false
pubkeys: [] # List of allowed public keys
npubs: [] # List of allowed npubs (Nostr public keys in bech32 format)
kind_whitelist:
enabled: false
kinds: [] # List of allowed event kinds
domain_whitelist:
enabled: false
domains: [] # List of allowed domains
blacklist: #Removing a pubkey from the Blacklist requires a hard restart; Blacklist overides the Whitelist
enabled: true
permanent_ban_words: [] # Words that trigger a permanent ban

View File

@ -0,0 +1,20 @@
pubkey_whitelist:
enabled: false
pubkeys:
- pubkey1
- pubkey2
npubs:
- npub18ls2km9aklhzw9yzqgjfu0anhz2z83hkeknw7sl22ptu8kfs3rjq54am44
- npub2
kind_whitelist:
enabled: false
kinds:
- "1"
- "2"
domain_whitelist:
enabled: false
domains:
- "example.com"
- "anotherdomain.com"

View File

@ -6,52 +6,50 @@ import (
"strconv"
)
// Helper function to check if a pubkey or npub is whitelisted
// Check if a pubkey or npub is whitelisted
func IsPubKeyWhitelisted(pubKey string) bool {
cfg := GetConfig()
if !cfg.PubkeyWhitelist.Enabled {
return true
}
cfg := GetWhitelistConfig()
if !cfg.PubkeyWhitelist.Enabled {
return true
}
// Check pubkeys
for _, whitelistedKey := range cfg.PubkeyWhitelist.Pubkeys {
if pubKey == whitelistedKey {
return true
}
}
for _, whitelistedKey := range cfg.PubkeyWhitelist.Pubkeys {
if pubKey == whitelistedKey {
return true
}
}
// Check npubs
for _, npub := range cfg.PubkeyWhitelist.Npubs {
decodedPubKey, err := utils.DecodeNpub(npub)
if err != nil {
fmt.Println("Error decoding npub:", err)
continue
}
if pubKey == decodedPubKey {
return true
}
}
for _, npub := range cfg.PubkeyWhitelist.Npubs {
decodedPubKey, err := utils.DecodeNpub(npub)
if err != nil {
fmt.Println("Error decoding npub:", err)
continue
}
if pubKey == decodedPubKey {
return true
}
}
return false
return false
}
// Check if a kind is whitelisted
func IsKindWhitelisted(kind int) bool {
cfg := GetConfig()
if !cfg.KindWhitelist.Enabled {
return true
}
cfg := GetWhitelistConfig()
if !cfg.KindWhitelist.Enabled {
return true
}
// Check event kinds
for _, whitelistedKindStr := range cfg.KindWhitelist.Kinds {
whitelistedKind, err := strconv.Atoi(whitelistedKindStr)
if err != nil {
fmt.Println("Error converting whitelisted kind to int:", err)
continue
}
if kind == whitelistedKind {
return true
}
}
for _, whitelistedKindStr := range cfg.KindWhitelist.Kinds {
whitelistedKind, err := strconv.Atoi(whitelistedKindStr)
if err != nil {
fmt.Println("Error converting whitelisted kind to int:", err)
continue
}
if kind == whitelistedKind {
return true
}
}
return false
}
return false
}

View File

@ -10,29 +10,56 @@ import (
)
var (
cfg *configTypes.ServerConfig
once sync.Once
cfg *configTypes.ServerConfig
whitelistCfg *configTypes.WhitelistConfig
once sync.Once
whitelistOnce sync.Once
)
// LoadConfig loads the server configuration from config.yml
func LoadConfig(filename string) (*configTypes.ServerConfig, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
data, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
var config configTypes.ServerConfig
err = yaml.Unmarshal(data, &config)
if err != nil {
return nil, err
}
var config configTypes.ServerConfig
err = yaml.Unmarshal(data, &config)
if err != nil {
return nil, err
}
once.Do(func() {
cfg = &config
})
once.Do(func() {
cfg = &config
})
return cfg, nil
return cfg, nil
}
// LoadWhitelistConfig loads the whitelist configuration from whitelist.yml
func LoadWhitelistConfig(filename string) (*configTypes.WhitelistConfig, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
var config configTypes.WhitelistConfig
err = yaml.Unmarshal(data, &config)
if err != nil {
return nil, err
}
whitelistOnce.Do(func() {
whitelistCfg = &config
})
return whitelistCfg, nil
}
func GetConfig() *configTypes.ServerConfig {
return cfg
return cfg
}
func GetWhitelistConfig() *configTypes.WhitelistConfig {
return whitelistCfg
}

View File

@ -13,12 +13,9 @@ type ServerConfig struct {
MaxConnections int `yaml:"max_connections"` // Maximum number of concurrent connections
MaxSubscriptionsPerClient int `yaml:"max_subscriptions_per_client"` // Maximum number of subscriptions per client
} `yaml:"server"`
RateLimit RateLimitConfig `yaml:"rate_limit"`
PubkeyWhitelist PubkeyWhitelistConfig `yaml:"pubkey_whitelist"`
KindWhitelist KindWhitelistConfig `yaml:"kind_whitelist"`
DomainWhitelist DomainWhitelistConfig `yaml:"domain_whitelist"`
Blacklist BlacklistConfig `yaml:"blacklist"`
ResourceLimits ResourceLimits `yaml:"resource_limits"`
Auth AuthConfig `yaml:"auth"`
EventPurge EventPurgeConfig `yaml:"event_purge"`
RateLimit RateLimitConfig `yaml:"rate_limit"`
Blacklist BlacklistConfig `yaml:"blacklist"`
ResourceLimits ResourceLimits `yaml:"resource_limits"`
Auth AuthConfig `yaml:"auth"`
EventPurge EventPurgeConfig `yaml:"event_purge"`
}

View File

@ -0,0 +1,19 @@
package config
type WhitelistConfig struct {
PubkeyWhitelist struct {
Enabled bool `yaml:"enabled"`
Pubkeys []string `yaml:"pubkeys"`
Npubs []string `yaml:"npubs"`
} `yaml:"pubkey_whitelist"`
KindWhitelist struct {
Enabled bool `yaml:"enabled"`
Kinds []string `yaml:"kinds"`
} `yaml:"kind_whitelist"`
DomainWhitelist struct {
Enabled bool `yaml:"enabled"`
Domains []string `yaml:"domains"`
} `yaml:"domain_whitelist"`
}

View File

@ -1,6 +0,0 @@
package config
type DomainWhitelistConfig struct {
Enabled bool `yaml:"enabled"`
Domains []string `yaml:"domains"`
}

View File

@ -1,6 +0,0 @@
package config
type KindWhitelistConfig struct {
Enabled bool `yaml:"enabled"`
Kinds []string `yaml:"kinds"`
}

View File

@ -1,7 +0,0 @@
package config
type PubkeyWhitelistConfig struct {
Enabled bool `yaml:"enabled"`
Pubkeys []string `yaml:"pubkeys"`
Npubs []string `yaml:"npubs"`
}

94
main.go
View File

@ -21,64 +21,64 @@ import (
)
func main() {
utils.EnsureFileExists("config.yml", "app/static/examples/config.example.yml")
utils.EnsureFileExists("relay_metadata.json", "app/static/examples/relay_metadata.example.json")
utils.EnsureFileExists("config.yml", "app/static/examples/config.example.yml")
utils.EnsureFileExists("whitelist.yml", "app/static/examples/whitelist.example.yml")
utils.EnsureFileExists("relay_metadata.json", "app/static/examples/relay_metadata.example.json")
restartChan := make(chan struct{})
go config.WatchConfigFile("config.yml", restartChan) // Critical goroutine
restartChan := make(chan struct{})
go config.WatchConfigFile("config.yml", restartChan)
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
var wg sync.WaitGroup
for {
wg.Add(1) // Add to WaitGroup for the server goroutine
var wg sync.WaitGroup
for {
wg.Add(1)
cfg, err := config.LoadConfig("config.yml")
if err != nil {
log.Fatal("Error loading config: ", err)
}
cfg, err := config.LoadConfig("config.yml")
if err != nil {
log.Fatal("Error loading config: ", err)
}
// Start event purging in the background
go mongo.ScheduleEventPurging(cfg)
_, err = config.LoadWhitelistConfig("whitelist.yml")
if err != nil {
log.Fatal("Error loading whitelist config: ", err)
}
config.SetResourceLimit(&cfg.ResourceLimits) // Apply limits once before starting the server
go mongo.ScheduleEventPurging(cfg)
client, err := mongo.InitDB(cfg)
if err != nil {
log.Fatal("Error initializing database: ", err)
}
config.SetResourceLimit(&cfg.ResourceLimits)
client, err := mongo.InitDB(cfg)
if err != nil {
log.Fatal("Error initializing database: ", err)
}
config.SetRateLimit(cfg)
config.SetSizeLimit(cfg)
config.SetRateLimit(cfg)
config.SetSizeLimit(cfg)
config.ClearTemporaryBans()
config.ClearTemporaryBans()
err = utils.LoadRelayMetadataJSON()
if err != nil {
log.Fatal("Failed to load relay metadata: ", err)
}
err = utils.LoadRelayMetadataJSON()
if err != nil {
log.Fatal("Failed to load relay metadata: ", err)
}
mux := setupRoutes()
server := startServer(cfg, mux, &wg)
mux := setupRoutes()
// Start the server
server := startServer(cfg, mux, &wg)
select {
case <-restartChan:
log.Println("Restarting server...")
server.Close() // Stop the current server instance
wg.Wait() // Wait for the server goroutine to finish
time.Sleep(3 * time.Second)
case <-signalChan:
log.Println("Shutting down server...")
server.Close() // Stop the server
mongo.DisconnectDB(client) // Disconnect from MongoDB
wg.Wait() // Wait for all goroutines to finish
return
}
}
select {
case <-restartChan:
log.Println("Restarting server...")
server.Close()
wg.Wait()
time.Sleep(3 * time.Second)
case <-signalChan:
log.Println("Shutting down server...")
server.Close()
mongo.DisconnectDB(client)
wg.Wait()
return
}
}
}
func setupRoutes() *http.ServeMux {

View File

@ -2,6 +2,7 @@ package mongo
import (
"context"
"grain/config"
types "grain/config/types"
"grain/server/utils"
"log"
@ -10,6 +11,7 @@ import (
"go.mongodb.org/mongo-driver/bson"
)
// PurgeOldEvents removes old events based on the configuration and a list of whitelisted pubkeys.
func PurgeOldEvents(cfg *types.EventPurgeConfig, whitelist []string) {
if !cfg.Enabled {
return
@ -21,10 +23,12 @@ func PurgeOldEvents(cfg *types.EventPurgeConfig, whitelist []string) {
// Calculate the cutoff time
cutoff := time.Now().AddDate(0, 0, -cfg.KeepDurationDays).Unix()
// Create the filter for purging old events
filter := bson.M{
"created_at": bson.M{"$lt": cutoff}, // Filter older events
}
// Exclude whitelisted pubkeys if specified in the config
if cfg.ExcludeWhitelisted && len(whitelist) > 0 {
filter["pubkey"] = bson.M{"$nin": whitelist} // Exclude whitelisted pubkeys
}
@ -52,7 +56,6 @@ func PurgeOldEvents(cfg *types.EventPurgeConfig, whitelist []string) {
}
}
// Example of a periodic purging task
// ScheduleEventPurging runs the event purging at a configurable interval.
func ScheduleEventPurging(cfg *types.ServerConfig) {
// Use the purge interval from the configuration
@ -61,26 +64,37 @@ func ScheduleEventPurging(cfg *types.ServerConfig) {
defer ticker.Stop()
for range ticker.C {
whitelist := getWhitelistedPubKeys(cfg)
// Fetch the whitelisted pubkeys without passing cfg directly
whitelist := getWhitelistedPubKeys()
PurgeOldEvents(&cfg.EventPurge, whitelist)
log.Printf("Purged old events, keeping whitelisted pubkeys: %v", whitelist)
}
}
// Fetch whitelisted pubkeys from both the config and any additional domains.
func getWhitelistedPubKeys(cfg *types.ServerConfig) []string {
whitelistedPubkeys := cfg.PubkeyWhitelist.Pubkeys
// Fetch whitelisted pubkeys from both the whitelist config and any additional domains.
func getWhitelistedPubKeys() []string {
// Get the whitelist configuration
whitelistCfg := config.GetWhitelistConfig()
if whitelistCfg == nil {
log.Println("whitelistCfg is nil, returning an empty list of whitelisted pubkeys.")
return []string{}
}
// Start with the statically defined pubkeys
whitelistedPubkeys := whitelistCfg.PubkeyWhitelist.Pubkeys
// Fetch pubkeys from domains if domain whitelist is enabled
if cfg.DomainWhitelist.Enabled {
domains := cfg.DomainWhitelist.Domains
if whitelistCfg.DomainWhitelist.Enabled {
domains := whitelistCfg.DomainWhitelist.Domains
pubkeys, err := utils.FetchPubkeysFromDomains(domains)
if err != nil {
log.Printf("Error fetching pubkeys from domains: %v", err)
return whitelistedPubkeys // Return existing whitelisted pubkeys in case of error
// Return the existing statically whitelisted pubkeys in case of an error
return whitelistedPubkeys
}
// Append fetched pubkeys from domains to the whitelisted pubkeys
whitelistedPubkeys = append(whitelistedPubkeys, pubkeys...)
}
return whitelistedPubkeys
}
}

View File

@ -68,37 +68,41 @@ func HandleEvent(ws *websocket.Conn, message []interface{}) {
}
func handleBlacklistAndWhitelist(ws *websocket.Conn, evt nostr.Event) bool {
if config.GetConfig().DomainWhitelist.Enabled {
domains := config.GetConfig().DomainWhitelist.Domains
pubkeys, err := utils.FetchPubkeysFromDomains(domains)
if err != nil {
fmt.Println("Error fetching pubkeys from domains:", err)
response.SendNotice(ws, "", "Error fetching pubkeys from domains")
return false
}
for _, pubkey := range pubkeys {
config.GetConfig().PubkeyWhitelist.Pubkeys = append(config.GetConfig().PubkeyWhitelist.Pubkeys, pubkey)
}
}
whitelistCfg := config.GetWhitelistConfig()
if blacklisted, msg := config.CheckBlacklist(evt.PubKey, evt.Content); blacklisted {
response.SendOK(ws, evt.ID, false, msg)
return false
}
// If domain whitelisting is enabled, dynamically fetch pubkeys from domains
if whitelistCfg.DomainWhitelist.Enabled {
domains := whitelistCfg.DomainWhitelist.Domains
pubkeys, err := utils.FetchPubkeysFromDomains(domains)
if err != nil {
fmt.Println("Error fetching pubkeys from domains:", err)
response.SendNotice(ws, "", "Error fetching pubkeys from domains")
return false
}
// Update the whitelisted pubkeys dynamically
whitelistCfg.PubkeyWhitelist.Pubkeys = append(whitelistCfg.PubkeyWhitelist.Pubkeys, pubkeys...)
}
if config.GetConfig().KindWhitelist.Enabled && !config.IsKindWhitelisted(evt.Kind) {
response.SendOK(ws, evt.ID, false, "not allowed: event kind is not whitelisted")
return false
}
// Check if the event's pubkey or content is blacklisted
if blacklisted, msg := config.CheckBlacklist(evt.PubKey, evt.Content); blacklisted {
response.SendOK(ws, evt.ID, false, msg)
return false
}
if config.GetConfig().PubkeyWhitelist.Enabled && !config.IsPubKeyWhitelisted(evt.PubKey) {
response.SendOK(ws, evt.ID, false, "not allowed: pubkey or npub is not whitelisted")
return false
}
// Check if the event's kind is whitelisted
if whitelistCfg.KindWhitelist.Enabled && !config.IsKindWhitelisted(evt.Kind) {
response.SendOK(ws, evt.ID, false, "not allowed: event kind is not whitelisted")
return false
}
return true
// Check if the event's pubkey is whitelisted
if whitelistCfg.PubkeyWhitelist.Enabled && !config.IsPubKeyWhitelisted(evt.PubKey) {
response.SendOK(ws, evt.ID, false, "not allowed: pubkey or npub is not whitelisted")
return false
}
return true
}
func handleRateAndSizeLimits(ws *websocket.Conn, evt nostr.Event, eventSize int) bool {
rateLimiter := config.GetRateLimiter()
sizeLimiter := config.GetSizeLimiter()