I have a few things in mine separated out into subscripts.. My actual post_cfg.sh is pretty small.
root@route10:/cfg# cat post-cfg.sh
#!/bin/sh
cd $(dirname $0)
for f in post-cfg.d/*.sh; do
. $f
done
root@route10:/cfg# ls post-cfg.d/
dnsmasq.sh enableNtp.sh setupfw.sh
I enable an extra persistent hosts file with:
root@route10:/cfg# cat post-cfg.d/dnsmasq.sh
#!/bin/sh
if [ "$(uci get dhcp.@dnsmasq[0].addnhosts)" != "/cfg/hosts" ]; then
uci add_list dhcp.@dnsmasq[0].addnhosts='/cfg/hosts'
uci commit
/etc/init.d/dnsmasq reload
fi
I also enable an ntp server with:
root@route10:/cfg# cat post-cfg.d/enableNtp.sh
#!/bin/sh
if [ "$(uci get system.ntp.enable_server)" != "1" ]; then
uci set system.ntp.enable_server="1"
uci commit system
/etc/init.d/sysntpd reload
fi
For firewall changes I disable all the ALGs (like SIP) and enable some emerging threat blocking (a bit simpler than suricata, but still cuts way down on on the intrusion attempts, and logs any attempts to contact a “bad” site from our network).
root@route10:/cfg# cat post-cfg.d/setupfw.sh
#!/bin/sh
ipset -exist create FH_N hash:net
ipset -exist create FH_A hash:ip
ipset -exist create LAN_FH_N hash:net
ipset -exist create LAN_FH_A hash:ip
if iptables -N LOG_DROP >&/dev/null; then
iptables -A LOG_DROP -m limit --limit 5/min -j LOG --log-prefix "IPTables-Dropped: " --log-level 4
iptables -A LOG_DROP -j DROP
fi
if !(iptables -L input_wan_rule | grep -q FH_N); then
iptables -A input_wan_rule -m set --match-set FH_N src -j DROP
iptables -A input_wan_rule -m set --match-set FH_A src -j DROP
fi
if !(iptables -L forwarding_wan_rule | grep -q FH_N); then
iptables -A forwarding_wan_rule -m set --match-set FH_N src -j DROP
iptables -A forwarding_wan_rule -m set --match-set FH_A src -j DROP
fi
if !(iptables -L forwarding_lan_rule | grep -q LAN_FH_N); then
iptables -A forwarding_lan_rule -m set --match-set LAN_FH_N dst -j LOG_DROP
iptables -A forwarding_lan_rule -m set --match-set LAN_FH_A dst -j LOG_DROP
fi
#uci set firewall.@defaults[0].helper='amanda ftp RAS Q.931 irc pptp snmp tftp'
uci set firewall.@defaults[0].helper=''
uci set firewall.@defaults[0].auto_helper='0'
uci commit
/etc/init.d/firewall reload
#update every 11 minutes
if !(crontab -l | grep -q blockThreats); then
(crontab -l ; echo '*/11 * * * * python /cfg/scripts/blockThreats.py') |crontab -
fi
Here’s my emerging threat blocking script - if anyone’s interested.
root@route10:/cfg# cat scripts/blockThreats.py
#!/usr/bin/python3
import os
import urllib
import urllib.request
import time
import calendar
import subprocess
tempFolder = '/tmp/blockThreats/'
tempIpPath = tempFolder + 'ipData'
tempNetPath = tempFolder + 'netData'
expiryPath = tempFolder + 'expiryInfo'
tempTemp = tempFolder + 'temp'
alreadyFetched = set()
freshlyFetched = set()
privateBlocks = {'10.0.0.0/8\n', '192.168.0.0/16\n', '172.16.0.0/12\n', '169.254.0.0/16\n'}
expiryTimes = {}
def readExpiryInfo():
"""
Reads expiry times and etags from a file and populates the expiryTimes dictionary.
"""
if os.path.exists(expiryPath):
with open(expiryPath, 'r') as f:
for line in f:
split = line.split(' ', 2)
if len(split) == 3:
path = split[-1][0 : -1]
t = float(split[0])
etag = split[1]
expiryTimes[path] = (t, etag)
def writeExpiryInfo():
"""
Writes the expiry times from the expiryTimes dictionary to a file.
"""
with open(expiryPath, 'w') as f:
for path, info in expiryTimes.items():
f.write(str(info[0]) + ' ' + info[1] + ' ' + path + '\n')
def tempPathForURL(url):
"""
Generates a temporary file path for a given URL.
Creates necessary directories if they do not exist.
Args:
url (str): The URL to generate the path for.
Returns:
str: The generated temporary file path.
"""
slashesPos = url.index('//')
if slashesPos >= 0:
result = tempFolder + url[slashesPos+2 : ]
os.makedirs(result[0 : result.rindex('/')], exist_ok=True)
return result
def timeFromResponseHeader(response, key):
"""
Extracts and converts a time value from an HTTP response header.
Args:
response (http.client.HTTPResponse): The HTTP response object.
key (str): The header key to extract the time from.
Returns:
float: The extracted time value in seconds since the epoch.
"""
val = response.headers[key]
return 0 if val is None else calendar.timegm(time.strptime(val, '%a, %d %b %Y %H:%M:%S %Z'))
def fetchBlockList(url):
"""
Fetches a block list from a given URL and saves it to a temporary path.
Uses cached data if it is still valid based on expiry times.
Args:
url (str): The URL to fetch the block list from.
Returns:
str: The path to the fetched block list.
"""
#print(url + '\n')
tempPath = tempPathForURL(url)
expireInfo = expiryTimes.get(tempPath)
if expireInfo is not None and time.time() < expireInfo[0]:
return tempPath
if url in alreadyFetched:
return tempPath
alreadyFetched.add(url)
req = urllib.request.Request(url=url, headers={'User-Agent': 'Mozilla/5.0'})
mtime = 0
if os.path.exists(tempPath):
mtime = os.path.getmtime(tempPath)
req.add_header('If-Modified-Since', time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(mtime)))
try:
with urllib.request.urlopen(req) as response:
etag = response.headers.get('etag')
expires = timeFromResponseHeader(response, 'Expires')
expiryTimes[tempPath] = (expires, etag or '')
if etag:
if expireInfo and etag == expireInfo[1]:
return tempPath
else:
lastModified = timeFromResponseHeader(response, 'Last-Modified')
if lastModified != 0 and lastModified <= mtime:
return tempPath
data = response.read()
with open(tempPath, mode='wb', buffering=0) as f:
f.write(data)
freshlyFetched.add(tempPath)
except urllib.error.URLError as e:
#print(e.reason)
pass
return tempPath
def writeSetToRestoreFile(s, f):
for line in s:
f.write('add bltmp ' + line)
def prepareRestores(isWan, blocklistPaths):
"""
Prepares IP and network blocklists for restoration.
This function reads IP addresses and network blocks from the provided blocklist paths,
filters them based on whether they are WAN or private blocks, and writes them to temporary
files for restoration.
Args:
isWan (bool): A flag indicating whether the blocks are for WAN (True) or not (False).
blocklistPaths (list of str): A list of file paths containing the blocklists.
Notes:
- IP addresses are written to a temporary file specified by `tempIpPath`.
- Network blocks are written to a temporary file specified by `tempNetPath`.
"""
addresses = set()
nets = set()
for p in blocklistPaths:
if os.path.exists(p):
with open(p, 'r') as inp:
for line in inp:
if line[0].isdigit():
if line.find('/') >= 0:
if isWan or line not in privateBlocks:
nets.add(line)
else:
addresses.add(line)
with open(tempIpPath, 'w') as f:
f.write('create bltmp hash:ip family inet hashsize 8192 maxelem 65536\n')
writeSetToRestoreFile(addresses, f)
with open(tempNetPath, 'w') as f:
f.write('create bltmp hash:net family inet hashsize 2048 maxelem 65536\n')
writeSetToRestoreFile(nets, f)
def doRestore(restorePath, ipsetName):
with open(restorePath, 'r') as inp:
subprocess.call(["/usr/sbin/ipset", "-!", "restore"], stdin=inp)
subprocess.call(["/usr/sbin/ipset", "swap", "bltmp", ipsetName])
subprocess.call(["/usr/sbin/ipset", "destroy", "bltmp"])
os.unlink(restorePath)
def anyUpdated(tempPaths):
for p in tempPaths:
if p in freshlyFetched:
return True
return False
def isEmptyIpset(setName):
with open(tempTemp, 'w') as f:
subprocess.call(["/usr/sbin/ipset", "list", "--terse", setName], stdout=f)
#result = 0
with open(tempTemp, 'r') as f:
info = f.read()
result = info[-3 : -1] == ' 0'
os.unlink(tempTemp)
return result
def doBlockThreats(fhN, fhA, isWan, blockListURLs):
subprocess.call(["/usr/sbin/ipset", "-exist", "create", fhN, "hash:net"])
subprocess.call(["/usr/sbin/ipset", "-exist", "create", fhA, "hash:ip"])
readExpiryInfo()
tempPaths = [fetchBlockList(url) for url in blockListURLs]
if anyUpdated(tempPaths) or isEmptyIpset(fhN) or isEmptyIpset(fhA):
writeExpiryInfo()
prepareRestores(isWan, tempPaths)
doRestore(tempIpPath, fhA)
doRestore(tempNetPath, fhN)
def doBlockThreatsUsingFirehol(fhN, fhA, isWan, blockLists):
doBlockThreats(fhN, fhA, isWan, [('https://iplists.firehol.org/files/' + bl + '.netset') for bl in blockLists])
doBlockThreatsUsingFirehol("FH_N", "FH_A", True, [ 'firehol_level1', 'firehol_level2', 'firehol_webserver', 'firehol_webclient'])
doBlockThreatsUsingFirehol("LAN_FH_N", "LAN_FH_A", False, [ 'firehol_level1', 'firehol_level2', 'firehol_webclient'])