Skip to content

Commit

Permalink
home: fix login attempt logging
Browse files Browse the repository at this point in the history
  • Loading branch information
ainar-g committed Apr 11, 2023
1 parent 6da7392 commit 35a649f
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 16 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ In this release, the schema version has changed from 17 to 20.
- The `POST /control/querylog_config` HTTP API; use the new `PUT
/control/querylog/config/update` API instead.

### Fixed

- Logging of the client's IP address after failed login attempts ([#5701]).

[#1163]: https://github.com/AdguardTeam/AdGuardHome/issues/1163
[#1333]: https://github.com/AdguardTeam/AdGuardHome/issues/1333
[#1163]: https://github.com/AdguardTeam/AdGuardHome/issues/1717
Expand All @@ -142,6 +146,7 @@ In this release, the schema version has changed from 17 to 20.
[#4262]: https://github.com/AdguardTeam/AdGuardHome/issues/4262
[#3290]: https://github.com/AdguardTeam/AdGuardHome/issues/4299
[#5567]: https://github.com/AdguardTeam/AdGuardHome/issues/5567
[#5701]: https://github.com/AdguardTeam/AdGuardHome/issues/5701

[rfc6761]: https://www.rfc-editor.org/rfc/rfc6761

Expand Down
54 changes: 39 additions & 15 deletions internal/home/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,21 @@ func realIP(r *http.Request) (ip net.IP, err error) {
return net.ParseIP(ipStr), nil
}

// writeErrorWithIP is like [aghhttp.Error], but when it includes the client IP
// address when writes to the log.
func writeErrorWithIP(
r *http.Request,
w http.ResponseWriter,
code int,
remoteIP string,
format string,
args ...any,
) {
text := fmt.Sprintf(format, args...)
log.Error("%s %s %s: from ip %s: %s", r.Method, r.Host, r.URL, remoteIP, text)
http.Error(w, text, code)
}

func handleLogin(w http.ResponseWriter, r *http.Request) {
req := loginJSON{}
err := json.NewDecoder(r.Body).Decode(&req)
Expand All @@ -421,42 +436,53 @@ func handleLogin(w http.ResponseWriter, r *http.Request) {
return
}

var remoteAddr string
var remoteIP string
// realIP cannot be used here without taking TrustedProxies into account due
// to security issues.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2799.
//
// TODO(e.burkov): Use realIP when the issue will be fixed.
if remoteAddr, err = netutil.SplitHost(r.RemoteAddr); err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "auth: getting remote address: %s", err)
if remoteIP, err = netutil.SplitHost(r.RemoteAddr); err != nil {
writeErrorWithIP(
r,
w,
http.StatusBadRequest,
r.RemoteAddr,
"auth: getting remote address: %s",
err,
)

return
}

if rateLimiter := Context.auth.raleLimiter; rateLimiter != nil {
if left := rateLimiter.check(remoteAddr); left > 0 {
if left := rateLimiter.check(remoteIP); left > 0 {
w.Header().Set(httphdr.RetryAfter, strconv.Itoa(int(left.Seconds())))
aghhttp.Error(r, w, http.StatusTooManyRequests, "auth: blocked for %s", left)
writeErrorWithIP(
r,
w,
http.StatusTooManyRequests,
remoteIP,
"auth: blocked for %s",
left,
)

return
}
}

cookie, err := Context.auth.newCookie(req, remoteAddr)
cookie, err := Context.auth.newCookie(req, remoteIP)
if err != nil {
aghhttp.Error(r, w, http.StatusForbidden, "%s", err)
writeErrorWithIP(r, w, http.StatusForbidden, remoteIP, "%s", err)

return
}

// Use realIP here, since this IP address is only used for logging.
ip, err := realIP(r)
if err != nil {
log.Error("auth: getting real ip from request: %s", err)
} else if ip == nil {
// Technically shouldn't happen.
log.Error("auth: unknown ip")
log.Error("auth: getting real ip from request with remote ip %s: %s", remoteIP, err)
}

log.Info("auth: user %q successfully logged in from ip %v", req.Name, ip)
Expand Down Expand Up @@ -544,8 +570,7 @@ func optionalAuthThird(w http.ResponseWriter, r *http.Request) (mustAuth bool) {
log.Debug("auth: redirected to login page by GL-Inet submodule")
} else {
log.Debug("auth: redirected to login page")
w.Header().Set(httphdr.Location, "/login.html")
w.WriteHeader(http.StatusFound)
http.Redirect(w, r, "login.html", http.StatusFound)
}
} else {
log.Debug("auth: responded with forbidden to %s %s", r.Method, p)
Expand All @@ -570,8 +595,7 @@ func optionalAuth(
// Redirect to the dashboard if already authenticated.
res := Context.auth.checkSession(cookie.Value)
if res == checkSessionOK {
w.Header().Set(httphdr.Location, "/")
w.WriteHeader(http.StatusFound)
http.Redirect(w, r, "", http.StatusFound)

return
}
Expand Down
2 changes: 1 addition & 1 deletion internal/home/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ func postInstall(handler func(http.ResponseWriter, *http.Request)) func(http.Res
path := r.URL.Path
if Context.firstRun && !strings.HasPrefix(path, "/install.") &&
!strings.HasPrefix(path, "/assets/") {
http.Redirect(w, r, "/install.html", http.StatusFound)
http.Redirect(w, r, "install.html", http.StatusFound)

return
}
Expand Down

0 comments on commit 35a649f

Please sign in to comment.