├── images ├── blockgrp ├── blockgrp.png ├── blockgrp2.png ├── firewall1.png └── firewall2.png ├── Dockerfile ├── crontab ├── config.php ├── add_block_firewall.php ├── del_block_firewall.php └── README.md /images/blockgrp: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /images/blockgrp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tusc/blockips-unifi/HEAD/images/blockgrp.png -------------------------------------------------------------------------------- /images/blockgrp2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tusc/blockips-unifi/HEAD/images/blockgrp2.png -------------------------------------------------------------------------------- /images/firewall1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tusc/blockips-unifi/HEAD/images/firewall1.png -------------------------------------------------------------------------------- /images/firewall2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tusc/blockips-unifi/HEAD/images/firewall2.png -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #FROM arm64v8/alpine:latest 2 | FROM alpine:latest 3 | 4 | RUN apk --update add composer php7-curl php7-session git tzdata && \ 5 | composer require art-of-wifi/unifi-api-client 6 | 7 | COPY add_block_firewall.php / 8 | COPY del_block_firewall.php / 9 | 10 | # requird when building arm images on x86 hardware 11 | ADD qemu-aarch64-static /usr/bin 12 | 13 | # build startup script 14 | RUN echo "#!/bin/sh" > /startscript.sh 15 | RUN echo "chown root:root /etc/crontabs/root" >> /startscript.sh 16 | RUN echo "/usr/sbin/crond -d 8 -f" >> /startscript.sh 17 | 18 | RUN chmod +x /startscript.sh 19 | 20 | ENTRYPOINT ["/startscript.sh"] 21 | -------------------------------------------------------------------------------- /crontab: -------------------------------------------------------------------------------- 1 | # do daily/weekly/monthly maintenance 2 | # min hour day month weekday command 3 | */15 * * * * run-parts /etc/periodic/15min 4 | 0 * * * * run-parts /etc/periodic/hourly 5 | 0 2 * * * run-parts /etc/periodic/daily 6 | 0 3 * * 6 run-parts /etc/periodic/weekly 7 | 0 5 1 * * run-parts /etc/periodic/monthly 8 | 9 | 10 | # Block the following IP addresses at 9PM. 11 | # Uncomment one of the two lines below. The first one will not log any output from the script. The second will send it to a text file. 12 | #0 21 * * * /usr/bin/php /add_block_firewall.php 192.168.200.10 192.168.200.11 192.168.200.12 > /dev/null 13 | #0 21 * * * /usr/bin/php /add_block_firewall.php 192.168.200.10 192.168.200.11 192.168.200.12 >> /var/log/blockfile.txt 2>&1 14 | 15 | 16 | # Unblock these IP addresses at 7AM. The same applies here. 17 | #0 7 * * * /usr/bin/php /del_block_firewall.php 192.168.200.10 192.168.200.11 192.168.200.12 > /dev/null 18 | #0 7 * * * /usr/bin/php /del_block_firewall.php 192.168.200.10 192.168.200.11 192.168.200.12 >> /var/log/blockfile.txt 2>&1 19 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | set_debug($debug); 31 | $loginresults = $unifi_connection->login(); 32 | 33 | 34 | /** make sure firewall rule exists **/ 35 | $data = $unifi_connection->list_firewallrules(); 36 | $rule_idx = array_search($rule_name,array_column($data,'name')); 37 | if ($rule_idx !== false) { 38 | /** echo json_encode($data[$rule_idx], JSON_PRETTY_PRINT); 39 | **/ 40 | $rule_id = $data[$rule_idx]->_id; 41 | echo "Id is ", $rule_id, "\r\n"; 42 | } else { 43 | echo "Firewall rule $rule_name not found! exiting....", "\r\n"; 44 | exit(1); 45 | } 46 | 47 | /** return all firewall groups **/ 48 | $data = $unifi_connection->list_firewallgroups(); 49 | 50 | /** search for group name as specified above **/ 51 | $grp_idx = array_search($group_name,array_column($data,'name')); 52 | 53 | 54 | if ($grp_idx !== false) { 55 | $group_id = $data[$grp_idx]->_id; 56 | echo "Id is ", $group_id, "\r\n"; 57 | } else { 58 | echo "Firewall group $group_name not found! exiting....", "\r\n"; 59 | exit(1); 60 | } 61 | 62 | echo "Before....\r\n"; 63 | echo json_encode($data[$grp_idx], JSON_PRETTY_PRINT); 64 | 65 | /** loop through all ip addresses passed via command line **/ 66 | 67 | for ($i = 1; $i < $argc; $i++) { 68 | echo "Blocking address $argv[$i]\r\n"; 69 | 70 | $ip_addr=$argv[$i]; 71 | 72 | /** search for IP address in existing group **/ 73 | if (!in_array($ip_addr,$data[$grp_idx]->group_members)) { 74 | echo "IP Address not found! Adding....", "\r\n"; 75 | array_push($data[$grp_idx]->group_members, $ip_addr); 76 | } else { 77 | /** already on list, do nothing **/ 78 | echo "$ip_addr is already on list", "\r\n"; 79 | continue; 80 | } 81 | } 82 | 83 | echo "After....\r\n"; 84 | echo json_encode($data[$grp_idx], JSON_PRETTY_PRINT); 85 | 86 | /** commit changes **/ 87 | $data = $unifi_connection->edit_firewallgroup($data[$grp_idx]->_id,$data[$grp_idx]->site_id,$data[$grp_idx]->name,$data[$grp_idx]->group_type,$data[$grp_idx]->group_members); 88 | 89 | if (!$data) { 90 | $error = $unifi_connection->get_last_results_raw(); 91 | echo json_encode($error, JSON_PRETTY_PRINT); 92 | } 93 | -------------------------------------------------------------------------------- /del_block_firewall.php: -------------------------------------------------------------------------------- 1 | set_debug($debug); 31 | $loginresults = $unifi_connection->login(); 32 | 33 | /** make sure firewall rule exists **/ 34 | $data = $unifi_connection->list_firewallrules(); 35 | $rule_idx = array_search($rule_name,array_column($data,'name')); 36 | if ($rule_idx !== false) { 37 | /** echo json_encode($data[$rule_idx], JSON_PRETTY_PRINT); 38 | **/ 39 | $rule_id = $data[$rule_idx]->_id; 40 | echo "Id is ", $rule_id, "\r\n"; 41 | } else { 42 | echo "Firewall rule $rule_name not found! exiting....", "\r\n"; 43 | exit(1); 44 | } 45 | 46 | /** list all firewall groups **/ 47 | $data = $unifi_connection->list_firewallgroups(); 48 | 49 | /** search for group name as specified above **/ 50 | $grp_idx = array_search($group_name,array_column($data,'name')); 51 | 52 | if ($grp_idx !== false) { 53 | $group_id = $data[$grp_idx]->_id; 54 | echo "Id is ", $group_id, "\r\n";; 55 | } else { 56 | echo "Firewall Group $group_name not found! exiting....", "\r\n";; 57 | exit(1); 58 | } 59 | 60 | echo "Before....\r\n"; 61 | echo json_encode($data[$grp_idx], JSON_PRETTY_PRINT); 62 | 63 | /** loop through all ip addresses passed via command line **/ 64 | 65 | for ($i = 1; $i < $argc; $i++) { 66 | echo "Removing address $argv[$i]\r\n"; 67 | 68 | $ip_addr=$argv[$i]; 69 | 70 | $pos = array_search($ip_addr,$data[$grp_idx]->group_members); 71 | 72 | if ($pos !== false) { 73 | echo "position is $pos\r\n"; 74 | 75 | echo "IP found! Deleting....", "\r\n";; 76 | unset($data[$grp_idx]->group_members[$pos]); 77 | $data[$grp_idx]->group_members = array_values($data[$grp_idx]->group_members); 78 | 79 | } else { 80 | echo "$ip_addr not found!\r\n", "\r\n";; 81 | continue; 82 | } 83 | } 84 | 85 | echo "After....\r\n"; 86 | echo json_encode($data[$grp_idx], JSON_PRETTY_PRINT); 87 | 88 | $data = $unifi_connection->edit_firewallgroup($data[$grp_idx]->_id,$data[$grp_idx]->site_id,$data[$grp_idx]->name,$data[$grp_idx]->group_type,$data[$grp_idx]->group_members); 89 | 90 | if (!$data) { 91 | $error = $unifi_connection->get_last_results_raw(); 92 | echo json_encode($error, JSON_PRETTY_PRINT); 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Block IPs on UDM/UXG firewalls 2 | 3 | ## Distributed under MIT license 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | ## Project Notes 12 | **Author:** Carlos Talbot (@tusc69 on ubnt forums) 13 | 14 | # About 15 | 16 | This docker container allows you to block and unblock IP addresses from your Unifi firewall list at certain times of the day. This can be useful when you have kids and you want to restrict their Internet usage. Using the great php client class from malle-pietje ( https://github.com/Art-of-WiFi/UniFi-API-client ), 17 | this script will automatically add and delete IP addresses from a firewall group defined on your Unifi controller. This firewall group has a predefined rule in the "WAN OUT" section to automatically 18 | drop traffic from any IP on the list. 19 | 20 | The script makes Unifi RESTful API calls to dynamically update the firewall group with IPs you pass to it via a scheduler (cron). 21 | 22 | # Preparation 23 | 24 | The docker containter can run on a Linux amd64 server or directly on a UDM/UDM Pro. Before you pull down the image you'll need to grab a couple of files first. For the example 25 | below we will be running directly on a UDM which explains the paths. If running on a Linux amd64 server just replace /mnt/data with a persistent path on your server. 26 | 27 | ``` 28 | mkdir -p /mnt/data/blockips-unifi 29 | curl -Lo /mnt/data/blockips-unifi/config.php https://github.com/tusc/blockips-unifi/blob/master/config.php?raw=true 30 | curl -Lo /mnt/data/blockips-unifi/crontab https://github.com/tusc/blockips-unifi/blob/master/crontab?raw=true 31 | ``` 32 | You then need to log into the Unifi controller, under classic settings go to Routing and Firewall, Firewall, Groups. Create a new group called "Block_Group". 33 | ![Blockgroup](/images/blockgrp.png) 34 | You need to populate it with at least one fake IP address as you cannot have empty firewall groups. Pick an address you don't use, for example an RFC1918 address not part of your subnet. 35 | 36 | ![Blockgroup](/images/blockgrp2.png) 37 | 38 | Now go to Firewall, Rule IPv4, WAN OUT. Create a new firewall rule. 39 | 40 | ![Blockgroup](/images/firewall1.png) 41 | 42 | You can call it whatever you want, as long as it references the firewall block group we previously created. Make sure you select the Drop Action and select the Block_Group at the 43 | bottom under Source. Leave everything else as the default. 44 | 45 | ![Blockgroup](/images/firewall2.png) 46 | 47 | At this point we're ready to edit the files we downloaded at the beginning of this section. Using your preferred editor go ahead and open up config.php on the UDM or Linux server. You'll need to populate your unifi credentials for the controller (controlleruser, controllerpassword) as well as the url (controllerurl). 48 | 49 | In addition, make sure the site name is correct along with the firewall group name and firewall rule name. If you used the defaults above for the firewall group and firewawll rule then 50 | there's no need to change the values. 51 | 52 | Finally, we need to edit the crontab file that schedules when the scripts run to add and remove IPs from the firewall group. You should see two examples for IP address additions and deletions. Uncomment one of the lines for each and make sure your list of IPs to block is correct. The first option does not log any activity. The second logs to a file on the container under /var/log. If your not familiar with cron's syntax take note of the first line in the file as it states what each of the columns represent. I've setup an example in the file for blocking IPs at 9 PM and removing the blocks at 7 AM. 53 | In the next section you'll want to make sure you specify your local timezone as this reflects when the cron jobs will run. 54 | 55 | # Installing 56 | 57 | 58 | If the container will be running on the UDM/pro you need to also install the CNI Macvlan tools for docker. On a typical Linux server these tools are already installed. You can use boostchicken's script for this which is refernced on this page: https://github.com/boostchicken/udm-utilities/blob/master/dns-common/on_boot.d/10-dns.sh. 59 | From the udm login prompt, enter the following (one time install) 60 | 61 | ``` 62 | ## network configuration and startup: 63 | CNI_PATH=/mnt/data/podman/cni 64 | if [ ! -f "$CNI_PATH"/macvlan ]; then 65 | mkdir -p $CNI_PATH 66 | curl -L https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-arm64-v0.8.6.tgz | tar -xz -C $CNI_PATH 67 | fi 68 | 69 | mkdir -p /opt/cni 70 | rm -f /opt/cni/bin 71 | ln -s $CNI_PATH /opt/cni/bin 72 | ``` 73 | In order to start the containter you will need to type the command below:
74 | **NOTE** Make sure to update the timezone variable TZ to your local timezone. You can find a list here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones 75 | 76 | ``` 77 | docker run -it -d --name blockips-unifi -e "TZ=America/Chicago" \ 78 | -v /mnt/data/blockips-unifi/config.php:/config.php \ 79 | -v /mnt/data/blockips-unifi/crontab:/etc/crontabs/root \ 80 | tusc/blockips-unifi:latest 81 | ``` 82 | This will download the latest image to your server. 83 | 84 | If you're running this containter on a UDM and have to reboot, you can manually restart the containter with the commmand below: 85 | ``` 86 | docker start blockips-unifi 87 | ``` 88 | Fortunately you can also take advantage of boostchicken's great tool to automatically start a Docker container after a reboot: 89 | https://github.com/boostchicken/udm-utilities/tree/master/on-boot-script 90 | 91 | If you're interested in compiling your own version I have a Dockerfile available here: https://github.com/tusc/blockips-unifi/blob/master/Dockerfile 92 | 93 | # Validating configuration 94 | 95 | You can test your credentials are valid by adding a test IP to the block list (container needs to be up and running): 96 | 97 | ``` 98 | docker exec -it blockips-unifi php /add_block_firewall.php 192.168.200.10 99 | ``` 100 | Watch the firewall group page on the Unifi controller and you should see the count go up by one. Try to access the Internet with that address. 101 | 102 | Conversly, removing the IP from the list can be done with this command: 103 | 104 | ``` 105 | docker exec -it blockips-unifi php /del_block_firewall.php 192.168.200.10 106 | ``` 107 | 108 | # Uninstalling 109 | 110 | To remove the docker instance and image you'll need to type the following at the UDM ssh prompt: 111 | 112 | 113 | ``` 114 | docker stop blockips-unifi 115 | docker rm blockips-unifi 116 | docker rmi docker.io/tusc/blockips-unifi 117 | rm -rf /mnt/data/blockips-unifi 118 | ``` 119 | Lastly, remember to remove the firewall block rule and firewall group from the controller. 120 | 121 | --------------------------------------------------------------------------------