landed IP address ban editing

This commit is contained in:
2026-02-19 18:29:27 -07:00
parent b8f9c7378c
commit ed607e33b3
10 changed files with 353 additions and 21 deletions
+90 -13
View File
@@ -19,6 +19,8 @@ import (
"slices"
"sync"
"time"
log "github.com/sirupsen/logrus"
)
// IPBanEntry represents an IP address banned from the system.
@@ -149,26 +151,74 @@ func setupIPBanSweep() func() {
}
// nukeIPBanCache completely clears the IP ban cache.
func nukeIPBanCache() {
func nukeIPBanCache(bad, good bool) {
banMutex.Lock()
defer banMutex.Unlock()
banSweeperReset <- true // send the reset signal to the sweeper
for k := range knownBans {
delete(knownBans, k)
if bad {
banSweeperReset <- true // send the reset signal to the sweeper
for k := range knownBans {
delete(knownBans, k)
}
}
for k := range knownGood {
delete(knownGood, k)
if good {
for k := range knownGood {
delete(knownGood, k)
}
}
}
// AmIPToString converts an IP addrsss, in terms of low and high 64-bit values, to a string.
func AmIPToString(low, high uint64) string {
// IsV4 returns true if the entry's address is an IPv4 address.
func (ipb *IPBanEntry) IsV4() bool {
ip := AmCombineIP(ipb.AddressLow, ipb.AddressHigh)
return ip.To4() != nil
}
// SetEnable sets the enable flag on an IP ban entry.
func (ipb *IPBanEntry) SetEnable(ctx context.Context, flag bool) error {
if flag == ipb.Enable {
return nil
}
_, err := amdb.ExecContext(ctx, "UPDATE ipban SET enable = ? WHERE id = ?", flag, ipb.Id)
if err == nil {
nukeIPBanCache(ipb.Enable, !ipb.Enable)
ipb.Enable = flag
}
return err
}
// Delete deletes an IP ban entry.
func (ipb *IPBanEntry) Delete(ctx context.Context) error {
_, err := amdb.ExecContext(ctx, "DELETE FROM ipban WHERE id = ?", ipb.Id)
if err == nil {
nukeIPBanCache(true, false) // can only affect "ban" cache entries, not "good" ones
}
return err
}
// AmCombineIP converts an IP address, in terms of low and high 64-bit values, to a net.IP value.
func AmCombineIP(low, high uint64) net.IP {
t := big.NewInt(0).Lsh(new(big.Int).SetUint64(high), 64)
addr := big.NewInt(0).Or(t, new(big.Int).SetUint64(low))
ip := net.IP(addr.FillBytes(make([]byte, 16)))
return net.IP(addr.FillBytes(make([]byte, 16)))
}
// AmIPToString converts an IP address, in terms of low and high 64-bit values, to a string.
func AmIPToString(low, high uint64, asV4 bool) string {
ip := AmCombineIP(low, high)
if asV4 {
ip = ip[12:16]
}
return ip.String()
}
// AmBreakUpIP breaks up the IP address into low and high uint64 values.
func AmBreakUpIP(addr net.IP) (uint64, uint64) {
iv := big.NewInt(0).SetBytes(addr)
ivLo := big.NewInt(0).And(iv, low64mask).Uint64()
ivHi := big.NewInt(0).Rsh(iv, 64).Uint64()
return ivLo, ivHi
}
/* AmTestIPBan tests an IP address to see if it's on the banned list.
* Parameters:
* ctx - Standard Go context parameter.
@@ -191,10 +241,7 @@ func AmTestIPBan(ctx context.Context, ipAddress string) (string, error) {
if addr == nil {
return "", fmt.Errorf("invalid address %s", ipAddress)
}
iv := big.NewInt(0)
iv.SetBytes(addr)
ivLo := big.NewInt(0).And(iv, low64mask).Uint64()
ivHi := big.NewInt(0).Rsh(iv, 64).Uint64()
ivLo, ivHi := AmBreakUpIP(addr)
row := amdb.QueryRowContext(ctx, `SELECT message, expire FROM ipban WHERE (address_lo & mask_lo) = (? & mask_lo)
AND (address_hi & mask_hi) = (? & mask_hi) AND (expire IS NULL OR expire >= NOW())
AND enable <> 0 ORDER BY mask_hi DESC, mask_lo DESC`, ivLo, ivHi)
@@ -236,3 +283,33 @@ func AmGetIPBan(ctx context.Context, id int32) (*IPBanEntry, error) {
}
return &(dbdata[0]), nil
}
// AmAddIPBan adds a new IP address ban.
func AmAddIPBan(ctx context.Context, addr, mask net.IP, expires *time.Time, message string, byUser *User) error {
log.Debugf("AmAddIPBan: addr = %s (%v), mask = %s(%v)", addr.String(), addr, mask.String(), mask)
if addr.IsUnspecified() {
return errors.New("cannot add IP ban with unspecified address")
}
if mask.IsUnspecified() {
return errors.New("cannot add IP ban with unspecified mask")
}
newEntry := IPBanEntry{
Expire: expires,
Message: message,
BlockByUid: byUser.Uid,
}
newEntry.AddressLow, newEntry.AddressHigh = AmBreakUpIP(addr)
if newEntry.AddressLow == 0 && newEntry.AddressHigh == 0 {
return errors.New("invalid or incorrectly-parsed address")
}
newEntry.MaskLow, newEntry.MaskHigh = AmBreakUpIP(mask)
if newEntry.MaskLow == 0 && newEntry.MaskHigh == 0 {
return errors.New("invalid or incorrectly-parsed mask")
}
_, err := amdb.NamedExecContext(ctx, `INSERT INTO ipban (address_lo, address_hi, mask_lo, mask_hi, enable, expire, message, block_by, block_on)
VALUES (:address_lo, :address_hi, :mask_lo, :mask_hi, 1, :expire, :message, :block_by, NOW())`, &newEntry)
if err == nil {
nukeIPBanCache(false, true)
}
return err
}