I have a community-oriented bulletin board running on phpBB3. I want to limit its users to local residents - or at least residents of my own country. I have nothing against people living elsewhere; it’s just that someone living in Chile probably won’t be interested in my little website in a small town in the USA unless they’re wanting to post spam to it. Here’s how I did it.
Note: I’m using Apache 2.2 on a FreeBSD server. The details for other combinations will be a little different but very similar.
Download a list of networks in my country
Country IP Blocks is a website that lists the network blocks associated with each country. While this isn’t guaranteed to be 100% accurate, it’ll be pretty close. After digging around on the site, I found that the list of all network blocks in the United States is at https://www.countryipblocks.net/e_country_data/US_allow.txt. I downloaded this to my Apache server’s Includes directory:
# cd /usr/local/etc/apache22/Includes
# curl -O https://www.countryipblocks.net/e_country_data/US_allow.txt
Block access to the User Control Panel
The User Control Panel, or UCP, is the phpBB page that handles registration and profile editing and all that other good stuff. By limiting access to that page (located at /ucp.php on my site) to only the network blocks in the list I downloaded in the first step, I block anyone outside the US from registering an account. They can still read my public forums - they just can’t participate.
I edited the Apache configuration file for my domain to add this clause:
<Location /ucp.php>
Order Deny,Allow
Deny from All
Include etc/apache22/Includes/US_allow.txt
Allow from ::
</Location>
The <Location> tag says that the following settings will only apply to the /ucp.php page.
The Order Deny,Allow directive tells Apache to obey any “Deny” directives by default, and then override those results with any matching “Allow” directives it finds.
The next line, Deny from All means exactly that: block all users unless their IP address is listed in one of the blocks I downloaded in the first step.
The fourth line instructs Apache to slurp in all of the allow from ... lines from the network list file. I chose this approach over putting the instructions in a more common .htaccess file for two reasons:
- If I want to update my list of network blocks, I can just re-download that file and restart Apache to reload its contents. Since I’ve never added any other directives to that file, I don’t have to worry about merging any records or remembering to copy the “extra” statements.
- When you put directives in a
.htaccessfile, those directives are reloaded every time a visitor tries to view a page - any page. As of this moment, there are nearly 40,000allow fromdirectives in the network block list and that’s a lot of extra work for something that won’t change very often. ByIncludeing its contents directly into the Apache configuration, those directives are only loaded and interpreted once whenever the webserver starts. The downside is that I’ll have to remember to restart Apache when or if I ever update the network block list.
Next is a directive to allow all IPv6 visitors. If I ever start receiving spam from IPv6 clients, I’ll reconsider that option.
The trailing </Location> line says that we’re done with this snippet.
Relax and enjoy the show
Soon after restarting Apache, “deny” messages started filtering into my site’s error log:
[Wed Jun 29 14:10:16 2011] [error] [client 77.93.2.81] client denied by server configuration: /usr/local/www/phpBB3/ucp.php, referer: http://norfolkboard.com/ucp.php?mode=register
Interpretation: someone in Latvia tried unsuccessfully to register an account on my site. While it’s remotely possible that a local resident is visiting European relatives and suddenly wanted to check my site, the odds are much greater that I just blocked a would-be spammer from opening an account.