mirror of
https://github.com/mjl-/mox.git
synced 2024-12-26 16:33:47 +03:00
log a consistent log line for failed authentication attempts, with the remote ip
so external tools (like fail2ban) can monitor the logs and block ip's of bots. for issue #30 by inigoserna, though i'm not sure i interpreted the suggestion correctly.
This commit is contained in:
parent
70d07c5459
commit
713d781bad
4 changed files with 28 additions and 4 deletions
|
@ -70,11 +70,14 @@ func checkAccountAuth(ctx context.Context, log *mlog.Log, w http.ResponseWriter,
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
var remoteIP net.IP
|
||||||
addr, err = net.ResolveTCPAddr("tcp", r.RemoteAddr)
|
addr, err = net.ResolveTCPAddr("tcp", r.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorx("parsing remote address", err, mlog.Field("addr", r.RemoteAddr))
|
log.Errorx("parsing remote address", err, mlog.Field("addr", r.RemoteAddr))
|
||||||
|
} else if addr != nil {
|
||||||
|
remoteIP = addr.IP
|
||||||
}
|
}
|
||||||
if addr != nil && !mox.LimiterFailedAuth.Add(addr.IP, start, 1) {
|
if remoteIP != nil && !mox.LimiterFailedAuth.Add(remoteIP, start, 1) {
|
||||||
metrics.AuthenticationRatelimitedInc("httpaccount")
|
metrics.AuthenticationRatelimitedInc("httpaccount")
|
||||||
http.Error(w, "429 - too many auth attempts", http.StatusTooManyRequests)
|
http.Error(w, "429 - too many auth attempts", http.StatusTooManyRequests)
|
||||||
return ""
|
return ""
|
||||||
|
@ -89,6 +92,7 @@ func checkAccountAuth(ctx context.Context, log *mlog.Log, w http.ResponseWriter,
|
||||||
} else if acc, err := store.OpenEmailAuth(t[0], t[1]); err != nil {
|
} else if acc, err := store.OpenEmailAuth(t[0], t[1]); err != nil {
|
||||||
if errors.Is(err, store.ErrUnknownCredentials) {
|
if errors.Is(err, store.ErrUnknownCredentials) {
|
||||||
authResult = "badcreds"
|
authResult = "badcreds"
|
||||||
|
log.Info("failed authentication attempt", mlog.Field("username", t[0]), mlog.Field("remote", remoteIP))
|
||||||
}
|
}
|
||||||
log.Errorx("open account", err)
|
log.Errorx("open account", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -132,11 +132,14 @@ func checkAdminAuth(ctx context.Context, passwordfile string, w http.ResponseWri
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
var remoteIP net.IP
|
||||||
addr, err = net.ResolveTCPAddr("tcp", r.RemoteAddr)
|
addr, err = net.ResolveTCPAddr("tcp", r.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorx("parsing remote address", err, mlog.Field("addr", r.RemoteAddr))
|
log.Errorx("parsing remote address", err, mlog.Field("addr", r.RemoteAddr))
|
||||||
|
} else if addr != nil {
|
||||||
|
remoteIP = addr.IP
|
||||||
}
|
}
|
||||||
if addr != nil && !mox.LimiterFailedAuth.Add(addr.IP, start, 1) {
|
if remoteIP != nil && !mox.LimiterFailedAuth.Add(remoteIP, start, 1) {
|
||||||
metrics.AuthenticationRatelimitedInc("httpadmin")
|
metrics.AuthenticationRatelimitedInc("httpadmin")
|
||||||
http.Error(w, "429 - too many auth attempts", http.StatusTooManyRequests)
|
http.Error(w, "429 - too many auth attempts", http.StatusTooManyRequests)
|
||||||
return false
|
return false
|
||||||
|
@ -164,10 +167,12 @@ func checkAdminAuth(ctx context.Context, passwordfile string, w http.ResponseWri
|
||||||
}
|
}
|
||||||
t := strings.SplitN(string(auth), ":", 2)
|
t := strings.SplitN(string(auth), ":", 2)
|
||||||
if len(t) != 2 || len(t[1]) < 8 {
|
if len(t) != 2 || len(t[1]) < 8 {
|
||||||
|
log.Info("failed authentication attempt", mlog.Field("username", "admin"), mlog.Field("remote", remoteIP))
|
||||||
return respondAuthFail()
|
return respondAuthFail()
|
||||||
}
|
}
|
||||||
if err := bcrypt.CompareHashAndPassword([]byte(passwordhash), []byte(t[1])); err != nil {
|
if err := bcrypt.CompareHashAndPassword([]byte(passwordhash), []byte(t[1])); err != nil {
|
||||||
authResult = "badcreds"
|
authResult = "badcreds"
|
||||||
|
log.Info("failed authentication attempt", mlog.Field("username", "admin"), mlog.Field("remote", remoteIP))
|
||||||
return respondAuthFail()
|
return respondAuthFail()
|
||||||
}
|
}
|
||||||
authCache.lastSuccessHash = passwordhash
|
authCache.lastSuccessHash = passwordhash
|
||||||
|
|
|
@ -1538,6 +1538,7 @@ func (c *conn) cmdAuthenticate(tag, cmd string, p *parser) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, store.ErrUnknownCredentials) {
|
if errors.Is(err, store.ErrUnknownCredentials) {
|
||||||
authResult = "badcreds"
|
authResult = "badcreds"
|
||||||
|
c.log.Info("authentication failed", mlog.Field("username", authc))
|
||||||
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
||||||
}
|
}
|
||||||
xusercodeErrorf("", "error")
|
xusercodeErrorf("", "error")
|
||||||
|
@ -1565,6 +1566,7 @@ func (c *conn) cmdAuthenticate(tag, cmd string, p *parser) {
|
||||||
acc, _, err := store.OpenEmail(addr)
|
acc, _, err := store.OpenEmail(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, store.ErrUnknownCredentials) {
|
if errors.Is(err, store.ErrUnknownCredentials) {
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", addr), mlog.Field("remote", c.remoteIP))
|
||||||
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
||||||
}
|
}
|
||||||
xserverErrorf("looking up address: %v", err)
|
xserverErrorf("looking up address: %v", err)
|
||||||
|
@ -1580,6 +1582,7 @@ func (c *conn) cmdAuthenticate(tag, cmd string, p *parser) {
|
||||||
err := acc.DB.Read(context.TODO(), func(tx *bstore.Tx) error {
|
err := acc.DB.Read(context.TODO(), func(tx *bstore.Tx) error {
|
||||||
password, err := bstore.QueryTx[store.Password](tx).Get()
|
password, err := bstore.QueryTx[store.Password](tx).Get()
|
||||||
if err == bstore.ErrAbsent {
|
if err == bstore.ErrAbsent {
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", addr), mlog.Field("remote", c.remoteIP))
|
||||||
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1593,7 +1596,8 @@ func (c *conn) cmdAuthenticate(tag, cmd string, p *parser) {
|
||||||
xcheckf(err, "tx read")
|
xcheckf(err, "tx read")
|
||||||
})
|
})
|
||||||
if ipadhash == nil || opadhash == nil {
|
if ipadhash == nil || opadhash == nil {
|
||||||
c.log.Info("cram-md5 auth attempt without derived secrets set, save password again to store secrets", mlog.Field("address", addr))
|
c.log.Info("cram-md5 auth attempt without derived secrets set, save password again to store secrets", mlog.Field("username", addr))
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", addr), mlog.Field("remote", c.remoteIP))
|
||||||
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1602,6 +1606,7 @@ func (c *conn) cmdAuthenticate(tag, cmd string, p *parser) {
|
||||||
opadhash.Write(ipadhash.Sum(nil))
|
opadhash.Write(ipadhash.Sum(nil))
|
||||||
digest := fmt.Sprintf("%x", opadhash.Sum(nil))
|
digest := fmt.Sprintf("%x", opadhash.Sum(nil))
|
||||||
if digest != t[1] {
|
if digest != t[1] {
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", addr), mlog.Field("remote", c.remoteIP))
|
||||||
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1675,6 +1680,7 @@ func (c *conn) cmdAuthenticate(tag, cmd string, p *parser) {
|
||||||
c.readline(false) // Should be "*" for cancellation.
|
c.readline(false) // Should be "*" for cancellation.
|
||||||
if errors.Is(err, scram.ErrInvalidProof) {
|
if errors.Is(err, scram.ErrInvalidProof) {
|
||||||
authResult = "badcreds"
|
authResult = "badcreds"
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", ss.Authentication), mlog.Field("remote", c.remoteIP))
|
||||||
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
xusercodeErrorf("AUTHENTICATIONFAILED", "bad credentials")
|
||||||
}
|
}
|
||||||
xuserErrorf("server final: %w", err)
|
xuserErrorf("server final: %w", err)
|
||||||
|
@ -1744,6 +1750,7 @@ func (c *conn) cmdLogin(tag, cmd string, p *parser) {
|
||||||
var code string
|
var code string
|
||||||
if errors.Is(err, store.ErrUnknownCredentials) {
|
if errors.Is(err, store.ErrUnknownCredentials) {
|
||||||
code = "AUTHENTICATIONFAILED"
|
code = "AUTHENTICATIONFAILED"
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", userid), mlog.Field("remote", c.remoteIP))
|
||||||
}
|
}
|
||||||
xusercodeErrorf(code, "login failed")
|
xusercodeErrorf(code, "login failed")
|
||||||
}
|
}
|
||||||
|
|
|
@ -985,6 +985,7 @@ func (c *conn) cmdAuth(p *parser) {
|
||||||
if err != nil && errors.Is(err, store.ErrUnknownCredentials) {
|
if err != nil && errors.Is(err, store.ErrUnknownCredentials) {
|
||||||
// ../rfc/4954:274
|
// ../rfc/4954:274
|
||||||
authResult = "badcreds"
|
authResult = "badcreds"
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", authc), mlog.Field("remote", c.remoteIP))
|
||||||
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
||||||
}
|
}
|
||||||
xcheckf(err, "verifying credentials")
|
xcheckf(err, "verifying credentials")
|
||||||
|
@ -1016,6 +1017,7 @@ func (c *conn) cmdAuth(p *parser) {
|
||||||
acc, _, err := store.OpenEmail(addr)
|
acc, _, err := store.OpenEmail(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, store.ErrUnknownCredentials) {
|
if errors.Is(err, store.ErrUnknownCredentials) {
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", addr), mlog.Field("remote", c.remoteIP))
|
||||||
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1031,6 +1033,7 @@ func (c *conn) cmdAuth(p *parser) {
|
||||||
err := acc.DB.Read(context.TODO(), func(tx *bstore.Tx) error {
|
err := acc.DB.Read(context.TODO(), func(tx *bstore.Tx) error {
|
||||||
password, err := bstore.QueryTx[store.Password](tx).Get()
|
password, err := bstore.QueryTx[store.Password](tx).Get()
|
||||||
if err == bstore.ErrAbsent {
|
if err == bstore.ErrAbsent {
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", addr), mlog.Field("remote", c.remoteIP))
|
||||||
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1044,7 +1047,8 @@ func (c *conn) cmdAuth(p *parser) {
|
||||||
xcheckf(err, "tx read")
|
xcheckf(err, "tx read")
|
||||||
})
|
})
|
||||||
if ipadhash == nil || opadhash == nil {
|
if ipadhash == nil || opadhash == nil {
|
||||||
c.log.Info("cram-md5 auth attempt without derived secrets set, save password again to store secrets", mlog.Field("address", addr))
|
c.log.Info("cram-md5 auth attempt without derived secrets set, save password again to store secrets", mlog.Field("username", addr))
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", addr), mlog.Field("remote", c.remoteIP))
|
||||||
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1053,6 +1057,7 @@ func (c *conn) cmdAuth(p *parser) {
|
||||||
opadhash.Write(ipadhash.Sum(nil))
|
opadhash.Write(ipadhash.Sum(nil))
|
||||||
digest := fmt.Sprintf("%x", opadhash.Sum(nil))
|
digest := fmt.Sprintf("%x", opadhash.Sum(nil))
|
||||||
if digest != t[1] {
|
if digest != t[1] {
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", addr), mlog.Field("remote", c.remoteIP))
|
||||||
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad user/pass")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1088,6 +1093,7 @@ func (c *conn) cmdAuth(p *parser) {
|
||||||
// todo: we could continue scram with a generated salt, deterministically generated
|
// todo: we could continue scram with a generated salt, deterministically generated
|
||||||
// from the username. that way we don't have to store anything but attackers cannot
|
// from the username. that way we don't have to store anything but attackers cannot
|
||||||
// learn if an account exists. same for absent scram saltedpassword below.
|
// learn if an account exists. same for absent scram saltedpassword below.
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", ss.Authentication), mlog.Field("remote", c.remoteIP))
|
||||||
xsmtpUserErrorf(smtp.C454TempAuthFail, smtp.SeSys3Other0, "scram not possible")
|
xsmtpUserErrorf(smtp.C454TempAuthFail, smtp.SeSys3Other0, "scram not possible")
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -1110,6 +1116,7 @@ func (c *conn) cmdAuth(p *parser) {
|
||||||
}
|
}
|
||||||
if err == bstore.ErrAbsent || err == nil && (len(xscram.Salt) == 0 || xscram.Iterations == 0 || len(xscram.SaltedPassword) == 0) {
|
if err == bstore.ErrAbsent || err == nil && (len(xscram.Salt) == 0 || xscram.Iterations == 0 || len(xscram.SaltedPassword) == 0) {
|
||||||
c.log.Info("scram auth attempt without derived secrets set, save password again to store secrets", mlog.Field("address", ss.Authentication))
|
c.log.Info("scram auth attempt without derived secrets set, save password again to store secrets", mlog.Field("address", ss.Authentication))
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", ss.Authentication), mlog.Field("remote", c.remoteIP))
|
||||||
xsmtpUserErrorf(smtp.C454TempAuthFail, smtp.SeSys3Other0, "scram not possible")
|
xsmtpUserErrorf(smtp.C454TempAuthFail, smtp.SeSys3Other0, "scram not possible")
|
||||||
}
|
}
|
||||||
xcheckf(err, "fetching credentials")
|
xcheckf(err, "fetching credentials")
|
||||||
|
@ -1129,6 +1136,7 @@ func (c *conn) cmdAuth(p *parser) {
|
||||||
c.readline() // Should be "*" for cancellation.
|
c.readline() // Should be "*" for cancellation.
|
||||||
if errors.Is(err, scram.ErrInvalidProof) {
|
if errors.Is(err, scram.ErrInvalidProof) {
|
||||||
authResult = "badcreds"
|
authResult = "badcreds"
|
||||||
|
c.log.Info("failed authentication attempt", mlog.Field("username", ss.Authentication), mlog.Field("remote", c.remoteIP))
|
||||||
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad credentials")
|
xsmtpUserErrorf(smtp.C535AuthBadCreds, smtp.SePol7AuthBadCreds8, "bad credentials")
|
||||||
}
|
}
|
||||||
xcheckf(err, "server final")
|
xcheckf(err, "server final")
|
||||||
|
|
Loading…
Reference in a new issue