wp-cli CentOS rpms

wp-cli is a really, REALLY useful tool. I’m pretty much using it on all of the wordpress servers that I manage now. But it can be a little awkward to install / keep up to date. And its usually nicer to have something managed by the OS’s package manager.

As such, I’ve created wp-cli rpms for EL6 and EL7, incase anyone wants to use them. The intention is to keep them up to date with the latest version. I’ll create a repo at some point, as there may be other tools that I would rather as rpms, but for now..here’s the download links:

CentOS 6 (i386 and x86_64)


CentOS 7 (x86_64)



…and the source RPM



WordPress Webroot Ownership / Permissions Script

Ok, so I’ve seen a fair amount of “run this script to fix your permissions” posts about, and for a number of reasons I don’t really like them.

Not saying that mine is perfect ofcourse, but the emphasis is trying to be around security.

With all security, you’re always compromising convenience. If you really want to secure your WordPress file ownership and permissions. Give it user:apache and 750/640 permissions throughout. But it won’t do you much good when it comes to installing plugins, etc.

So, here’s what you can do.

NOTE: The outcome of this script (should you read it and use it properly) is that your wordpress web root will be read only to apache (this is how we want it really) and wp-content and below will be writeable by apache (you need this if you want to install plugins, upload media). Having apache write to wp-content is the compromise here, because its undesirable for the web process to have write permissions…but we’re balancing security and convenience here. Because apache cannot write to a directory higher than wp-content, then it will NOT be able to update itself. So you’ll need to organise that when the times comes. I might write a post about that, but because it sucks to have apache writing to the webroot, I possibly won’t


## ok, you want to replace my ‘dcr226’ with whatever user you
## login to your server with. Change all of these settings to suit your system


chown -R $username:web_user $web_directory
find $web_directory -type d -exec chmod 750 {} \;
find $web_directory -type f -exec chmod 640 {} \;
find $web_directory/wp-content -type d -exec chmod 770 {} \;
find $web_directory/wp-content -type f -exec chmod 660 {} \;

#selinux stuff because…you should be using it

chcon -R -t httpd_sys_content_t $web_directory
chcon -R -t httpd_sys_rw_content_t $web_directory/wp-content
setsebool httpd_unified 0


WordPress IPtables – Security Tips – How To

WordPress IPTables

Please note, this is a working document. If you see anything thats missing, or needs more info..please do let me know

We really don’t want much in the way of outbound traffic coming from File our WordPress install. Malicious scrips and plugins will try to “phone home” or potentially deny someone else’s webserver of service, or possibly download more malicious scripts to be used on your website.

Unfortunately, in some cases we *want* wordpress to connect outbound, for example in the case of installing new plugins.

Now, if you want to install your plugins and themes manually you don’t need to do any of this, you can just upload the zip file onto your webserver, and unzip it into the wp-content/plugins directory. However, in the event that you want to search and install plugins from the dashboard, here’s how you can configure iptables.

The following assumes that your default firewall policy is to DROP outbound packets (OUTPUT -P DROP)

During my logging, it seems that the dashboard uses the same host/IP address when querying plugins ( which does indeed have a wordpress PTR record ( domain name pointer wordpress.org.) and when used in the browser resolves to wordpress.org. We need to allow outbound packets on port 80 from our server to this ip address.

the basic rule is:

OUTPUT -p tcp –dport 80 -d -m conntrack –ctstate NEW –syn -j ACCEPT

Now there are a couple of ways you Dinner can do this, you can insert this rule into the top of the OUTPUT chain with

iptables -I OUTPUT -p tcp --dport 80 -d -m conntrack --ctstate NEW --syn -j ACCEPT

or you can cheap NBA jerseys hand edit /etc/sysconfig/iptables and add the rule into the chain wholesale jerseys there.

Personally, I wanted to keep it a little tidier so created a new chain for wordpress (OUTPUT_WP) and Year added it there.


Turns out WordPress will try port 80 first (http) and then fall onto port 443 (https). Seeing as SSL is much more desirable in this context, we can actually allow to the destination port (–dport) 443.

Also, it would seem that WordPress have more ip addresses in that range, and although it will still work with just the one IP address..seems to make sense to Markierung allow it to failover onto another quickly enough. I’ll add more of these as time goes by.

So now we have:

OUTPUT -p tcp --dport 443 -m iprange --dst-range -m conntrack --ctstate NEW --syn -j ACCEPT

A word about email

In order for your wordpress instance to send email, it not only needs to have a MTA (postfix or sendmail perhaps) but needs to egress on port 25 (TCP)

OUTPUT -p tcp cheap MLB jerseys --dport smtp -m conntrack --ctstate NEW --syn -j ACCEPT


WordPress Security – File Permissions

We’ve been working recently on security, in this particular document we’re focussing on wordpress file and directory permissions.

This is a working document, which should be subject to updates and revisions

Firstly, there is no reason that I can find..that any of the files or directories in a wordpress installation should be world readable, so to that effect we can assume all permissions will end in a “0”, removing any world-readable permissions.

Secondly, we shouldn’t have the web user (apache, http, www-data for example) owning any of the files. In the very least a human should own the files in the web root, and the web process user should be accessing based on group permissions. The reason for this is, the owner can change file permissions as he wishes..which may well be undesirable in the event that a malicious script is executed for example. Additionally, we want the user to be able to write to files..whereas the web server process usually only a needs to *read* files. So in the very first place we’d do About something like this

[plain]chown -R (your username):(web server group) /path/to/webroot/[/plain]

so in the case of my system, I’d perhaps do something like this:

[plain]chown -R dcr226:apache /var/webroot/www[/plain]

So onto actual permissions for these user/group combinations. As stated before, there is no good wholesale mlb jerseys reason to have anything in the webroot world readable. So from an overarching perspective, files can How be 640 and directories 750. To explain how this works:

Files – 640 permission

Owner has read/write access (in this case a human owner, *not* the web server) (rw)
Group members have read access (in this case the web server’s user – perhaps apache, httpd or www-data) (r)
Everyone else has no access whatsoever (-)

Directories – 750 permission

Basically the same as above, with the exception that directories need to execute in wholesale jerseys order to change directory, etc. So both the owner and group get execution permissions (rwx)

Fixing the file permissions:

So, now we know what file permissions we want to issue..its time to do the work. This can be done as follows:

[plain]find /path/to/webroot -type d -exec chmod 750 {} ;[/plain]

the above command searches (find) through the defined web root, looking for directories (-type d), then executes chmod 750 on each of them. Should take a few seconds to complete.

[plain]find /path/to/webroot -type f -exec chmod 640 {} ;[/plain]

the above command does the same, but this time for actual files, instead of directories. ‘find’ in this case will recurse through directories.


so it looks like centos6 at least ships with the httpd_unified boolean set. With the boolean on, Apache processes can read/write/execute all httpd_sys_content* labels. This isn’t what we want to achieve.

[plain]setsebool -P httpd_unified 0[/plain]

then, we can create read only selinux labelling for the web root using:

[plain]semanage fcontext -a -t httpd_sys_content_t “/path/to/www(.*)?”
restorecon -Rv www[/plain]

now, apache simply isn’t allowed to write to any files, or execute any files in the webroot.


you’ve got a choice here, you can either manually copy/paste and .htaccess settings into the file yourself, or allow apache to write to the file using the following permissions

[plain]chown dcr226:apache /path/to/www/.htaccess
chmod wholesale jerseys 660 /path/to/www/.htaccess[/plain]

then set the correct selinux context..

[plain]semanage fcontext -a -t httpd_sys_rw_content_t /path/to/www/.htaccess
restorecon -v /path/to/www/.htaccess[/plain]

some things need write contexts and permissions…

Apache needs to write to the wp-content/ directory in the very least to add new media, themes and plugins, so we need to make that happen with file permissions and a selinux context.

[plain]find /path/to/webroot/wp-content -type d -exec chmod 770 {} ;

find /path/to/webroot/wp-content -type f -exec chmod 660 {} ;
semanage fcontext -a -t httpd_sys_rw_content_t “/path/to/www/wp-content(.*)?”
restorecon -Rv /path/to/www/wp-content[/plain]

Optional – protect xmlrpc.php

unless you’re posting using a desktop/mobile application, or connecting to the xmlrpc api, then you likely want to prevent access to xmlrpc.php. This can be done a couple of ways, one of which is using your httpd.conf (or relevant config file in the drop directory) as such

Inside of your <Directory>…</Directory> directive

[plain]<Files xmlrpc.php>
Order allow,deny
Deny from all


When Its Time To Update The Whole Thing

When you need to update wordpress core files, if you want wordpress to update itself, then you’re going to need to give the web process writeable permissions cheap jerseys throughout the webroot..

[plain]find /path/to/webroot -type d -exec chmod 770 {} wholesale jerseys ;

find /path/to/webroot -type f -exec chmod 660 {} ;

and if you’re using selinux..

[plain] chcon -R -t httpd_sys_rw_content_t /path/to/webroot


Not forgetting to Breach re-set Welcome them again afterwards!