In a previous post I wrote about the documentation structure I have in mind for a PostgreSQL security best practice. Considering what XCCDF can give us, the idea is to have the following structure:
Hardening PostgreSQL +- Basic setup +- Instance level configuration | +- Pre-startup configuration | `- PostgreSQL internal configuration +- Database recommendations `- User definitions
For the profiles, I had:
infrastructure instance user +- administrator +- end user `- functional account
Let’s bring this into an XCCDF document.
The XCCDF (Extensible Configuration Checklist Description Format) format is an XML structure in which we can document whatever we want – but it is primarily used for configuration checklists and best practices. The documenting aspect of a security best practice in XCCDF is done through XHTML basic tags (do not use fancy things – limit yourself to p, pre, em, strong, … tags), so some knowledge on XHTML (next to XML in general) is quite important while developing XCCDF guides. At least, if you don’t use special editors for that.
We start with the basics:
<?xml version="1.0" encoding="UTF-8"?> <Benchmark xmlns="http://checklists.nist.gov/xccdf/1.2" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="xccdf_org.gentoo.dev.swift_benchmark_postgresql-20131212-1" xsi:schemaLocation="http://checklists.nist.gov/xccdf/1.2 xccdf-1.2.xsd" resolved="0"> <title>PostgreSQL Best Practices</title> <description> This document describes how to securely set up PostgreSQL on a Gentoo Linux platform, making it more resilient against attacks and vulnerabilities. </description> </Benchmark>
Two things I want to focus on here: the xmlns:h and id attributes.
- The xmlns:h attribute is an XML requirement, telling whatever XML parser is used later that tags that use the h: namespace is for XHTML tags. So later in the document, we’ll use <h:p>...</h:p> for XHTML paragraphs.
The id attribute is XCCDF specific, and since XCCDF 1.2 also requires to be in this particular syntax:
The <namespace> is recommended to be an inverted domain name structure. I also added my nickname so there are no collisions with namespaces provided by other developers in Gentoo. So SwifT’s dev.gentoo.org becomes org.gentoo.dev.swift.
This id structure will be used in other tags as well. Instead of *_benchmark it would be *_rule (for Rule ids), *_group (for Group ids), etc. You get the idea.
Now we add in some metadata in the document (with Benchmark as parent):
<status date="2013-12-12">draft</status> <platform idref="cpe:/a:postgresql:postgresql" /> <version>20131212.1</version> <model system="urn:xccdf:scoring:default" /> <model system="urn:xccdf:scoring:flat" /> <model system="urn:xccdf:scoring:flat-unweighted" />
So what is all this?
- The <status> tag helps in tracking the state of the document.
The <platform> tag is to tell the XCCDF interpreter when the document is applicable. It references a CPE (Common Platform Enumeration) entity, in this case for PostgreSQL. Later, we will see that an automated test is assigned to the detection of this CPE. If the test succeeds, then PostgreSQL is installed and the XCCDF interpreter can continue testing and evaluating the system for PostgreSQL best practices. If not, then PostgreSQL is not installed and the XCCDF does not apply to the system.
There is a huge advantage to this: you can check all your systems for compliance with the PostgreSQL best practices (this XCCDF document) and on the systems that PostgreSQL is not installed, it will simply state that the document is not applicable (without actually trying to validate all the rules in the document). So there is no direct need to only check systems you know have PostgreSQL on (and thus potentially ignore systems that have PostgreSQL but that you don’t know of – usually those systems are much less secure as well ;-).
- The <version> tag versions the document.
The <model> tags tell the XCCDF interpreter which scoring system should be used.
Scoring will give points to rules, and the XCCDF interpreter will sum the scores of all rules to give a final score to the “compliance” state of the system. But scoring can be done on several levels. The default one uses the hierarchy of the document (nested Groups and Rules) to give a final number whereas the flat one does not care about the structure. Finally, the flat-unweighted one does not take into account the scores given by the author – all rules get the value of 1.
Now we define the Profiles to use. I will give the example for two: user and administrator, you can fill in the other ones ;-)
<Profile id="xccdf_org.gentoo.dev.swift_profile_user"> <title>User profile</title> <description> This profile defines the basic security measures to be applied against a database user (role in PostgreSQL parlance). </description> </Profile> <Profile id="xccdf_org.gentoo.dev.swift_profile_administrator" extends="xccdf_org.gentoo.dev.swift_profile_user"> <title>Administrator profile</title> <description> This profile extends the user profile with those rules specific to administrators, such as stronger password requirements. </description> </Profile>
Finally, the Groups (still with Benchmark as their parent, but below the Profiles) which define the documentation structure of the guide:
<Group id="xccdf_org.gentoo.dev.swift_group_introduction"> <title>Introduction</title> <description> ... </description> </Group> <Group id="xccdf_org.gentoo.dev.swift_group_basic"> <title>Basic setup</title> ... </Group> <Group id="xccdf_org.gentoo.dev.swift_group_instance"> <title>Instance level configuration ... <Group id="xccdf_org.gentoo.dev.swift_group_instance-prestart"> <title>Pre-startup configuration</title> ... </Group> <Group id="xccdf_org.gentoo.dev.swift_group_instance-internal"> <title>PostgreSQL internal configuration</title> ... </Group> </Group>
With all this defined, our basic skeleton for the PostgreSQL best practice document is ready. To create proper content, we can use the XHTML code inside the <description> tags, like so:
<description> Considering these pros and cons, it is recommended to have at least the following file system locations to be on a different file system: <h:ul> <h:li> <h:code>/tmp</h:code> as this is a world-writable location and requires specific mount options. When possible, this location can be made a <h:em>tmpfs</h:em> file system. This is to protect the root file system from being flooded. </h:li> <h:li> <h:code>/var</h:code> as this contains variable data (and thus is prone to grow extensively depending on the installed services). This is to protect the root file system from being flooded. </h:li> </h:ul> </description>
As said in the previous post though, just documenting various aspects is not enough. It is recommended to add references. In XCCDF, this is done through the <reference> tag, which is within a Group and usually below the <description> information:
<Group ...> <description>...</description> <reference href="http://path/to/resource">Resource Guide,...</reference> </Group>
With this alone, it is already possible to write up an XCCDF guide describing how to securely setup (in our case) PostgreSQL while keeping track of the resources that helped define the secure setup. Tools like openscap can generate HTML or even Docbook (which in turn can be converted to manual pages, PDF, Word, RTF, …) from this information:
# oscap xccdf generate guide --format docbook --output guide.docbook postgresql-xccdf.xml
In the next post, I’ll talk about the other documenting entities within XCCDF (besides <description> and their meaning) and start with enhancing the document with automated checks.