feat(auth): сервис аккаунтов и аутентификации Фазы 1
- регистрация: политика пароля (≥10 + локальный словарь 10k утечек, SecLists MIT), username и email без учёта регистра, шифрование email с привязкой к аккаунту, blind index для поиска, автовступление в главный сервер с ролью @user - вход: Argon2id+pepper, TOTP при включённом 2FA, блокировка после серии неудач, события безопасности; единый ответ на неверный пароль и неизвестный аккаунт - сессии: opaque-токены (в БД только SHA-256), ротация токена, logout/logout-all, отзыв остальных сессий при смене пароля, step-up с окном 10 минут - 2FA: настройка секрета с QR-URL, подтверждение кодом, 8 резервных кодов (хранятся хэшами), обязательность для инстанс-админов, запрет отключения - тесты на реальной SQLite: регистрация/вход/дубликаты, слабые и утёкшие пароли, ротация и logout-all, TOTP и резервные коды, step-up, смена пароля, выключенная регистрация, отсутствие секретов в логах
This commit is contained in:
+39
-24
@@ -29,34 +29,49 @@ type Config struct {
|
||||
ReadPoolSize int
|
||||
MaxUploadSize int64
|
||||
TLSEnabled bool
|
||||
Version string
|
||||
Commit string
|
||||
BuildDate string
|
||||
ShutdownTTL time.Duration
|
||||
Maintenance time.Duration
|
||||
// Секреты инстанса (генерируются установщиком, AGENT.md §10.3).
|
||||
SessionPepper string
|
||||
MasterKey string
|
||||
TOTPEncryptionKey string
|
||||
SessionTTLHours int
|
||||
Argon2MemoryKiB int
|
||||
Argon2Iterations int
|
||||
Argon2Parallelism int
|
||||
Version string
|
||||
Commit string
|
||||
BuildDate string
|
||||
ShutdownTTL time.Duration
|
||||
Maintenance time.Duration
|
||||
}
|
||||
|
||||
func Load() (Config, error) {
|
||||
cfg := Config{
|
||||
Domain: env("DOMAIN", "localhost"),
|
||||
FilesDomain: env("FILES_DOMAIN", ""),
|
||||
InstanceName: env("INSTANCE_NAME", "glchat"),
|
||||
ListenAddr: env("LISTEN_ADDR", DefaultListenAddr),
|
||||
DataDir: env("DATA_DIR", DefaultDataDir),
|
||||
WebRoot: env("WEB_ROOT", "/app/web"),
|
||||
DatabasePath: env("DATABASE_PATH", ""),
|
||||
SQLiteCache: env("SQLITE_CACHE_SIZE", "-8000"),
|
||||
SQLiteMmap: env("SQLITE_MMAP_SIZE", "134217728"),
|
||||
LogLevel: strings.ToLower(env("LOG_LEVEL", DefaultLogLevel)),
|
||||
LogFormat: strings.ToLower(env("LOG_FORMAT", "json")),
|
||||
ReadPoolSize: envInt("SQLITE_READ_POOL", 4),
|
||||
MaxUploadSize: envInt64("MAX_UPLOAD_SIZE", 26214400),
|
||||
TLSEnabled: envBool("TLS_ENABLED", true),
|
||||
Version: env("APP_VERSION", "dev"),
|
||||
Commit: env("APP_COMMIT", "none"),
|
||||
BuildDate: env("APP_BUILD_DATE", "unknown"),
|
||||
ShutdownTTL: time.Duration(envInt("SHUTDOWN_TIMEOUT_SECONDS", 20)) * time.Second,
|
||||
Maintenance: time.Duration(envInt("DB_MAINTENANCE_INTERVAL_SECONDS", 21600)) * time.Second,
|
||||
Domain: env("DOMAIN", "localhost"),
|
||||
FilesDomain: env("FILES_DOMAIN", ""),
|
||||
InstanceName: env("INSTANCE_NAME", "glchat"),
|
||||
ListenAddr: env("LISTEN_ADDR", DefaultListenAddr),
|
||||
DataDir: env("DATA_DIR", DefaultDataDir),
|
||||
WebRoot: env("WEB_ROOT", "/app/web"),
|
||||
DatabasePath: env("DATABASE_PATH", ""),
|
||||
SQLiteCache: env("SQLITE_CACHE_SIZE", "-8000"),
|
||||
SQLiteMmap: env("SQLITE_MMAP_SIZE", "134217728"),
|
||||
LogLevel: strings.ToLower(env("LOG_LEVEL", DefaultLogLevel)),
|
||||
LogFormat: strings.ToLower(env("LOG_FORMAT", "json")),
|
||||
ReadPoolSize: envInt("SQLITE_READ_POOL", 4),
|
||||
MaxUploadSize: envInt64("MAX_UPLOAD_SIZE", 26214400),
|
||||
TLSEnabled: envBool("TLS_ENABLED", true),
|
||||
SessionPepper: env("SESSION_PEPPER", ""),
|
||||
MasterKey: env("MASTER_KEY", ""),
|
||||
TOTPEncryptionKey: env("TOTP_ENCRYPTION_KEY", ""),
|
||||
SessionTTLHours: envInt("SESSION_TTL_HOURS", 720),
|
||||
Argon2MemoryKiB: envInt("ARGON2_MEMORY_KIB", 19456),
|
||||
Argon2Iterations: envInt("ARGON2_ITERATIONS", 2),
|
||||
Argon2Parallelism: envInt("ARGON2_PARALLELISM", 1),
|
||||
Version: env("APP_VERSION", "dev"),
|
||||
Commit: env("APP_COMMIT", "none"),
|
||||
BuildDate: env("APP_BUILD_DATE", "unknown"),
|
||||
ShutdownTTL: time.Duration(envInt("SHUTDOWN_TIMEOUT_SECONDS", 20)) * time.Second,
|
||||
Maintenance: time.Duration(envInt("DB_MAINTENANCE_INTERVAL_SECONDS", 21600)) * time.Second,
|
||||
}
|
||||
if cfg.FilesDomain == "" {
|
||||
cfg.FilesDomain = "files." + cfg.Domain
|
||||
|
||||
Reference in New Issue
Block a user