package model import ( "net/netip" "time" ) // Ban is an address a ban list keeps out (docs/bans.md): of every port, // or of one protocol and port. Bans are kept apart from the // configuration, like a dynamic set's entries: not in the history, and // not on probation. type Ban struct { ID int64 `json:"id"` ListID int64 `json:"list_id"` List string `json:"list"` // Address is what is banned: an address, or an IPv6 prefix of the // list's grouping ("2001:db8:1:2::/64"). Address string `json:"address"` Protocol string `json:"protocol,omitempty"` // tcp or udp; "" every port Port int `json:"port,omitempty"` // Expires is when the ban ends (nil: it doesn't); Timeout the // seconds it has left then. Expires *time.Time `json:"expires,omitempty"` Timeout int `json:"timeout,omitempty"` Created time.Time `json:"created"` // Who banned it (the user or token's name), Source the program's own // name for why ("guard/ssh", "fail2ban/sshd"), Reason free text (a // log line). Who string `json:"who"` Source string `json:"source,omitempty"` Reason string `json:"reason,omitempty"` } // Prefix is the banned address as a prefix. func (b *Ban) Prefix() (netip.Prefix, error) { if p, err := netip.ParsePrefix(b.Address); err == nil { return p, nil } a, err := netip.ParseAddr(b.Address) if err != nil { return netip.Prefix{}, err } return netip.PrefixFrom(a, a.BitLen()), nil } // BanAddress writes a banned prefix: an address alone when it is one // address, else the prefix. func BanAddress(p netip.Prefix) string { if p.IsSingleIP() { return p.Addr().String() } return p.String() }