简体   繁体   中英

Automate whitelisting ip addresses in iptables via bash

I am looking for the best way to automate whitelisting IP addresses into iptables . The list of ip addresses and ports comes from a JSON file /accept-rules.json which is formatted like:

[
  {
    "ip": "1.2.3.4",
    "cidr": 32,
    "protocol": "tcp",
    "port": 3306
  },
  {
    "ip": "2.4.5.6",
    "cidr": 32,
    "protocol": "tcp",
    "port": 80
  },
  {
    "ip": "5.6.7.8",
    "cidr": 32,
    "protocol": "tcp",
    "port": 443
  },
  {
    "ip": "6.8.3.1",
    "cidr": 32,
    "protocol": "tcp",
    "port": 53
  }
]

I need a bash or python script which reads the json file and creates ACCEPT iptables rules. Example ACCEPT rules based on the json above should look like:

iptables -A INPUT -s 1.2.3.4/32 -p tcp -m tcp --dport 3306 -j ACCEPT
iptables -A INPUT -s 2.4.5.6/32 -p tcp -m tcp --dport 80 -j ACCEPT
iptables -A INPUT -s 5.6.7.8/32 -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A INPUT -s 6.8.3.1/32 -p tcp -m tcp --dport 53 -j ACCEPT

Any idea of the best way to code this up?

Pure based on syntax provided

There is a cleaner pure bash version. (without eval )

declare -A iptArray
iptArray[action]='A'
getval() {
    [[ "$@" =~  \"([^\*]*)\"\ *:\ *\"?([^\",]*)\"?[,\ ]*$ ]] && \
        iptArray[${BASH_REMATCH[1]}]=${BASH_REMATCH[2]}
}
while read line;do
    getval $line
    [[ "$line" =~ } ]] && \
        echo iptables -${iptArray[action]} INPUT -p ${iptArray[protocol]} \
            -s ${iptArray[ip]}/${iptArray[cidr]} \
            --dport ${iptArray[port]} -j ACCEPT
  done < ipt_whitelist.json 
iptables -A INPUT -p tcp -s 1.2.3.4/32 --dport 3306 -j ACCEPT
iptables -A INPUT -p tcp -s 2.4.5.6/32 --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -s 5.6.7.8/32 --dport 443 -j ACCEPT
iptables -A INPUT -p tcp -s 6.8.3.1/32 --dport 53 -j ACCEPT

(Delete echo for doing action instead of just printing it)

Working over under , using jq tool

As you know have to found values named protocol , ip , cidr and port in each field, you could ensure ordering and extract values by using jq in something like:

i=0
while :;do
    for var in proto ip mlen port ;do
        read -r $var
        [ "${!var}" = "null" ] && break 2
    done < <(
      jq -r ".[$i].protocol,.[$i].ip,.[$i].cidr,.[$i].port" <whitelist.json
    )
    echo iptables -A INPUT -p $proto -s $ip/$mlen -dport $port -j ACCEPT
    ((i++))
done
iptables -A INPUT -p tcp -s 1.2.3.4/32 -dport 3306 -j ACCEPT
iptables -A INPUT -p tcp -s 2.4.5.6/32 -dport 80 -j ACCEPT
iptables -A INPUT -p tcp -s 5.6.7.8/32 -dport 443 -j ACCEPT
iptables -A INPUT -p tcp -s 6.8.3.1/32 -dport 53 -j ACCEPT

(Again: Delete echo for doing action instead of just printing it)

Here is a python code:

This will generate a bash script file 'accept.sh' with all the iptable entries.

# accept.py

fp = open("accept-rules.json", "r")

data = fp.readlines()
fp1 = open("accept.sh", "w")

for line in data:

    if "{" in line:
        datum = {}
    elif "}" in line:
        s = "iptables -A INPUT -s " + datum["ip"] + "/" + datum["cidr"] + " -p " + datum["protocol"] + " -m " + datum["protocol"] + " --dport " + datum["port"] + " -j ACCEPT\n"
        fp1.write(s)
    elif "[" in line or "]" in line:
        continue
    else:
        datum[line.split(":")[0].strip().strip('"')] = line.split(":")[1].strip().strip(",").strip('"')

fp1.close()
fp.close()

Note that iptables -A adds rules to the end of the table. When matching rules, iptables works from top to bottom and the first match wins so if you had previously blocked an address then white listing it with -A won't work (many default rulesets hav a blaket reject all at the end for example) It's better to use iptables -I to insert rules at the begining in this case.

#!/bin/bash

function getval {
    set -- $1
    RET=${2//[\",]/}
}
while read line
    do
        set -- $line
        if [[ "$1" == '"ip":' ]]
            then
                getval "$line"
                IPADDRESS=$RET
                read line
                getval "$line"
                CIDR=$RET
                read line
                getval  "$line"
                PROTOCOL=$RET
                read line
                getval "$line"
                PORT=$RET
                /sbin/iptables  -I INPUT -s "$IPADDRESS"/"$CIDR" -p "$PROTOCOL" -m "$PROTOCOL" --dport "$PORT" -j ACCEPT
             fi
    done <file.json

A cleaner Python version:

#!/usr/bin/env python
import json
import sys

for rule in json.load(sys.stdin):
    print("iptables -I INPUT -s {ip}/{cidr} -p {protocol} "
          "-m {protocol} --dport {port} -j ACCEPT".format(**rule))

Note: It uses -I to insert rules at the beginning.

Example

$ json2iptables < accept-rules.json

Output

iptables -I INPUT -s 1.2.3.4/32 -p tcp -m tcp --dport 3306 -j ACCEPT
iptables -I INPUT -s 2.4.5.6/32 -p tcp -m tcp --dport 80 -j ACCEPT
iptables -I INPUT -s 5.6.7.8/32 -p tcp -m tcp --dport 443 -j ACCEPT
iptables -I INPUT -s 6.8.3.1/32 -p tcp -m tcp --dport 53 -j ACCEPT

Python implementation:

import json
rules_file = open('accept-rules.json', 'r')
rules = json.load(rules_file)
rules_file.close()
iptables = open('iptavles.sh', 'w')
for rule in rules:
    rule_str = 'iptables -A INPUT -s %s/%s -p tcp -m %s --dport %s -j ACCEPT\n' % (rule['ip'], rule['cidr'], rule['protocol'], rule['port'])
    iptables.write(rule_str)
iptables.close()

accept-rules.json - start json file, iptables.sh - goal file

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM