# 用 GoFiber + GORM + PostgreSQL 實作「IP 白名單(CIDR) 」
管理後台通常只有少量管理者使用,但一旦被暴露在公網,風險極高。
本文將透過一個實際可上線的案例,說明如何使用:
- GoFiber(HTTP Framework)
- GORM(ORM)
- PostgreSQL(
cidr+gist index)
實作一個 「只開白名單、即時生效、不使用快取」 的後台 IP 存取控制。
# 使用情境(案例設定)
假設我們有一個管理後台,需求如下:
- 管理者來源 IP 必須在白名單內
- 白名單可由後台介面即時調整
- 不希望重啟服務或同步快取
- 流量低、規則數量少(< 100)
- 寧可全部擋下,也不能放錯人進來
這正是「直接查 DB、fail-closed」最適合的場景。
# Step 1:設計資料表(CIDR 白名單)
使用 PostgreSQL 原生 cidr 型別來儲存網段。
CREATE TABLE admin_ip_allowlist ( | |
id bigserial PRIMARY KEY, | |
network cidr NOT NULL, | |
enabled boolean DEFAULT true, | |
remark text, | |
created_at timestamptz DEFAULT now() | |
); |
# 建立 GIST index(重點)
CREATE INDEX idx_admin_ip_allowlist_network | |
ON admin_ip_allowlist | |
USING gist (network inet_ops); |
⚠️ 若省略
inet_ops,會出現data type cidr has no default operator class for access method "gist"
# Step 2:核心查詢(IP 是否命中白名單)
每次請求只需要這一筆查詢:
SELECT 1 | |
FROM admin_ip_allowlist | |
WHERE enabled = true | |
AND network >>= :ip::inet | |
LIMIT 1; |
- 查得到資料 → 放行
- 查不到 → 拒絕
# Step 3:GORM 寫法(避免整段 Raw SQL)
GORM 不支援 >>= 運算子,但可以透過 gorm.Expr 使用。
# Model
type AdminIPAllowlist struct { | |
ID uint | |
Network string | |
Enabled bool | |
CreatedAt time.Time | |
} |
# Step 4:Private IP 直接放行(實務必備)
內網來源(VPN、Bastion、K8s)通常不需要進白名單比對。
func isInternalIP(ip net.IP) bool { | |
return ip.IsPrivate() || | |
ip.IsLoopback() || | |
ip.IsLinkLocalUnicast() | |
} |
# Step 5:GoFiber Middleware(完整實作)
func AdminAllowlistMiddleware(db *gorm.DB) fiber.Handler { | |
return func(c *fiber.Ctx) error { | |
ipStr := c.IP() | |
ip := net.ParseIP(ipStr) | |
if ip == nil { | |
return fiber.ErrForbidden | |
} | |
// Private /internal IP 直接放行 | |
if isInternalIP(ip) { | |
return c.Next() | |
} | |
var count int64 | |
err := db.Model(&AdminIPAllowlist{}). | |
Where("enabled = ?", true). | |
Where(gorm.Expr("network >>= ?::inet", ipStr)). | |
Limit(1). | |
Count(&count).Error | |
if err != nil || count == 0 { | |
return fiber.ErrForbidden | |
} | |
return c.Next() | |
} | |
} |
這個 middleware 具備以下特性:
- 每次 request 只打一筆 DB
- DB 修改立即生效
- 無快取、不需 reload
- DB / SQL 出錯時自動 fail-closed
# Step 6:正確取得真實 Client IP
若服務在 Proxy 後面(Nginx / Cloudflare),需設定 Fiber:
app := fiber.New(fiber.Config{ | |
ProxyHeader: fiber.HeaderXForwardedFor, | |
TrustedProxies: []string{ | |
"127.0.0.1", | |
"10.0.0.0/8", | |
"192.168.0.0/16", | |
}, | |
}) |
⚠️ 若未限制可信 proxy,攻擊者可能偽造
X-Forwarded-For
# 為什麼這個做法適合「管理後台」?
- 規則少 → DB 查詢成本低
- 不快取 → 不會有同步問題
cidr+gist→ 查詢語意清楚- fail-closed → 安全優先
這並不適合高流量 API,但非常適合管理後台。
# 總結
這篇文章示範了一個實際可上線的管理後台 IP 白名單實作:
- 使用 PostgreSQL
cidr儲存網段 - 搭配
gist (inet_ops)index 加速查詢 - 在 GoFiber middleware 中直接查 DB
- 不使用快取,確保規則即時生效
- Private IP 直接放行,Public IP 嚴格限制
如果你正在打造管理後台,這會是一個簡單、可靠、不容易出錯的做法。