7869ff83ba
* Add support for authenticated static photo URLs, leveraging the NGINX module ngx_http_auth_request. The README is updated with an example NGINX config how to set this up on the proxy side. * In settings.json a new SignedPhoto section is added: not enabled by default. * PhotoURL will append a ?jwt= token to the /static/photos/ path for the current user, which expires after 30 seconds. * When SignedPhoto is enabled, it will enforce that the JWT token is valid and matches the username of the current logged-in user, or else will return with a 403 Forbidden error.
224 lines
5.5 KiB
Go
224 lines
5.5 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/encryption/coldstorage"
|
|
"code.nonshy.com/nonshy/website/pkg/encryption/keygen"
|
|
"code.nonshy.com/nonshy/website/pkg/log"
|
|
"github.com/SherClockHolmes/webpush-go"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// Version of the config format - when new fields are added, it will attempt
|
|
// to write the settings.toml to disk so new defaults populate.
|
|
var currentVersion = 5
|
|
|
|
// Current loaded settings.json
|
|
var Current = DefaultVariable()
|
|
|
|
// Variable configuration attributes (loaded from settings.json).
|
|
type Variable struct {
|
|
Version int
|
|
BaseURL string
|
|
AdminEmail string
|
|
CronAPIKey string
|
|
Mail Mail
|
|
Redis Redis
|
|
Database Database
|
|
BareRTC BareRTC
|
|
Maintenance Maintenance
|
|
Encryption Encryption
|
|
SignedPhoto SignedPhoto
|
|
WebPush WebPush
|
|
Turnstile Turnstile
|
|
UseXForwardedFor bool
|
|
}
|
|
|
|
// DefaultVariable returns the default settings.json data.
|
|
func DefaultVariable() Variable {
|
|
return Variable{
|
|
BaseURL: "http://localhost:8080",
|
|
Mail: Mail{
|
|
Enabled: false,
|
|
Host: "localhost",
|
|
Port: 25,
|
|
From: "no-reply@localhost",
|
|
},
|
|
Redis: Redis{
|
|
Host: "localhost",
|
|
Port: 6379,
|
|
},
|
|
Database: Database{
|
|
SQLite: "database.sqlite",
|
|
Postgres: "host=localhost user=nonshy password=nonshy dbname=nonshy port=5679 sslmode=disable TimeZone=America/Los_Angeles",
|
|
},
|
|
CronAPIKey: uuid.New().String(),
|
|
}
|
|
}
|
|
|
|
// LoadSettings loads the settings.json file or, if not existing, creates it with the default settings.
|
|
func LoadSettings() {
|
|
var writeSettings bool
|
|
|
|
if _, err := os.Stat(SettingsPath); !os.IsNotExist(err) {
|
|
log.Info("Loading settings from %s", SettingsPath)
|
|
content, err := os.ReadFile(SettingsPath)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("LoadSettings: couldn't read settings.json: %s", err))
|
|
}
|
|
|
|
var v Variable
|
|
err = json.Unmarshal(content, &v)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("LoadSettings: couldn't parse settings.json: %s", err))
|
|
}
|
|
|
|
Current = v
|
|
} else {
|
|
WriteSettings()
|
|
log.Warn("NOTICE: Created default settings.json file - review it and configure mail servers and database!")
|
|
}
|
|
|
|
// If there is no DB configured, exit now.
|
|
if !Current.Database.IsSQLite && !Current.Database.IsPostgres {
|
|
log.Error("No database configured in settings.json. Choose SQLite or Postgres and update the DB connector string!")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Initialize the AES encryption key.
|
|
if len(Current.Encryption.AESKey) == 0 {
|
|
log.Warn("NOTICE: rolling a random 32-byte (256-bit) AES encryption key for the settings file")
|
|
aesKey, err := keygen.NewAESKey()
|
|
if err != nil {
|
|
log.Error("Couldn't generate AES key: %s", err)
|
|
os.Exit(1)
|
|
}
|
|
Current.Encryption.AESKey = aesKey
|
|
writeSettings = true
|
|
}
|
|
|
|
// Initialize the cold storage RSA keys.
|
|
if len(Current.Encryption.ColdStorageRSAPublicKey) == 0 {
|
|
x509publicKey, err := coldstorage.Initialize()
|
|
if err != nil {
|
|
log.Error("Initializing cold storage: %s", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Store the public key in the settings.json.
|
|
Current.Encryption.ColdStorageRSAPublicKey = x509publicKey
|
|
writeSettings = true
|
|
}
|
|
|
|
// Initialize the VAPID keys for Web Push Notification.
|
|
if len(Current.WebPush.VAPIDPublicKey) == 0 {
|
|
privateKey, publicKey, err := webpush.GenerateVAPIDKeys()
|
|
if err != nil {
|
|
log.Error("Initializing VAPID keys for Web Push: %s", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
Current.WebPush.VAPIDPrivateKey = privateKey
|
|
Current.WebPush.VAPIDPublicKey = publicKey
|
|
writeSettings = true
|
|
}
|
|
|
|
// Initialize JWT token for SignedPhoto feature.
|
|
if Current.SignedPhoto.JWTSecret == "" {
|
|
Current.SignedPhoto.JWTSecret = uuid.New().String()
|
|
writeSettings = true
|
|
}
|
|
|
|
// Have we added new config fields? Save the settings.json.
|
|
if Current.Version != currentVersion || writeSettings {
|
|
log.Warn("New options are available for your settings.json file. Your settings will be re-saved now.")
|
|
Current.Version = currentVersion
|
|
if err := WriteSettings(); err != nil {
|
|
log.Error("Couldn't write your settings.json file: %s", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// WriteSettings will commit the settings.json to disk.
|
|
func WriteSettings() error {
|
|
log.Error("Note: initial settings.json was written to disk.")
|
|
|
|
var buf bytes.Buffer
|
|
enc := json.NewEncoder(&buf)
|
|
enc.SetIndent("", " ")
|
|
err := enc.Encode(Current)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("WriteSettings: couldn't marshal settings: %s", err))
|
|
}
|
|
|
|
return os.WriteFile(SettingsPath, buf.Bytes(), 0600)
|
|
}
|
|
|
|
// Mail settings.
|
|
type Mail struct {
|
|
Enabled bool
|
|
Host string // localhost
|
|
Port int // 25
|
|
From string // noreply@localhost
|
|
Username string // SMTP credentials
|
|
Password string
|
|
}
|
|
|
|
// Redis settings.
|
|
type Redis struct {
|
|
Host string
|
|
Port int
|
|
DB int
|
|
}
|
|
|
|
// Database settings.
|
|
type Database struct {
|
|
IsSQLite bool
|
|
IsPostgres bool
|
|
SQLite string
|
|
Postgres string
|
|
}
|
|
|
|
// BareRTC chat room settings.
|
|
type BareRTC struct {
|
|
JWTSecret string
|
|
URL string
|
|
}
|
|
|
|
// Maintenance mode settings.
|
|
type Maintenance struct {
|
|
PauseSignup bool
|
|
PauseLogin bool
|
|
PauseChat bool
|
|
PauseInteraction bool
|
|
}
|
|
|
|
// Encryption settings.
|
|
type Encryption struct {
|
|
AESKey []byte
|
|
ColdStorageRSAPublicKey []byte
|
|
}
|
|
|
|
// SignedPhoto settings.
|
|
type SignedPhoto struct {
|
|
Enabled bool
|
|
JWTSecret string
|
|
}
|
|
|
|
// WebPush settings.
|
|
type WebPush struct {
|
|
VAPIDPublicKey string
|
|
VAPIDPrivateKey string
|
|
}
|
|
|
|
// Turnstile (Cloudflare CAPTCHA) settings.
|
|
type Turnstile struct {
|
|
Enabled bool
|
|
SiteKey string
|
|
SecretKey string
|
|
}
|