mirror of
https://github.com/ditatompel/xmr-remote-nodes.git
synced 2024-12-22 11:39:23 +00:00
feat: Store hashed user IP address when submitting new node
This feature added to help trace spammers. The IP address stored with one-way hash + salt to maintain user privacy.
This commit is contained in:
parent
d19e5844b0
commit
48a25bece0
6 changed files with 73 additions and 8 deletions
|
@ -16,6 +16,11 @@ IPV6_CAPABLE=false
|
|||
# #############
|
||||
APP_URL="https://xmr.ditatompel.com" # URL where user can access the web UI, don't put trailing slash
|
||||
|
||||
# APP_SECRET is random 64-character hex string that give us 32 random bytes.
|
||||
# For now, this used for ip address salt, but may be useful for another feature
|
||||
# in the future. You can achieve this using `openssl rand -hex 32`.
|
||||
APP_SECRET=
|
||||
|
||||
# Fiber Config
|
||||
APP_PREFORK=false
|
||||
APP_HOST="127.0.0.1"
|
||||
|
|
|
@ -13,7 +13,8 @@ type App struct {
|
|||
LogLevel string
|
||||
|
||||
// configuration for server
|
||||
URL string // URL where user can access the web UI, don't put trailing slash
|
||||
URL string // URL where user can access the web UI, don't put trailing slash
|
||||
Secret string // random 64-character hex string that give us 32 random bytes
|
||||
|
||||
// fiber specific config
|
||||
Prefork bool
|
||||
|
@ -61,6 +62,7 @@ func LoadApp() {
|
|||
|
||||
// server configuration
|
||||
app.URL = os.Getenv("APP_URL")
|
||||
app.Secret = os.Getenv("APP_SECRET")
|
||||
|
||||
// fiber specific config
|
||||
app.Host = os.Getenv("APP_HOST")
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
type migrateFn func(*DB) error
|
||||
|
||||
var dbMigrate = [...]migrateFn{v1, v2, v3, v4}
|
||||
var dbMigrate = [...]migrateFn{v1, v2, v3, v4, v5}
|
||||
|
||||
func MigrateDb(db *DB) error {
|
||||
version := getSchemaVersion(db)
|
||||
|
@ -287,3 +287,18 @@ func v4(db *DB) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func v5(db *DB) error {
|
||||
// table: tbl_node
|
||||
slog.Debug("[DB] Adding additional columns to tbl_node")
|
||||
_, err := db.Exec(`
|
||||
ALTER TABLE tbl_node
|
||||
ADD COLUMN submitter_iphash CHAR(64) NOT NULL DEFAULT ''
|
||||
COMMENT 'hashed IP address who submitted the node'
|
||||
AFTER date_entered;`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ func (s *fiberServer) addNodeHandler(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
moneroRepo := monero.New()
|
||||
if err := moneroRepo.Add(f.Protocol, f.Hostname, uint(f.Port)); err != nil {
|
||||
if err := moneroRepo.Add(c.IP(), s.secret, f.Protocol, f.Hostname, uint(f.Port)); err != nil {
|
||||
handler := adaptor.HTTPHandler(templ.Handler(views.Alert("error", err.Error())))
|
||||
return handler(c)
|
||||
}
|
||||
|
@ -354,7 +354,7 @@ func (s *fiberServer) addNodeAPI(c *fiber.Ctx) error {
|
|||
hostname := c.FormValue("hostname")
|
||||
|
||||
moneroRepo := monero.New()
|
||||
if err := moneroRepo.Add(protocol, hostname, uint(port)); err != nil {
|
||||
if err := moneroRepo.Add(c.IP(), s.secret, protocol, hostname, uint(port)); err != nil {
|
||||
return c.JSON(fiber.Map{
|
||||
"status": "error",
|
||||
"message": err.Error(),
|
||||
|
|
|
@ -8,8 +8,9 @@ import (
|
|||
|
||||
type fiberServer struct {
|
||||
*fiber.App
|
||||
db *database.DB
|
||||
url string
|
||||
db *database.DB
|
||||
url string
|
||||
secret string
|
||||
}
|
||||
|
||||
// NewServer returns a new fiber server
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package monero
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -54,6 +56,7 @@ type Node struct {
|
|||
Latitude float64 `json:"latitude" db:"lat"`
|
||||
Longitude float64 `json:"longitude" db:"lon"`
|
||||
DateEntered int64 `json:"date_entered,omitempty" db:"date_entered"`
|
||||
SubmitterIPHash string `json:"submitter_iphash,omitempty" db:"submitter_iphash"`
|
||||
LastChecked int64 `json:"last_checked" db:"last_checked"`
|
||||
FailedCount uint `json:"failed_count,omitempty" db:"failed_count"`
|
||||
LastCheckStatus types.JSONText `json:"last_check_statuses" db:"last_check_status"`
|
||||
|
@ -176,7 +179,35 @@ func (r *moneroRepo) Nodes(q QueryNodes) (Nodes, error) {
|
|||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
*
|
||||
id,
|
||||
hostname,
|
||||
ip_addr,
|
||||
port,
|
||||
protocol,
|
||||
is_tor,
|
||||
is_i2p,
|
||||
is_available,
|
||||
nettype,
|
||||
height,
|
||||
adjusted_time,
|
||||
database_size,
|
||||
difficulty,
|
||||
version,
|
||||
uptime,
|
||||
estimate_fee,
|
||||
asn,
|
||||
asn_name,
|
||||
country,
|
||||
country_name,
|
||||
city,
|
||||
lat,
|
||||
lon,
|
||||
date_entered,
|
||||
last_checked,
|
||||
last_check_status,
|
||||
cors_capable,
|
||||
ipv6_only,
|
||||
ip_addresses
|
||||
FROM
|
||||
tbl_node
|
||||
%s
|
||||
|
@ -190,7 +221,7 @@ func (r *moneroRepo) Nodes(q QueryNodes) (Nodes, error) {
|
|||
return nodes, err
|
||||
}
|
||||
|
||||
func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
||||
func (r *moneroRepo) Add(submitterIP, salt, protocol, hostname string, port uint) error {
|
||||
if protocol != "http" && protocol != "https" {
|
||||
return errors.New("Invalid protocol, must one of or HTTP/HTTPS")
|
||||
}
|
||||
|
@ -270,6 +301,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
|||
lat,
|
||||
lon,
|
||||
date_entered,
|
||||
submitter_iphash,
|
||||
last_checked,
|
||||
last_check_status,
|
||||
ip_addresses,
|
||||
|
@ -288,6 +320,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
|||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
)`,
|
||||
protocol,
|
||||
|
@ -300,6 +333,7 @@ func (r *moneroRepo) Add(protocol string, hostname string, port uint) error {
|
|||
0,
|
||||
0,
|
||||
time.Now().Unix(),
|
||||
hashIPWithSalt(submitterIP, salt),
|
||||
0,
|
||||
string(statusDb),
|
||||
ips,
|
||||
|
@ -397,6 +431,14 @@ func (r *moneroRepo) Countries() ([]Countries, error) {
|
|||
return c, err
|
||||
}
|
||||
|
||||
// hashIPWithSalt hashes IP address with salt designed for checksumming, but
|
||||
// still maintain user privacy, this is NOT cryptographic security.
|
||||
func hashIPWithSalt(ip, salt string) string {
|
||||
hasher := sha256.New()
|
||||
hasher.Write([]byte(salt + ip)) // Combine salt and IP
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
||||
// ParseNodeStatuses parses JSONText into [5]int
|
||||
// Used this to parse last_check_status for templ engine
|
||||
func ParseNodeStatuses(statuses types.JSONText) [5]int {
|
||||
|
|
Loading…
Reference in a new issue