fix(security): W-02 Secure-Cookie-Flag + W-03 TrustedProxies für X-Forwarded-For
W-02: Cookie Secure-Flag ist nun über config.yml steuerbar.
api.secure_cookies: true/false — default false (kein Breaking Change).
Alle 3 SetCookie-Aufrufe (Login, Logout, TOTP) nutzen s.cfg.SecureCookies.
W-03: remoteIP() ist jetzt eine Methode und prüft api.trusted_proxies.
X-Forwarded-For wird nur ausgewertet wenn der direkte Peer in der
trusted_proxies-Liste steht (IP oder CIDR). Sonst wird r.RemoteAddr
verwendet — kein Spoofing mehr möglich.
Neue Hilfsfunktion: isTrustedProxy(ip, proxies).
config.go: APIConfig um SecureCookies bool + TrustedProxies []string erweitert.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+8
-2
@@ -9,8 +9,14 @@ import (
|
|||||||
|
|
||||||
// APIConfig holds configuration for the HTTP API server.
|
// APIConfig holds configuration for the HTTP API server.
|
||||||
type APIConfig struct {
|
type APIConfig struct {
|
||||||
Bind string `yaml:"bind"`
|
Bind string `yaml:"bind"`
|
||||||
Secret string `yaml:"secret"`
|
Secret string `yaml:"secret"`
|
||||||
|
// SecureCookies sets the Secure flag on session cookies.
|
||||||
|
// Enable when TLS is terminated at this server or at a trusted reverse proxy.
|
||||||
|
SecureCookies bool `yaml:"secure_cookies"`
|
||||||
|
// TrustedProxies is a list of IP addresses or CIDR ranges whose
|
||||||
|
// X-Forwarded-For header is trusted. Empty = trust no proxy (use r.RemoteAddr).
|
||||||
|
TrustedProxies []string `yaml:"trusted_proxies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config is the full application configuration loaded from YAML.
|
// Config is the full application configuration loaded from YAML.
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ func (s *Server) handleExportPDF(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventExport,
|
EventType: audit.EventExport,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
MailID: id,
|
MailID: id,
|
||||||
Detail: "pdf",
|
Detail: "pdf",
|
||||||
Success: true,
|
Success: true,
|
||||||
@@ -551,7 +551,7 @@ func (s *Server) handleExportZIP(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventExport,
|
EventType: audit.EventExport,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Detail: fmt.Sprintf("zip: %d mails", exported),
|
Detail: fmt.Sprintf("zip: %d mails", exported),
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (s *Server) handleSaveLDAP(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "ldap_config_saved",
|
EventType: "ldap_config_saved",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "LDAP-Konfiguration gespeichert",
|
Detail: "LDAP-Konfiguration gespeichert",
|
||||||
})
|
})
|
||||||
@@ -109,7 +109,7 @@ func (s *Server) handleDeleteLDAP(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "ldap_config_deleted",
|
EventType: "ldap_config_deleted",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "LDAP-Konfiguration gelöscht",
|
Detail: "LDAP-Konfiguration gelöscht",
|
||||||
})
|
})
|
||||||
@@ -167,7 +167,7 @@ func (s *Server) handleTestLDAP(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "ldap_connection_test",
|
EventType: "ldap_connection_test",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: result.OK,
|
Success: result.OK,
|
||||||
Detail: result.Message,
|
Detail: result.Message,
|
||||||
})
|
})
|
||||||
@@ -218,7 +218,7 @@ func (s *Server) handleCreateTenant(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "tenant_created",
|
EventType: "tenant_created",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "Mandant erstellt: " + req.Name,
|
Detail: "Mandant erstellt: " + req.Name,
|
||||||
})
|
})
|
||||||
@@ -308,7 +308,7 @@ func (s *Server) handleDeleteTenant(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "tenant_deleted",
|
EventType: "tenant_deleted",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "Mandant gelöscht: " + strconv.FormatInt(id, 10),
|
Detail: "Mandant gelöscht: " + strconv.FormatInt(id, 10),
|
||||||
})
|
})
|
||||||
@@ -469,7 +469,7 @@ func (s *Server) handleSaveTenantLDAP(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "tenant_ldap_config_saved",
|
EventType: "tenant_ldap_config_saved",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "Mandant-LDAP-Konfiguration gespeichert",
|
Detail: "Mandant-LDAP-Konfiguration gespeichert",
|
||||||
})
|
})
|
||||||
@@ -500,7 +500,7 @@ func (s *Server) handleDeleteTenantLDAP(w http.ResponseWriter, r *http.Request)
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "tenant_ldap_config_deleted",
|
EventType: "tenant_ldap_config_deleted",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "Mandant-LDAP-Konfiguration gelöscht",
|
Detail: "Mandant-LDAP-Konfiguration gelöscht",
|
||||||
})
|
})
|
||||||
@@ -539,7 +539,7 @@ func (s *Server) handleTestTenantLDAP(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "tenant_ldap_connection_test",
|
EventType: "tenant_ldap_connection_test",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: result.OK,
|
Success: result.OK,
|
||||||
Detail: result.Message,
|
Detail: result.Message,
|
||||||
})
|
})
|
||||||
@@ -611,7 +611,7 @@ func (s *Server) handleAdminSaveTenantLDAP(w http.ResponseWriter, r *http.Reques
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "tenant_ldap_config_saved",
|
EventType: "tenant_ldap_config_saved",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "Mandant-LDAP-Konfiguration gespeichert (tenant " + strconv.FormatInt(id, 10) + ")",
|
Detail: "Mandant-LDAP-Konfiguration gespeichert (tenant " + strconv.FormatInt(id, 10) + ")",
|
||||||
})
|
})
|
||||||
@@ -643,7 +643,7 @@ func (s *Server) handleAdminDeleteTenantLDAP(w http.ResponseWriter, r *http.Requ
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "tenant_ldap_config_deleted",
|
EventType: "tenant_ldap_config_deleted",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "Mandant-LDAP-Konfiguration gelöscht (tenant " + strconv.FormatInt(id, 10) + ")",
|
Detail: "Mandant-LDAP-Konfiguration gelöscht (tenant " + strconv.FormatInt(id, 10) + ")",
|
||||||
})
|
})
|
||||||
@@ -683,7 +683,7 @@ func (s *Server) handleAdminTestTenantLDAP(w http.ResponseWriter, r *http.Reques
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "tenant_ldap_connection_test",
|
EventType: "tenant_ldap_connection_test",
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: result.OK,
|
Success: result.OK,
|
||||||
Detail: result.Message + " (tenant " + strconv.FormatInt(id, 10) + ")",
|
Detail: result.Message + " (tenant " + strconv.FormatInt(id, 10) + ")",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ func (s *Server) handleChangePassword(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventUserMgmt,
|
EventType: audit.EventUserMgmt,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "change_password",
|
Detail: "change_password",
|
||||||
})
|
})
|
||||||
@@ -143,7 +143,7 @@ func (s *Server) handleChangeEmail(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventUserMgmt,
|
EventType: audit.EventUserMgmt,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "change_email",
|
Detail: "change_email",
|
||||||
})
|
})
|
||||||
|
|||||||
+45
-17
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"math"
|
"math"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -239,7 +240,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventLogin,
|
EventType: audit.EventLogin,
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: false,
|
Success: false,
|
||||||
Detail: "rate limited",
|
Detail: "rate limited",
|
||||||
})
|
})
|
||||||
@@ -249,11 +250,11 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
token, user, totpRequired, err := s.authMgr.Login(req.Username, req.Password)
|
token, user, totpRequired, err := s.authMgr.Login(req.Username, req.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = s.users.RecordLoginAttempt(req.Username, remoteIP(r))
|
_ = s.users.RecordLoginAttempt(req.Username, s.remoteIP(r))
|
||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventLogin,
|
EventType: audit.EventLogin,
|
||||||
Username: req.Username,
|
Username: req.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: false,
|
Success: false,
|
||||||
Detail: classifyLoginError(err),
|
Detail: classifyLoginError(err),
|
||||||
})
|
})
|
||||||
@@ -266,7 +267,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventLogin,
|
EventType: audit.EventLogin,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "totp_pending",
|
Detail: "totp_pending",
|
||||||
})
|
})
|
||||||
@@ -282,7 +283,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventLogin,
|
EventType: audit.EventLogin,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -293,7 +294,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
MaxAge: 8 * 3600,
|
MaxAge: 8 * 3600,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
SameSite: http.SameSiteStrictMode,
|
SameSite: http.SameSiteStrictMode,
|
||||||
// Secure: true — enable when TLS is terminated at this server
|
Secure: s.cfg.SecureCookies,
|
||||||
})
|
})
|
||||||
|
|
||||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||||
@@ -343,13 +344,14 @@ func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) {
|
|||||||
MaxAge: -1,
|
MaxAge: -1,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
SameSite: http.SameSiteStrictMode,
|
SameSite: http.SameSiteStrictMode,
|
||||||
|
Secure: s.cfg.SecureCookies,
|
||||||
})
|
})
|
||||||
|
|
||||||
sess := sessionFromCtx(r.Context())
|
sess := sessionFromCtx(r.Context())
|
||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventLogout,
|
EventType: audit.EventLogout,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -439,7 +441,7 @@ func (s *Server) handleCreateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventUserMgmt,
|
EventType: audit.EventUserMgmt,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Detail: "created user: " + user.Username,
|
Detail: "created user: " + user.Username,
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
@@ -513,7 +515,7 @@ func (s *Server) handleUpdateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventUserMgmt,
|
EventType: audit.EventUserMgmt,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Detail: fmt.Sprintf("updated user %d", id),
|
Detail: fmt.Sprintf("updated user %d", id),
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
@@ -579,7 +581,7 @@ func (s *Server) handleDeleteUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventUserMgmt,
|
EventType: audit.EventUserMgmt,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Detail: fmt.Sprintf(
|
Detail: fmt.Sprintf(
|
||||||
"deleted user %d (%s, role=%s); %d IMAP account(s) removed; emails retained per GoBD",
|
"deleted user %d (%s, role=%s); %d IMAP account(s) removed; emails retained per GoBD",
|
||||||
id, target.Username, target.Role, imapDeleted,
|
id, target.Username, target.Role, imapDeleted,
|
||||||
@@ -693,7 +695,7 @@ func (s *Server) handleSearch(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventSearch,
|
EventType: audit.EventSearch,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Query: q,
|
Query: q,
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
@@ -942,11 +944,37 @@ func classifyLoginError(err error) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func remoteIP(r *http.Request) string {
|
// remoteIP returns the real client IP.
|
||||||
if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
|
// X-Forwarded-For is only trusted when the direct connection comes from a
|
||||||
return strings.TrimSpace(strings.Split(fwd, ",")[0])
|
// configured trusted proxy. Otherwise r.RemoteAddr is used directly to prevent
|
||||||
|
// clients from spoofing their IP in audit logs and rate-limit counters.
|
||||||
|
func (s *Server) remoteIP(r *http.Request) string {
|
||||||
|
directIP, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
if directIP == "" {
|
||||||
|
directIP = r.RemoteAddr
|
||||||
}
|
}
|
||||||
return r.RemoteAddr
|
if len(s.cfg.TrustedProxies) > 0 && isTrustedProxy(directIP, s.cfg.TrustedProxies) {
|
||||||
|
if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
|
||||||
|
return strings.TrimSpace(strings.Split(fwd, ",")[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return directIP
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTrustedProxy checks whether ip is in the list of trusted proxy addresses/CIDRs.
|
||||||
|
func isTrustedProxy(ip string, proxies []string) bool {
|
||||||
|
parsed := net.ParseIP(ip)
|
||||||
|
for _, p := range proxies {
|
||||||
|
if strings.Contains(p, "/") {
|
||||||
|
_, cidr, err := net.ParseCIDR(p)
|
||||||
|
if err == nil && cidr.Contains(parsed) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if p == ip {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Mail access middleware ────────────────────────────────────────────────
|
// ── Mail access middleware ────────────────────────────────────────────────
|
||||||
@@ -1324,7 +1352,7 @@ func (s *Server) handleServiceAction(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "service." + body.Action,
|
EventType: "service." + body.Action,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Detail: name,
|
Detail: name,
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
@@ -1346,7 +1374,7 @@ func (s *Server) handleServiceAction(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: "service." + body.Action,
|
EventType: "service." + body.Action,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Detail: name,
|
Detail: name,
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func (s *Server) handleTOTPSetupPost(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventUserMgmt,
|
EventType: audit.EventUserMgmt,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Detail: "totp_enabled",
|
Detail: "totp_enabled",
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
@@ -156,7 +156,7 @@ func (s *Server) handleTOTPDisable(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventUserMgmt,
|
EventType: audit.EventUserMgmt,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Detail: "totp_disabled",
|
Detail: "totp_disabled",
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
@@ -180,7 +180,7 @@ func (s *Server) handleTOTPLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventLogin,
|
EventType: audit.EventLogin,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: false,
|
Success: false,
|
||||||
Detail: "totp_login_failed: " + err.Error(),
|
Detail: "totp_login_failed: " + err.Error(),
|
||||||
})
|
})
|
||||||
@@ -193,7 +193,7 @@ func (s *Server) handleTOTPLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventLogin,
|
EventType: audit.EventLogin,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Success: true,
|
Success: true,
|
||||||
Detail: "totp_login_completed",
|
Detail: "totp_login_completed",
|
||||||
})
|
})
|
||||||
@@ -205,6 +205,7 @@ func (s *Server) handleTOTPLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
MaxAge: 8 * 3600,
|
MaxAge: 8 * 3600,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
SameSite: http.SameSiteStrictMode,
|
SameSite: http.SameSiteStrictMode,
|
||||||
|
Secure: s.cfg.SecureCookies,
|
||||||
})
|
})
|
||||||
|
|
||||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||||
@@ -253,7 +254,7 @@ func (s *Server) handleTOTPReset(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.audlog.Log(audit.Entry{
|
s.audlog.Log(audit.Entry{
|
||||||
EventType: audit.EventUserMgmt,
|
EventType: audit.EventUserMgmt,
|
||||||
Username: sess.Username,
|
Username: sess.Username,
|
||||||
IPAddress: remoteIP(r),
|
IPAddress: s.remoteIP(r),
|
||||||
Detail: fmt.Sprintf("totp_reset_by_admin: TOTP reset by %s for user %s (id=%d)", sess.Username, target.Username, id),
|
Detail: fmt.Sprintf("totp_reset_by_admin: TOTP reset by %s for user %s (id=%d)", sess.Username, target.Username, id),
|
||||||
Success: true,
|
Success: true,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user