You are on page 1of 24

M y SQL

Magazine
Summer 2007 Issue 1

How
Secure
Is
Your
Server?
Welcome to MySQL Magazine. This is the magazine for the community of MySQL database
administrators and developers who use MySQL on a daily basis to provide some of the best
infrastructure available on the Internet.

What will you find here? News, articles on both administration and devlopment, feedback from the
users. Simply anything relating to MySQL!!

MySQL in the News

The biggest news of the past few months for MySQL users has to be the MySQL Conference that was
April 23rd through the 26th. If you didn’t go-you missed out! There were many speakers including our
own Baron Schwartz. Topics ranged from building your basic knowledge, improving the performance
of your servers and quite a few presentations on how large “Web 2.0” companies such as Digg and
Flickr handle their traffic loads. Many of the speakers have their slides available for viewing. You can
find them and other informaion about the conference at: http://www.mysqlconf.com. In addition there
are several people in the MySQL community who have graciously supplied audio and video from the
conference. An RSS feed of mp3s that will work with itunes is at http://download.tailrank.com/mysql-
conf/mysqlconf.rss. Kevin Burton created these mp3s from ogg vorbis recording that Baron Schwartz
made (available at http://www.xaprb.com/presentations/mysqlconf2007/). In addition, Sheeri Kritzer,
has made available her recording (both audio and video) of presentations at the conference. They are
available at http://technocation.org/index.php?option=com_content&task=view&id=54
As an aside, Sheeri does a great MySQL podcast called OurSQL (available from the itunes store).

Next year the conference will be April 15-18. Go ahead and make plans to attend. You won’t regret it!

MySQL AB has also recently announced the addition of two new certifications. In March MySQL AB
announced the new Certified MySQL Cluster DBA. Next, in April, MySQL AB announced the new
Certified MySQL Associate. The CMCDBA (rolls off the tongue doesn’t it?) shows mastery of all facets
of the new MySQL cluster server. The CMA is a more entry-level certification showing basic
knowledge of the MySQL server.

Book Worm

One of the books on my bookshelf is Pro MySQL by Michael Kruckenberg and Jay Pipes. This is an
excellent book for someone seeking information about MySQL that goes beyond the basics. At almost
700 pages (not including index) this book is divided into two parts. The first part is devoted to the
architecture of the MySQL server and MySQL-based devlopment. The second half of the book
devoted to database administration.

Some of the topics include benchmarking/profiling (worth the cost of the book alone), storage engines,
security, backup, replication and clustering. Each is covered in-depth from true experts.

This is a well written book. If you consider yourself a professional MySQL DBA the information is
invauluble. This book makes a great reference for topics that you might not deal with on a day to day
basis. My only quible would be that it needs to be updated. It was printed in 2005 and covers up to
version five of our favorite database. In particular the informaiton on clustering is dated (because with
version 5.1 the tables are no longer required to be in memory). Even so the book is still well worth the
money.
Contents Summer 2007
3 MySQL’s Future?
6 Securing MySQL Server
9 Optimizing MySQL with Rails - Part One
12 Convenient MySQL Privilege Scripting and Versioning
14 Operating System Hardening

Regular Features
1 News
1 Book Worm
4 Coding Corner - Protecting a MySQL Database from SQL Injection Attacks - Part 1
23 log-bin
MySQL’s Future?
by Keith Murphy

So where is MySQL going? Before we examine it would be nice to see some polish applied to this
this question it is a good idea to look at the often used area of MySQL. News from the recent
current feature set of MySQL (as of version MySQL user conference has the 6.1 release
5.1.x). having online backup. In addition there should be
subquery optimization in version 6.1.
ACID compliance
transactions These are the major features slated for the next
triggers two releases of MySQL. In addition, there will
functions continue to be improvements over many areas of
cursors MySQL.
views
subqueries We are fortunate that MySQL AB and the
transactions community together have been able to implement
row-based replication almost every feature that is of significance. Even
multiple database engines though this is the case, there are areas that will
partitioning continue to move forward. Clustering is a feature
shared-nothing clustering that will continue to gain momentum as it
becomes better implemented. I find this area of
What is on the horizon for our favorite database? MySQL technology fascinating and I look forward
Certainly there has been a good deal of buzz of to the combination of growing stability and new
late about the new Falcon database engine. feature advancements of the young clustering
There has been an alpha release (6.0) of the environment.
server that includes the the new engine. Please
be forwarned that this database engine is in no About the author
way production quality. It certainly has great
promise. The Falcon db engine was created by Keith Murphy is the editor of MySQL Magazine.
Jim Starkey who is one of the pioneers of He is also the chief Dolphin at Broadwick
RDBMS. Some people think that Falcon will Corporation.
become the default engine for MySQL - especially
after Oracle bought Innodb. The expected
release date for a production version 6.0 is Q2
2008.

Another item of interest is implementation of


foreign keys for all the major database engines in
version 6.1. Currently foreign keys are only
supported on InnoDB. Foreign keys are a great
help for the developers out there in keeping the
data in the database syncronized.

Prior to the release of version 5.1 there was


discussion of the implementation of an online-
backup tool that would be superior to current
implementations. This was originally slated for
the 5.1 release. This just has not happened. As
a DBA who performs back-ups on a routine basis
Coding Protecting a MySQL Database from
Corner SQL Injection Attacks - Part I
by Peter Brawley
http://www.artfulsoftware.com

SQL injection is a method for attacking If your application's connection supports multiple
databases. The attacker "injects" elements into SQL commands in a single query call, all your
your program's SQL in order to bypass customer data just went away.
authorization or damage the database.
LEVEL 1 DEFENSE: NEGATIVE INPUT FILTERS
Web sites that send SQL commands to databases
are particularly vulnerable to SQL injection, The simplest way to prevent this sort of injection
because they often rely on dynamic SQL, and is to search the SQL string for semi-colons and
because it can be easy to mount millions of such double dashes, and remove them before passing
attacks until one succeeds. Here is a simple the statement to the database. That's easy in an
example. A PHP page asks the user for a name adequate application language, for example in
and a password, then sends this to the database: PHP:

SELECT * FROM mysql.user WHERE user = '$usr' AND $protectedqry = str_replace( "--", "", str_replace( ";", "", $qry ));
password = '$pwd';
If $qry has offending characters, sending
Is it as innocuous as it looks? Suppose a user $protectedqry to the database raises a MySQL
enters something like this as a user name: error. That provides one level of protection.

' OR 1>0; -- Better still, search the string for double dashes
and semi-colons, and if either is found then
When your application plugs that entry into your refuse to send the query to the database. If you
SQL, the command becomes: want to be really thoroughgoing, you could
blacklist the IP address that launched the attack.
SELECT * FROM mysql.user WHERE user = '' OR 1>0; --
AND password = '' Now you are fully protected against attacks that
use double dashes and semi-colons. Have you
Your intruder just retrieved all rows and columns covered all possible attacks? Not a chance,
of the mysql.user table. Not exactly what you had human ingenuity having no practical limit. For
in mind. example, a favorite trick we haven't touched on is
introduction of malevolent WHERE clauses.
Or a malevolent user might supply this username:
LEVEL 2 DEFENSE: POSITIVE INPUT FILTERS
OR 1>0; TRUNCATE customers; --
The attacker has to succeed just once. If your
whereupon your application sends this command database is to be safe, you must succeed every
to the database: time. You are on better logical ground enforcing
a simple positive validation pattern than looking
SELECT * FROM mysql.user WHERE user = '' OR 1>0;
for a limitless number of dangerous or invalid
TRUNCATE customers; -- ' AND password = ''
patterns. Positive input filters improve your
chances of success enormously.
For example, you could decide to accept only
alphanumeric characters in user names and
passwords. It is easy to enforce that rule in PHP:

if( ereg( '[^A-Za-z0-9]+', $usr.$pwd )) {


echo "<script>
alert('Alphabetic and numeric characters only, please.');
</script>";

You can formulate more stringent tests based on


specific input requirements.

LEVEL 3 DEFENSE: OUTPUT FILTERS

Finally, application languages provide generic


tools for cleaning up submissions to your
database. Again in PHP the function to use is
mysql_real_escape_string():

$qry = mysql_real_escape_string( $qry,


$connection_resource );

LEVEL 4 DEFENSE: ENCAPSULATION

Enterprise RDBMS policies usually require that all


such protective logic be encapsulated in stored
procedures. In Part II we will study how to do this
in MySQL.

Summary

To stop SQL injection attacks in their tracks, apply


simple positive and negative input filters, and
escape possibly problematic characters in what
you send to the database.

About the author

Peter Brawley is president of Artful Software


Development and co-author of Getting It Done
With MySQL 5.
Securing MySQL Server
by Keith Murphy

When looking to secure a MySQL server there are several important areas to consider. We will take
time to examine each of these areas in some detail. This will give you strong foundational knowledge
when looking at securing a MySQL server.

User accounts

A very common discussion point for MySQL security is the user accounts of the database. It is
certainly foundational to understanding the security of your server. MySQL user security is based on
an ACL (Access Control List) model. This is used for everything a user does including query
execution, connection capability and server administration capabilities. All of this information is stored
in the user table of the mysql database. Permissions are generally managed with the GRANT and
REVOKE ocmmands.

User management can be one of the more arcane areas of being a DBA. Even so, it is simply to
important to neglect. Each user should have the minimal set of permissions to perform the tasks
needed. Not every user needs to ability to drop tables/databases!! Don’t be lazy in this area as it will
bite you eventually.

By default the root user does not have a password when you set up a MySQL server. Change this
immediately by executing the msyqladmin -u root -p ‘new-password’ comand.

MySQL ships with two default users and a default 'test' table. The default users are for connecting to
the DBMS without specifying a password, so removing these users is obviously a very good security
measure. There are also entries so that tables called or starting with 'test' can be world-writable. You
will want to disable these accounts and remove the ‘test’ database.

Executing the following two commands from the MySQL monitor will achieve the desired results:

mysql> DELETE FROM user WHERE User = '';


mysql> DELETE FROM db WHERE Host = '%';

Connection methods

It is quite common to connect to a MySQL server using the command line mysql client. If you connect
to a server from your desktop using this client all your information is being sent across the network
without very strong encryption. This is something you want to avoid doing. It is a best practice to
connect the server that MySQL is running on using a tool such as ssh that provides an encrypted
communication channel and then use the client tools on the server to work with MySQL.
Network design considerations

If you are planning a network for a typical website that is publicly accessible there is one security
concern to keep in mind. It is always best to have your web server and your database server running on
separate machines. If they are not running on the same machine you will want to have the MySQL
server (which does not need to be accessed by the public) on its own private network.

Take a look at the following diagram:

As you can see we have a small network


that is publicly accessable through
72.9.253.1. Each web server will be
accessible (through the load balancer). The
MySQL server, however, is not accessible.
The 192.168.1.x network is not available
over the Internet. You would have to be on
the internal network to reach the server.
This makes it much more difficult for an
intruder to reach and attempt to compromise
the MySQL server. Of course the MySQL
server can communicate freely with the web
servers over the internal network.

When operating overTCP/IP MySQL, by


default, uses port 3306. You should have
this port blocked on your network firewall(s)
to disable access from outside.

Operating system considerations

Whatever operating system you choose to run MySQL on there are a several concerns in preparing the
operating system for use. You should always install a minimal set of packages necessary to run your
operating system. No need for the “everything but the kitchen sink” mentality. The reason why is that
every additional packages/applications installed gives an attacker another avenue of possible access.
As a side benefit, you will get better performance out of your MySQL server.

Remove any unneccessary users from the operating system. Like the extra packages, unneeded users
provide an avenue of access for an attacker. Use strong passwords for your system users. Your dog’s
name isnot a good password. It should be a mix of letters (capitalized and uncapitalized) and other
symbols. Minimally it should be eight characters long, but should probably be more in the ten to twelve
character range.

In addition, you should consider the physical security of a system. Any system can be compromised if
left unattended. Servers should be in dedicated areas behind locked doors minimally. Data center
space with locked cabinets is always a good idea if possible.
User account running the MySQL daemon

Do not run the MySQL daemon as the Unix root user. It is a very dangerous practice as any user with
FILE privileges will be able to create files as root (for example, ~root/.bashrc). To prevent this, mysqld
will refuse to run as root unless it is specified directly via --user=root option. The mysqld daemon can be
run as any user instead. You can also create a new user mysql to make things more secure. If you run
mysqld as another Unix user, you don't need to change the root user name in the user table, because
MySQL user names have nothing to do with Unix user names. You can edit the mysql.server script to
start mysqld as another Unix user. Normally this is done with the su command.

If you put a password for the Unix root user in the mysql.server script, make sure this script is readable
only by root. Check that the Unix user that mysqld runs as is the only user with read/write privileges in
the database directories.

Mysqld file options

It is important to understand the settings in your mysqld options file. This file is typically named my.conf.
There is one settings that it is critical that you examine - the skip-networking options.

The skip-networking options disables TCP/IP network connections. This is a good security feature if you
are running MySQL on the same server as the web server (in a typical Internet serving connection). If
the MySQL server is located on another server (as it must use TCP/IP to communicate with the other
server).

Resources

http://dev.mysql.com/doc/refman/5.0/en/security.html

About the author

Keith Murphy is the editor of MySQL Magazine. He is also the chief Dolphin at Broadwick Corporation.
Optimizing MySQL with Rails - Part One
By Dan Buettner
drbuettner@gmail.com

Rails’ philosophy of convention over configuration and plethora of built in helpers makes it almost
scary-easy to prototype a database-driven web application, and the transparency with which your
applicaton interacts with the database makes it easy to forget about the often-complex beast known as
your RDBMS lurking behind the scenes. As with the application’s Ruby code, it is easy to create a
basic database, and easy to change the database structure as your application evolves - but
eventually most web applications need to grow up and be deployed somewhere, and when they do,
the database needs to be up to the challenge.

MySQL is one of the most widely-used databases for Rails development and deployment. I will
present several different methods you can use to improve performance - some fairly general, some
MySQL-specific, and some Rails-specific. Index creation and caching are probably the two biggest
items in terms of potential payoff, but all the methods can be employed in various combinations for
maximum benefit.

Object-oriented languages can present special challenges to relational databases because of the
sheer number of queries executed in some cases. This isn’t a fault, really, but a result of clean,
readable, simple code. With proper testing, and a bit of tweaking, your database can be made to keep
up with your application.

Creating effective indices

When you create tables with migrations, the only default index is on the primary key - and while that
index is often useful in joins, it is unusual for that to be the only index you need on an important table.
It may be that you know right away which fields you’ll be querying on and can create useful indices
right away, or it could be that you only gather a good understanding of the nature of the data late in the
project, but at some point prior to launch you’ll want to spend some time on indices.

It is easy to go overboard creating indices: MySQL can use only one index per instance of a table per
query, and extra indices take up disk space and slow down write operations. That said, creating an
index is usually a highly effective step in increasing performance, and generally requires very little
effort to experiment with.

Multi-column indices can be a great way to hone performance in large datasets, but they do require
some care when creating them to be most useful. A multi-column index is used from left to right, with
no gaps allowed – that is to say, if you index several columns like so:

ALTER TABLE t1 CREATE_INDEX multi_col_idx (c1, c2, c3)

This index will be usable to speed up queries that specify c1, c1 and c2, or c1 and c2 and c3 in the
WHERE clause; however, it will not be usable to speed up queries against only c2, or only c3, or c2
and c3. A query against c1 and c3 would be assisted with c1 information, but not able to also use the
index on the c3 portion.

As an example you might have a table with directory-type information about a company’s employees,
and users can search by specifying a last name and, optionally, a first name.
Something like this code:

if params[:first_name].blank?
Employee.find :all, :conditions => [“last_name = ?”, params[:last_name]]
else
Employee.find :all, :conditions => [“last_name = ? AND first_name = ?”, params[:last_name], params[:first_name]]
end

In this case, a multi-column index across last_name and then first_name would be more beneficial
than separate indices on each column. Specifying last_name as the first column in the index makes
sense as (in this somewhat simplistic example) we are requiring that last_name always be present.

ALTER TABLE employees CREATE_INDEX both_names_idx (last_name, first_name)

Also be aware of MySQL’s limitation with LIKE string searches and indices, that the search string must
match from the beginning of the column. This can use an index:

SELECT * FROM employees WHERE last_name LIKE “Smith%”

while this cannot, due to the wildcard in front of Smith:

SELECT * FROM employees WHERE last_name LIKE “%Smith%”

Join tables, which link two other tables in has_and_belongs_to_many relationships, are excellent
candidates for indexing, and a case where indexing both columns can help significantly. Examine your
typical queries to determine which column to put first.

Use MySQL’s slow query log and the Rails log

Use the Rails log file in a development environment to evaluate the queries executed and the
database time for each page. Extract the queries and use MySQL’s EXPLAIN feature to see how
indices are being used. I often run a tail -f command on log/development.log in my application,
examining the output as I load each page in my Rails application and copy-pasting the queries into a
MySQL session for evaluation. Example output shows both the complete query and the execution
time:

Webpage Load (0.041033) SELECT * FROM content_items WHERE (content_items.`url_text` = 'home') AND (
(content_items.`type` = 'Webpage' ) ) LIMIT 1

Parsing the output of the EXPLAIN command can be daunting for complex queries, but with a little
reading and perseverance, and if you start with simple queries, you can get the hang of it pretty
quickly. It is especially easy to notice the lack of index usage:

mysql> EXPLAIN SELECT * FROM content_items WHERE (content_items.`url_text` = 'home') AND ( (content_items.`type` =
'Webpage' ) ) LIMIT 1 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: content_items
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 261
Extra: Using where
1 row in set (0.00 sec)
We see that possible_keys, which should be a list of any indices on columns used in the query, is
NULL, meaning there is no way we’re going to get an index hit. In fact, the type of ‘ALL’ indicates a
table scan, and rows shows an estimate of the number of rows MySQL thinks it will have to examine.
261 is small in this single table query, but in complex joins the effect on performance can be
exponential when multiple tables do not have index hits.

Knowing my application and my data, I know that I will always select on a specific value for the url_text
column, and nearly always specify a value for type. If we add an index like so, it should help a lot –
specifying the column I’ll always use first, and the column I may not always use second:

ALTER TABLE content_items ADD INDEX multi_col_idx (url_text, type)

Now my EXPLAIN output looks a lot better:

*************************** 1. row ***************************


id: 1
select_type: SIMPLE
table: content_items
type: ref
possible_keys: multi_col_idx
key: multi_col_idx
key_len: 516
ref: const,const
rows: 1
Extra: Using where
1 row in set (0.00 sec)

You can see that the number of rows MySQL thinks it will have to examine has dropped to just one.

MySQL’s slow query log can be beneficial in identifying queries that are truly slow, taking one second
or more to execute. You can specify the number of seconds in the MySQL server configuration file,
but the minimum is one second, which sometimes doesn’t give you enough granularity to find subtle
slowdowns. A query that takes, say, 90 seconds, but is only executed occasionally can cost you a lot
less than a query that takes a quarter or even a tenth of a second but is executed on every page load.

While it might be nice to speed up that 90 second query, if it’s only executed for a report that one or
two people use once a week, your time is better spent on that other query. That’s where your Rails
development log file can be quite helpful.

Next time

In part two of this article we will discuss sample data and development setups, the MySQL and Rails
query caches, a couple of common pitfalls when designing database tables and queries, and other
items relating to Rails performance on MySQL!

About the author

Dan Buettner works for Outsell, Inc., a San Francisco area market research company tracking the in-
formation industry, though he lives in Des Moines, Iowa. In a past life he worked at The Des Moines
Register, a midsize newspaper, as an IT manager, newspaper production specialist, software devel-
oper, and Sybase and MySQL DBA. He has been using MySQL since version 3.x in 1999, and Rails
since Summer 2006. This article was finished while attending RailsConf 2007.
Convenient MySQL Privilege Scripting and Versioning
by Baron Schwartz

MySQL provides several levels of privilege table are listed as SELECT, INSERT, DELETE,
granularity, and if you're like me, you may find but Anne's are listed as INSERT, SELECT,
privileges getting unnecessarily complex quickly. DELETE. This makes it hard to compare the
Redundant privileges, or users with similar but grants.
not identical privileges, can be a headache to
manage, and if you need to change or clean them Imagine that you overcome this somehow, and
up at the command line, it can be very tedious to now you see only five differences between the
do. I also believe privileges are code and should two. Your remaining task is to type REVOKE
be automatically saved in a version control statements for Bob's account. This also gets old
system, for all the same reasons I version control after you do it a few times. Imagine how hard this
other kinds of code. process becomes when you have many users on
many servers!
Both tasks are harder than they might seem at
first glance, but they can be made easier with a This same difficulty makes it hard to place the
little scripting. grants into version control. In my experience, the
grants are frequently printed out in a different
What's hard about managing privileges? order every time you check, even when they
haven't changed. This leads to spurious
Though it seems straightforward at first, it can changesets in your version control system, and
actually be quite tedious to manage user again makes it hard to compare and see what is
privileges, especially from the command line. For truly changing.
example, let's say you have users Bob and Anne,
with nearly the same privileges, and your task MySQL Show Grants to the rescue
is to remove Bob's extra privileges so he has the
same rights as Anne. To make it more I wrote a tool, unimaginatively called MySQL
interesting, imagine the users are not on the Show Grants, to help me address these very
same server, and each has about a hundred problems. Superficially similar to "mysql -e 'show
privileges. How do you do this? grants'", it has some important differences that
make it easy to script solutions to the above
The most straightforward way is to connect to problems:
each server and type SHOW GRANTS FOR
<username>, then visually scan the lists and 1. Grants are sorted within each line, so they
compare. This works okay for a few privileges, always appear in the same order.
but not hundreds.
2. The lines are sorted.
Your next thought is to pipe the grants into files
and use command-line utilities, such as diff, to These properties are enough to solve the
compare them. This is a great idea, but here's problem of spurious changesets in version
where you start to notice the little things that control. All you need to do is run a nightly cron
make this job harder. The grants are not ordered job to dump the grants (by default it dumps grants
the same. You can pipe them through sort, but for all users), then check it into your source
you still don't get them in the same order, control system.
because not only are the individual GRANT
statements not ordered the same in the output, As for cleaning up user privileges, I also added
but the actual grants are sorted differently within features to help you diff users conveniently,
the lines! For example, Bob's grants on a given adding or revoking with ease:
1. You can split each GRANT command onto its
own line. GRANT SELECT, INSERT becomes
GRANT SELECT; GRANT INSERT. This makes
it much easier to see which exact priveleges are
different.

2. You can convert GRANT statements to


REVOKE statements.

These features make it easy to revoke Bob's


excess privileges and make them the same as
Anne's. For example,

mysql-show-grants --separate --revoke --only bob --


host=server1 \
| grep REVOKE > bob-privileges
mysql-show-grants --separate --revoke --only anne --
host=server2 \
| grep REVOKE | sed -e 's/anne/bob/g' > anne-privileges

These commands separate the GRANT


statements and convert them into REVOKE
statements, then put the results into files. You
can easily run diff on these two files now and see
what has changed; the files contain valid
REVOKE commands, so you can even execute
the difference by piping it into the mysql
command-line client.

MySQL Show Grants is licensed under the GPL,


hosted on SourceForge, and can be downloaded
from http://sourceforge.net/projects/mysqltoolkit
The system requirements are very moderate: Perl
and DBD, which might already be on your
system.

Though I have solved my own needs with this


tool, I also want it to be useful to others. If you
need additional features, feel free to request them
via the SourceForge bug tracker, forums, or
mailing list.

About the author

Baron Schwartz works for the Rimm-Kaufman


Group, a search marketing agency in
Charlottesville, Virginia. He writes frequently
about MySQL on his blog (http://www.xaprb.com).
He is the author of the innotop MySQL and
InnoDB monitor, and MySQL Toolkit, a collection
of command-line tools.
Operating System Hardening
by Keith Murphy

Some might question why there is an article about the operating system in a magazine about MySQL.
When considering the overall security of a database it is important to take into account the underlying
operating system of the database. You can have a very secure MySQL server compromised through
the underlying operating system.

MySQL runs on a variety of platforms. There is not enough space to cover every platform. First I will
discuss several principles that are going to be applicable to any operating system. Then I will cover in
detail the setup of the operating system. Linux is one of the most popular platforms for MySQL and
CentOS is one of the most common Linux distribution.

There are two security principles that apply to server hardening - the principle of separation and the
principle of defense in depth. The principle of separation is the idea that a server should perform a
minimal set of task. In another words, the server should NOT operating email, web and database
services. Why is this? Because less software loaded and running on the server provides fewer
avenues for attack. The principle of defense in depth means that only one method of protection
should not be used to protect your server/network. For example, many people mistakenly think that a
firewall will protect your network completely. In addition to a firewall you should have good password,
intrusion detection systems, etc.

Do not underestimate the importance of these two principles. When performing the following sample
installation of CentOS I will demonstrate these two principles. The operating system will not have any
unneeded services running and there will be multiple forms of protection. Consider this when planning
your own deployments.

Hardening a CentOS 4.X base system

We will be hardening a basic install of CentOS 4.X. In addition to the basic operating system install,
we are installing several additional programs to provide layers of security:

• tcp wrappers
• libsafe
• chkroot
• tripwire

We will make sure all unnecessary services are deactivated, and all file systems permissions are
properly set. Physical security of the system should be taken into account as well.

In preparation for the install we will be using the following partitioning scheme:
Partition Size Notes
/boot 100MB Contains all startup files and the kernel
/ 6 gb top level partion (“root partition”)
swap 2x memory contains paging file for memory management
/usr 10 gb contains programs, libraries and documentation
/var 10 gb system log files
/tmp 1 gb temporary files
/home remaining space user files
Why are we partitioning like this?

• Protection against SUID programs


• Easy backup & upgrade management
• Ability for better control of mounted file system
• Limit each file system’s ability to grow

You will probably want to make some modifiations to this layout. If you are setting up a MySQL server
you might want to set up specific partitions for your database logs and tablespace.

Insert the CentOS server cd into the drive.

The boot screen will display three options:

• Install or upgrade CentOS in graphical mode: <Enter>


• Install or upgrade CentOS in text mode: linux text <Enter>
• Use function keys for more information

Hit Enter to begin the installation in graphical mode.

Before the installation begins, you will be prompted to test the CD media. If you have not already done
this, select OK. This should be done at least once for all of the installation CDs. When the media test
has completed, re-insert CD #1 and select Continue. If you have already done this, select Skip.

The following will walk through our Operation System installation. An explanation of each step is
provided on the left side of the screen.

Screen Action

Welcome Select Next

Language Select English (default)


Select Next

Keyboard Select US English (default)


Select Next

Mouse Configuration Select Generic - 3 Button Mouse (PS/2)


Select Next
Disc Partitioning Setup Select Manually partition with Disk Druid
Select Next
Layout the partitioning as described earlier
Boot Loader Configuration Leave default selection of GRUB boot loader. Select Use
a boot loader password, and enter a password.
Select Next
Network Configuration Configure with your network settings
Screen Action

Firewall Select Enable firewall


• allow only ssh through the firewall
Select Next
Additional Language Support Default language for the system: English (USA)
Additional languages to install: English (USA) only

Time Zone Selection Select System clock uses UTC


Select your timezone (i.e., America/Denver)
On the UTC Offset tab
• Select offset (i.e, UTC-07 US Mountain)
• Select Use daylight saving time
• Select Next

Set Root Password Enter a root password

Package Defaults Select Customize set of packages to be installed.

Package Group Selection Select only the following package:


• Minimal

Preparing to install Select Next will begin the installation, this is the
last opportunity to cancel the install.

Installing Packages Insert disks as prompted


Installation Complete When the CD is ejected, remove the media and
select Exit to reboot the system. A complete log of
the installation can be found in the /root/install.log

Post Installation

Once the installation is complete save the anaconda-ks.cfg file somewhere so it won’t be overwritten or
deleted. This file is located in root’s home directory (/root) and is a complete kickstart configuration file
that can be used to build a duplicate system. Also generate a listing of all RPMs installed on the systems
using the following command. rpm -qa | sort > /root/rpm-list

Patching the System

This system will be using yum on a internal server to get updates. All updates on this server have been
reviewed and tested before being made available to machines on the network. With this in mind, we will
be using yum to update the machine, which requires it to be on our network. We have provided a secure
subnet on which to do this, however, we’ve also taken the extra precaution during installation by
installing a firewall that is currently only allowing ssh traffic, and the required DNS replies and DHCP
requests for network connectivity. This update is initially performed by during installation from the update
server.
Securing services

Now we will remove some services. There are several ways to stop services, I will talk about two of
them.

• running the chkconfig command


• running the ntsysv command

First, the chkconfig command. To see what services are currently running on your system:

chkconfig --list

This will show you a complete list of all processes and what run level they are starting at. To make it a
little easier to read you might want to run chkconfig -list | grep :on and this will only show you the
processes that are starting up.

Now, with my default system I have this output from the chkconfig --list command:

network 0:off 1:off 2:on 3:on 4:on 5:on 6:off


gpm 0:off 1:off 2:on 3:on 4:on 5:on 6:off
haldaemon 0:off 1:off 2:off 3:on 4:on 5:on 6:off
xfs 0:off 1:off 2:on 3:on 4:on 5:on 6:off
acpid 0:off 1:off 2:off 3:on 4:on 5:on 6:off
rpcsvcgssd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
xinetd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
readahead_early0:off 1:off 2:off 3:off 4:off 5:on 6:off
netfs 0:off 1:off 2:off 3:on 4:on 5:on 6:off
messagebus 0:off 1:off 2:off 3:on 4:on 5:on 6:off
mdmonitor 0:off 1:off 2:on 3:on 4:on 5:on 6:off
rhnsd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
crond 0:off 1:off 2:on 3:on 4:on 5:on 6:off
atd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
apmd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
smartd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
autofs 0:off 1:off 2:off 3:on 4:on 5:on 6:off
isdn 0:off 1:off 2:on 3:on 4:on 5:on 6:off
readahead 0:off 1:off 2:off 3:off 4:off 5:on 6:off
rpcidmapd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
anacron 0:off 1:off 2:on 3:on 4:on 5:on 6:off
irqbalance 0:off 1:off 2:off 3:on 4:on 5:on 6:off
pcmcia 0:off 1:off 2:on 3:on 4:on 5:on 6:off
nfslock 0:off 1:off 2:off 3:on 4:on 5:on 6:off
sendmail 0:off 1:off 2:on 3:on 4:on 5:on 6:off
sshd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
rpcgssd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
syslog 0:off 1:off 2:on 3:on 4:on 5:on 6:off
cups 0:off 1:off 2:on 3:on 4:on 5:on 6:off
kudzu 0:off 1:off 2:off 3:on 4:on 5:on 6:off
portmap 0:off 1:off 2:off 3:on 4:on 5:on 6:off
iptables 0:off 1:off 2:on 3:on 4:on 5:on 6:off
cpuspeed 0:off 1:on 2:on 3:on 4:on 5:on 6:off
rawdevices 0:off 1:off 2:off 3:on 4:on 5:on 6:off
Now we will stop some (unneeded) services:

[root@rivendale /]# for svc in gpm kudzu netfs atd apmd isdn \
> pcmcia autofs portmap nfslock mdmonitor mdmpd cups ; do
> /sbin/chkconfig -level 2345 $svc off
> /sbin/service $svc stop
> done

This is fairly straightforward, each of the services listed gets removed from the startup list, and also
stopped when you run the script.

Here is the output of the chkconfig -list command:

network 0:off 1:off 2:on 3:on 4:on 5:on 6:off


haldaemon 0:off 1:off 2:off 3:on 4:on 5:on 6:off
acpid 0:off 1:off 2:off 3:on 4:on 5:on 6:off
rpcsvcgssd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
xinetd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
readahead_early 0:off 1:off 2:off 3:off 4:off 5:on 6:off
messagebus 0:off 1:off 2:off 3:on 4:on 5:on 6:off
rhnsd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
crond 0:off 1:off 2:on 3:on 4:on 5:on 6:off
smartd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
readahead 0:off 1:off 2:off 3:off 4:off 5:on 6:off
rpcidmapd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
anacron 0:off 1:off 2:on 3:on 4:on 5:on 6:off
irqbalance 0:off 1:off 2:off 3:on 4:on 5:on 6:off
sendmail 0:off 1:off 2:on 3:on 4:on 5:on 6:off
sshd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
rpcgssd 0:off 1:off 2:off 3:on 4:on 5:on 6:off
syslog 0:off 1:off 2:on 3:on 4:on 5:on 6:off
iptables 0:off 1:off 2:on 3:on 4:on 5:on 6:off
cpuspeed 0:off 1:on 2:on 3:on 4:on 5:on 6:off
rawdevices 0:off 1:off 2:off 3:on 4:on 5:on 6:off

In this particular setup, I went from 34 services running to 21 services running. This is much better.

Network security

We will be modifying the /etc/sysctl.conf file. The final version will look like this:

# Kernel sysctl configuration file for Red Hat Linux


#
# For binary values, 0 is disabled, 1 is enabled. See sysctl(8) and
# sysctl.conf(5) for more details.
# Controls IP packet forwarding
# Disable IP Forwarding
net.ipv4.ip_forward = 0
# Disables IP Source Routing
net.ipv4.conf.all.accept_source_route = 0
# Enables SYN flood protection
net.ipv4.tcp_max_syn_backlog = 4096
# Enables TCP SYNC flood protection
net.ipv4.tcp_syncookies = 1
# Disables the ability to send ICMP Redirect
net.ipv4.conf.all.send_redirects = 0
# Disables ICMP Redirect acceptance
net.ipv4.conf.all.accept_redirect = 0
# Another parameter that disables ICMP Redirect acceptance
net.ipv4.conf.default.accept_redirects = 0
# Controls source route verification
# Enable IP Spoofing protection
net.ipv4.conf.default.rp_filter = 1
# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0
# Controls whether core dumps will append the PID to the core filename.
# Useful for debugging multi-threaded applications.
kernel.core_uses_pid = 1

After changing the file, set the file permisions:

# chown root:root /etc/sysctl.conf


# chmod 0600 /etc/sysctl.conf

Now, you need to restart the network service:

# /etc/rc.d/init.d/network restart

It is always a good idea to have a log-in banner when someone accesses the system. Legally, it is a
good idea, plus it also helps to disguise what particular services your system is running.

For ssh, add the following line to the /etc/hosts.allow file

sshd : ALL : banners /etc/issue

then do the following:

# mkdir /etc/banners
# touch /etc/banners/sshd

create the content of the sshd file:

220-Hello, %c
220-All activity on rivendale.paragon-cs.com is logged.
220-Thank you

The %c variable shows client information such as the username/hostname.


Secure Console and Root Account

You really don’t need to be logging in across the network with the root account. To fix this, edit the
/etc/securetty file to disable network root login. Here is what it should look like:

[root@rivendale /]# cat /etc/securetty


tty1
tty2
tty3
tty4
tty5
tty6

The root account can log in from the local console and the ssh daemon. To disable ssh root login you
need to add ’PermitRootLogin no’ to the /etc/ssh/sshd_config file, and then restart sshd.

Restricting root login from the local console

Linux provides a "Single User Mode" to perform system maintenance. By default, Linux does not
require the root password to log into single user mode. To require the password, add
"~~:S:wait:/sbin/sulogin" to /etc/inittab. Then run /sbin/init q to activate the change.

Restricting usage of ’ctrl-alt-del’

If you type ’ctrl-alt-del’ simultaneously, a Linux system will typically reboot. This is easy to fix, and
should be done. There are two ways of doing this:

Edit the /etc/inittab again and comment (#) out the following line:

ca::ctrlaltdel:/sbin/shutdown -t3 -r now

or

add a -a option to the line. The -a flag tells shutdown to look for the /etc/shutdown.allow file. You can
restrict any users who are allowed to shutdown the system using ’ctrl-alt-del’.

In /etc/inittab:
ca::ctrlaltdel:/sbin/shutdown -a -t3 -r now
In /etc/shutdown.allow
bmurphy
root

Disabling console program access

If you want to disallow any user at the console to run poweroff, halt, and reboot run the following
commands:

[root@rivendale /]# rm -f /etc/security/console.apps/poweroff


[root@rivendale /]# rm -f /etc/security/console.apps/halt
[root@rivendale /]# rm -f /etc/security/console.apps/reboot
Removing unnecessary system accounts

Some user accounts and groups are not needed by the server. It’s best to keep the /etc/password file
as small as possible. This will make it easier to see unauthorized additions easier.

Some common users you don’t need:

gopher, adm, pcap, rpc, rpcuser, nfsnobody, games, news, uucp apache, xfs, named, ntp

And the groups:

news, uucp, adm, games, dip

[root@rivendale /]# for user in gopher pcap rpc rpcuser nfsnobody \


> games news uucp apache xfs named ntp adm ; \
> do /usr/sbin/userdel $user ; done
[root@rivendale /]# for group in news uucp adm games dip; \
> do /usr/sbin/groupdel $group ; done
[root@rivendale /]# pwck
[root@rivendale /]# grpck

Mount /usr file system as read-only

For protection against Trojan binaries being installed in the /usr partition, /usr can be mounted as read-
only. In the /etc/fstab file:

/usr ext3 ro 1 2

If you need to allow write access (to install a program):

[root@rivendale /]# mount -o remount,rw /usr

Then after installation, change it back to read only:

[root@rivendale /]# mount -o remount,ro /usr

Additional Software Installation

As we mentioned earlier there are several packages we want to install to perform additional hardening
of the system: tcp wrappers, libsafe, chkroot, and tripwire.

TCP Wrappers

We will configure TCP Wrapper to control the authentication of the ssh daemon. Two files will be used
(/etc/host.allow and /etc/hosts.deny). The hosts.deny file will by default deny everyone, and the
hosts.allow file will only allow those to whom we wish to grant access.

Make sure you have TCP Wrapper

[root@rivendale /]# rpm -qa | grep wrapper


tcp_wrappers-7.6-38
[root@rivendale /]#
Create the hosts.allow and hosts.deny files (if you don’t already have them)

[root@rivendale /]# vi /etc/hosts.deny


[root@rivendale /]# vi /etc/hosts.allow
[root@rivendale /]# cat /etc/hosts.allow
ssh: 192.168.1.101: allow
[root@rivendale /]# cat /etc/hosts.deny
ALL:ALL:

Libsafe

Libsafe is a interesting program that helps protect your system from what is called from stack attacks -
typically called buffer overflows. A buffer overflow occurs when a memory stack is filled to capacity
when a piece of code is executed in the stack. This causes the potential side efect of a root shell.
Buffer overflows occur from either poor programming practices or weaknesses in the programming
language itself. Libsafe is a way for the operating system to deny buffer overflow attacks. Libsafe
monitors system calls and if an attack is detected then the entire process group will be sent a SIGKILL
signal. The attempt is also logged in /var/log/secure. Libsafe is available from
http://www.research.avayalabs.com/project/libsafe.

Chkrootkit

The chkrootkit script is a tool that checks for various trojans on your system. The homepage is
http://www.chkrootkit.org. Chkrootkit is available as a tarball..but since it is just a shell script it is not
difficult to install. Just download it and unzip it somewhere (/usr/local maybe?). Have it executed on a
regular basis using the crontab utility.

Tripwire

Tripwire is a very good program that builds a database of the size of your system files and then you
periodically run tripwire to compare your current system files against the database. It is important to
create a baseline of your system files as soon as you do the installation and preferably before you are
plugged into the public Internet. The "official" homepage of tripwire is http://www.tripwiresecurity.com.
This page is about the enterprise version of tripwire however. Google tripwire if you need additional
information.

Conclusion

What have we learned from this exercise? We have reduced the number of services running on a
server with a minimal install of packages. In addition we have added several very useful programs to
help keep tabs on your systems security. You have applied both the principles of seperation and
defense in depth. Do not fall into the trap of thinking that just because you hardened your server to
begin with that you are safe. An important aspect of security is staying vigilant and monitoring your
system(s).

About the author

Keith Murphy is the editor of MySQL Magazine. He is also the chief Dolphin at Broadwick Corporation.
log-bin
So what is this all about? I decided to start MySQL Magazine because I see a need for the magazine.
The world’s number one open-source database has over 10 million installations. While there are
stacks of books available, and numerous resources available online there are no magazines available
about MySQL. Sure there is the occassional article about MySQL in the various Linux magazines or
Sys Admin Magazine but I want something that is only about my passion - MySQL.

I have been involved in Unix in general and Linux specifically for quite a while. The first time I booted
up a Sun workstation was in 1992 or 1993. Back then Sun used the SunOS which was BSD-Unix
based. I moved into using Linux in 1997 with some version of Slackware. When I started an ISP in
1998 I began using RedHat and MySQL.

Previously I had worked as a DBA on an Oracle system running on HP-UX. Contrast this with running
MySQL on commodity PC hardware and as a DBA you can see the possibilities of MySQL even at the
fairly immature growth stage that was version three. Sure, many of the features of Oracle were not in
place. Even so it was solid, very quick and absolutely free.

Over the years I have watched MySQL grow and mature. More and more big companies have started
utilizing it. Now with the birth of “Web 2.0”, there is an absolute need for full featured databases that
can scale with the growth of the website and deliver billions of queries a month. Livejournal, YouTube,
Google, Kaneva and Flikr are a few of the large sites powered on the backend by MySQL. Why is
this? The maturity of MySQL has allowed these companies to scale very quickly. The cost-
effectiveness is obviously a factor but it really boils down to the fact that MySQL enables these
companies to provide a better experience to the end user. And with the maturing of the clustering
capabilities in version 5.x there is a whole new level we can take MySQL. While there are commericial
RDMS that provide for clustering the costs are astronomical. I would suggest that if you are a MySQL
DBA, or you are aspiring to be one, you invest in the hardware needed to run Xen well - any 64-bit
processor with hardware virtualization support and a few gigs of RAM. Then load it up with CentOS 5
and create some virtual servers running version 5.1.x of MySQL. Work with clustering. Experiment,
play, above all have fun!

I am looking forward to this project. It is my way of giving back to the FOSS community. Also, I want
to hear back from the community about your thoughts too!

Thanks,

Keith Murphy (bmurphy@paragon-cs.com)