-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add --show-real-ips logic and convert to async mode
- Loading branch information
1 parent
4eb0165
commit a8e94b5
Showing
4 changed files
with
195 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
const | ||
CF_IP_API: string = "https://api.cloudflare.com/client/v4/ips" | ||
CF_REAL_IP_HEADER: string = "CF-Connecting-IP;" | ||
|
||
FASTLY_IP_API:string = "https://api.fastly.com/public-ip-list" | ||
|
||
DEFAULT_OUTPUT_PATH: string = "/etc/nginx/reverse_proxies" | ||
|
||
CFG_SET_REAL_IP_FROM: string = "set_real_ip_from" | ||
CFG_REAL_IP_HEADER: string = "real_ip_header" | ||
|
||
ONE_MINUTE: int = 60_000 | ||
THREE_HOURS: int = 108_000_00 | ||
SIX_HOURS: int = 216_000_00 | ||
TWELVE_HOURS: int = 432_000_00 | ||
# TWELVE_HOURS: int = int(12.hours.milliseconds) | ||
|
||
NGINX_PROCESS_NAME: string = "nginx" | ||
NGINX_TEST_CMD: string = "nginx -t" | ||
NGINX_RELOAD_CMD: string = "nginx -s reload" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import std/[os, osproc, strformat, times] | ||
|
||
|
||
proc ensureNginxExists() = | ||
let now: string = getTime().format("yyyy-MM-dd HH:mm:ss") | ||
|
||
let result: string = findExe(NGINX_PROCESS_NAME) | ||
if result == "": | ||
quit(fmt"{now} - nginx command not found", 1) | ||
|
||
|
||
proc testNginxConfig(): int = | ||
let now: string = getTime().format("yyyy-MM-dd HH:mm:ss") | ||
echo fmt"{now} - Testing nginx configuration" | ||
|
||
return execCmd(command=NGINX_TEST_CMD) | ||
|
||
|
||
proc reloadNginx() = | ||
let now: string = getTime().format("yyyy-MM-dd HH:mm:ss") | ||
|
||
let result: int = execCmd(command=NGINX_RELOAD_CMD) | ||
if result != 0: | ||
echo fmt"{now} - nginx process reload failed" | ||
else: | ||
echo fmt"{now} - nginx process reloaded successfully" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import std/[asyncdispatch, httpclient, json, strformat, options, strutils] | ||
include consts, nginx | ||
|
||
|
||
type | ||
IPCidrs = object | ||
ipv4: JsonNode | ||
ipv6: JsonNode | ||
etag: string | ||
etagChanged: bool | ||
|
||
|
||
proc reloadNginxAt(hour: int = 3, minute: int = 0) {.async.} = | ||
while true: | ||
let now: DateTime = getTime().local() | ||
if now.hour == hour and now.minute == minute: | ||
let testResult = testNginxConfig() | ||
if testResult != 0: | ||
echo fmt"{now} - nginx configuration test failed" | ||
continue | ||
|
||
reloadNginx() | ||
|
||
await sleepAsync(ONE_MINUTE) | ||
|
||
|
||
proc populateReverseProxyFile(filePath: string=DEFAULT_OUTPUT_PATH, ipCidr: IPCidrs): bool = | ||
let now: string = getTime().format("yyyy-MM-dd HH:mm:ss") | ||
echo fmt"{now} - Populating CIDRs file in {filePath}" | ||
|
||
if ipCidr.etagChanged: | ||
try: | ||
let file: File = open(filePath, fmWrite) | ||
defer: file.close() | ||
|
||
file.write("# Cloudflare ranges\n") | ||
file.write("# Last etag: ", ipCidr.etag, "\n\n") | ||
file.write("# IPv4 CIDRS\n") | ||
|
||
for cidr in ipCidr.ipv4: | ||
file.write(CFG_SET_REAL_IP_FROM, " ", cidr.getStr(), ";", "\n") | ||
|
||
file.write("\n# IPv6 CIDRS\n") | ||
|
||
for cidr in ipCidr.ipv6: | ||
file.write(CFG_SET_REAL_IP_FROM, " ", cidr.getStr(), ";", "\n") | ||
|
||
file.write("\n\n", CFG_REAL_IP_HEADER, " ", CF_REAL_IP_HEADER, "\n") | ||
return true | ||
except: | ||
echo fmt"{now} - Could not open {filePath}" | ||
return false | ||
|
||
|
||
proc getCloudflareCIDRs(): Option[IPCidrs] = | ||
let now: string = getTime().format("yyyy-MM-dd HH:mm:ss") | ||
echo fmt"{now} - Getting Cloudflare CIDRs" | ||
|
||
let client: HttpClient = newHttpClient() | ||
let response: Response = client.get(CF_IP_API) | ||
|
||
if response.code != Http200: | ||
echo fmt"{now} - API call to {CF_IP_API} failed" | ||
return none(IPCidrs) | ||
|
||
let jsonResponse: JsonNode = parseJson(response.body) | ||
|
||
let etag: string = jsonResponse["result"]["etag"].getStr() | ||
|
||
let apiSuccess: bool = jsonResponse["success"].getBool() | ||
if apiSuccess != true: | ||
echo fmt"{now} - API `success` is not true: {apiSuccess}" | ||
return none(IPCidrs) | ||
|
||
let ipv4Cidrs: JsonNode = jsonResponse["result"]["ipv4_cidrs"] | ||
let ipv6Cidrs: JsonNode = jsonResponse["result"]["ipv6_cidrs"] | ||
|
||
if ipv4Cidrs.isNil or ipv6Cidrs.isNil: | ||
return none(IPCidrs) | ||
else: | ||
return some(IPCidrs(ipv4: ipv4Cidrs, ipv6: ipv6Cidrs, etag: etag, etagChanged: true)) | ||
|
||
|
||
proc getCurrentEtag(configFile: string=DEFAULT_OUTPUT_PATH): string = | ||
let now: string = getTime().format("yyyy-MM-dd HH:mm:ss") | ||
|
||
if not fileExists(configFile): | ||
echo fmt"{now} - {configFile} does not exist" | ||
return | ||
|
||
for line in lines(configFile): | ||
if line.startsWith("# Last etag:"): | ||
let etagLine: seq[string] = line.split("# Last etag: ") | ||
if len(etagLine) > 1: | ||
return etagLine[1] | ||
|
||
|
||
proc fetchAndProcessIPCidrs() {.async.} = | ||
let now: string = getTime().format("yyyy-MM-dd HH:mm:ss") | ||
|
||
while true: | ||
let etag: string = getCurrentEtag() | ||
let cfCIDRs: Option[IPCidrs] = getCloudflareCIDRs() | ||
|
||
case cfCIDRs.isSome: | ||
of true: | ||
let cidrs: IPCidrs = cfCIDRs.get() | ||
if etag != cidrs.etag: | ||
if populateReverseProxyFile(ipCidr=cidrs): | ||
waitFor reloadNginxAt() | ||
else: | ||
echo fmt"{now} - etag has not changed {etag}" | ||
of false: | ||
echo fmt"{now} - Failed fetching CIDRs" | ||
|
||
await sleepAsync(SIX_HOURS) | ||
|