Securing Liferay Chapter 1: Introduction, Basics and Operating System Level

You probably know the basic installation instructions for Liferay Bundles: „unzip and run startup.sh“ - with this you get to a working Liferay installation in a minute.

While this is great for a quick demo, you might want to do more in case of production setups. This is a part 1 of summary of a workshop held at Liferay's North America Symposium 2014 in Boston. Like in the workshop, it won't give prescriptive information – e.g. you won't be able to hit the „secure“ checkbox – but will have to judge the settings you find for yourself. Also, this guide is not complete: Security is well depending on the general setup, requirements and policies. What works for one is not enough for somebody else. And vice versa. This summarizes things that work for me and that I see working for others. I encourage you to comment on this article if there are aspects that aren't covered but should be taken into account when securing the setup.

The sample setup we'll do (if you want to follow along) uses „Tomcat on Linux with MySql“ as platform. However, I intend to discuss or demonstrate the underlying problems, so that you can still get quite some information out of this series if your platform differs.

Operating System Level

For the purpose of you following along, I'm assuming that the bundle has been deployed to /opt/liferay already. If you want to keep the directory names as they're contained in the zip file, you can achieve this with (pseudocode)

   sudo unzip /path/to/your/liferay-portal-tomcat-6.2-ee-sp8-*.zip /opt/
   sudo ln -s /opt/liferay-portal-tomcat* /opt/liferay
   sudo ln -s /opt/liferay/tomcat-7.0* /opt/liferay/tomcat
   sudo mkdir /opt/liferay/deploy

As you can see, I prefer to have easy pathnames and I've omitted some of the timestamps and version indicators to make the directory structure more readable. If you have multiple versions, the wildcard might do more than you expect.

Typically Production Liferay Systems should listen to port 80 and 443. The easiest way to achieve this is by changing port 8080 to 80 in tomcat's conf/server.xml and then run

   sudo /opt/liferay/tomcat/bin/startup.sh

e.g. run tomcat as root. If you're frightened by this, read on. If you're not frightened by this line, you should: This is the first mistake to avoid. You simply don't want any internet-connected software to be running as root. So the first change is a no-change: Keep tomcat's standard ports (8080) for now – we'll take care of the port issue later – as a first step, we just don't want to run as root.

(Editorial question: Does this statement mislead the quick reader to actually run as root? Should I rephrase this part of the article?)

User Account

To begin, create or identify a specific user account for tomcat. Adjust the actual permissions on the system to minimize access and match your policy. Simplified:

   adduser –system liferay

So, in case I mislead you above: It's really vital that you never run an internet connected process as root. Please don't ever do this. We're using an account that is not allowed to log in to the server, e.g. has no shell (-system). Use what's appropriate on your platform.

Database driver

Before actually starting up tomcat and Liferay, let's make sure the database driver is available. For the purpose of this article, I'm using ubuntu and its bundled mysql, along with the JDBC-Driver (sudo apt-get install libmysql-java). This ensures that I do get operating system level upgrades. Naturally this differs for a different database - especially for commercial ones, you still have to take care of driver updates.

   sudo ln -s /usr/share/java/mysql.jar /opt/liferay/tomcat/lib/ext/

(mnemonic trick to remember the order of parameters for ln: they're following the same semantics as cp – Duh. I felt stupid when sb told me this. Not that I want you to feel this way as well...)

Starting a Daemon & fixing file permissions

Now you might be tempted to already start up our server (sudo -u liferay /opt/liferay/tomcat/bin/startup.sh) but it would still signal several issues when writing temporary- and work files: You probably have unzipped the bundle as a different user, so that “liferay” can't write the temp files. As we want to run tomcat as a daemon anyway, we'll create a script to achieve this and have it do the work of preparing the proper permissions. Here's one that works for me – I don't claim particular shellscript-elegance. (Edit: Read the comments as well as Brett's script) Use your favorite editor to create /etc/init.d/liferay and make it executable:

# Liferay NAS Symposium Boston 2014 auto-start
#
### BEGIN INIT INFO
# Provides:          liferay
# Required-Start:    $apache2 $mysql
# Required-Stop:     $apache2
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# X-Interactive:     true
# Short-Description: Start/stop tomcat server bundled with liferay
### END INIT INFO

export JAVA_HOME=/usr/lib/jvm/default-java

# If Liferay has ever been started (or unzipped) with a different user 
# account than what it's running as, we need to correct permissions.
#
# cd /opt/liferay
# chown -R liferay data deploy
# cd tomcat
# chown -R liferay webapps conf temp logs work
# cd /opt/liferay/tomcat

# run on hardened permissions by default
# chown -R root webapps conf ../deploy

cd /opt/liferay

case $1 in
start)
        # run on softened permissions by default: might need to deploy hooks etc. on startup
        chown -R liferay tomcat/temp tomcat/logs tomcat/work tomcat/webapps tomcat/conf deploy
        sudo -u liferay /opt/liferay/tomcat/bin/startup.sh
        ;;
stop)  
        sudo -u liferay /opt/liferay/tomcat/bin/shutdown.sh
        ;;
soften)
        # can and should be run when the server is running.
        chown -R liferay webapps conf ../deploy
        ;;
harden)
        # can and should be run while the server is running.
        chown -R root tomcat/webapps tomcat/conf deploy
        ;;
restart)
        sudo -u liferay /opt/liferay/tomcat/bin/shutdown.sh
        sleep 5
        sudo -u liferay /opt/liferay/tomcat/bin/startup.sh
        ;;
esac   
exit 0

Note the nonstandard options “soften” and “harden” - we're discussing them later, but maybe it already gives you an idea of what to do with them. (want to contribute to the elegance of this works-for-me script? You might want to add checks to make sure that the directories are actually existing.

Once you have this script (remember to chown root, chmod u+x, chmod go-rwx it)

    sudo service liferay start

With this, we've covered most of the OS side of configuration and the appserver side of Liferay has advanced quite some way. An additional issue that I've not yet taken into account is to run with a Security Manager. Also, we'll leave the port issue (we're still running on port 8080) for later.

Note that an even easier way to cover the OS side is to rely on your operating system's methods to update tomcat - e.g. just install Liferay's WAR distribution and dependencies on top of ubuntu's tomcat (in our example). This would be easier, but it would demonstrate less principles of the installation. But it would come with some nice and fancy daemon script.

Remember: This does not claim to be the absolute truth - please add your own recommendations and different policies/practices. Security never has the absolute truth: If I'd show you how to absolutely nail the implementation you'd complain that nothing works any more. Security is a matter of policy, as much as it is a matter of experience, absence of stupid mistakes and some things more.

Future chapters

  • Securing Liferay's configuration
  • Fixing the port 8080 issues
  • more Tomcat lockdown

...coming soon...

Also, another Radio Liferay episode on security is in the can - scheduled to be published very soon.

Blogs
Nice article. One thing though: I have noticed, that sometimes tomcat won't stop. In my experience something like this needs to be done:

if (isRunning)
... tomcat/bin/shutdown.sh
sleep 5
if (isRunning)
kill $PID
sleep 5
if (isRunning)
kill -9 PID

Using ps & grep isRunning can easily be implemented.

Minor note: I don't think that X-Interactive: true is a necessary/useful. But since you didn't mention chkconfig (or update-rc.d) at all, the comment is probably moot).
Good point, thanks. That's the "missing shellscript elegance" emoticon

As restarting is a manual operation, I'm typically at the server for the operation - able to kill -9 when necessary. But I'll try it out and add it to the script. It's probably even already contained in ubuntu's standard tomcat script...
Based on my current workload, it'll take a while. But as the missing piece is already clearly pointed out, this is probably not an issue.
It is good to read something about security. Liferay is quite reliable compared to other framework written in PHP. However, what are the good practices to install a bullet proof Liferay? Thank you for this tutorial.
I would have thought that you would recommend to install Liferay from the WAR archive deployed in a fresh installed Tomcat. This would prevent from the init script hassle and the problem of log rotation issue. By experience, Liferay and other Java applications are quite verbose, for the sake of developers, but the catalina.out file is reaching very quickly a critical size. With an application server or a Tomcat installed, the distribution system takes care of the init script and of the log rotation. And I think that it is better for administrators to use a Tomcat from the distribution repository than the Tomcat provided with the Liferay bundle.
Thanks a lot Olaf for your article.
Hi Patrick,
Thanks for your feedback. I agree that the standard Linux distribution tomcat is a lot better suited. I'll add this to the third chapter, when I come back to tomcat configuration. However, the bundle is really popular due to its ease of use, and too often I just see the "sudo startup.sh", e.g. the bundle being run as root. For anybody who loves suggesting this, I'm now providing a place to link to (I had to correct this over and over again). Logfile sizes are a good thing to keep an eye on - and you're bringing them onto my radar for the next chapters. While not purely security/hardening related, they're on a border line and an out-of-control logfile can be hazardous for the system.
Thanks again.
Olaf
Olaf, thanks for your comment and "thumb up".
By the way, even though it is trivial and straightforward to install Liferay from the provided bundle, I came accross some threads in message boards regarding installation of Liferay from the WAR file, either in Tomcat or JBoss. This is documented but the documentation is sometimes ambiguous and people are still struggling to install properly Liferay this way. I may send you a document outlining Liferay installation on a production environment as i don't know where I could publish it here so that the whole community can share and update it.
Hi Patrick,
if you want to - you can write a blog entry yourself: https://www.liferay.com/de/web/james.falkner/blog/-/blogs/community-blogging-now-availa-11 or participate in updating the documentation on the new dev/docs site - see https://dev.liferay.com/participate. But I'm happy to accept your input as well: Your choice.
Especially if the information in the user guide is incomplete or misleading, I'm very interested in seeing it corrected.
Very useful series of articles Olaf, thanks.

I've been a sysadmin longer than I care to remember, and over the years have made a few generalizations about scripts that make them more readable and easier to follow as well as more secure.

First, to expand on Christoph's points - the isRunning can be a conglomeration of ps and grep or the more recent application that seems to be coming on linux and solaris called pgrep. If you have multiple application servers however this can be difficult to manage - and it might be better to use a more specific grep string against the arguments of the running process...

Second, I try to avoid the use of sudo in my root level scripts, largely because my environment does not allow root to use sudo, but also because a unix level utility already exists to execute something as another user - su. Replacing your calls to sudo with su would cover that script without too much problem.

Third, if you put the comments near the actions it's easier to follow, as opposed to the script where you have your comments at the top detailing what you want to do later... isn't it better to comment the lines as you execute them? I know it's picking nits, but as time goes on you find little things make your life simpler as an admin, and that's always a good thing.

Thanks for listening, and keep the good articles like this one coming!

-- Charles
Hi Charles,
Thanks for your feedback. You're right in all points. The update that I was talking about in the reply to Christoph is still pending - until then I rely on readers to include these comments in their quest for a secure installation. All of this will be solved by following Patrick's advice to use the distribution's tomcat for Liferay (and install the WAR distribution) - at least that's a suggestion that I've included in chapter 4. But I want to make this script more elegant too - and it's good that this triggers discussion and feedback.
I totally forgot about Brett's tomcat startup script - even though I even commented there... https://www.liferay.com/web/brett.swaim/blog/-/blogs/sample-tomcat-startup-scripts
[...] Disable "create new accounts" if you don't want random users to create new accounts (e.g. in an intranet) JSONWS access Disable Control Panel, add "My Account" to user's personal pages instead The... [...] Read More