SEPostgreSQL Documentation

From PostgreSQL wiki
Jump to navigationJump to search

This page is a base to revise documentation of SE-PostgreSQL module.

So, don't use any markups which are not supported in *.sgml, such as
inline images.


sepgsql is a module which performs as an external security provider; to support label based mandatory access control (MAC) base on SELinux policy.

This extension won't build at all unless the installation was configured with --with-selinux.


PostgreSQL provides various kind of hooks. Some of these hooks can be utilized to make access control decision on the supplied users' accesses on database objects. We call plug-in modules making access control decision based on its own security model as an external security provider.

This module acquires control on these strategic points, then it asks SELinux to check whether the supplied access shall be allowed, or not. Then, it returns its access control decision. If violated, this module prevents this access with rising an error for example.

A series of making decision is done independently from the default database privilege mechanism. Users must be allowed with both of access control models, whenever they try to access something.

We can see SELinux as a function which takes two arguments then returns a bool value; allowed or denied. The first argument in this analogy is label of subject which tries to reference a certain obejct. The other one is label of the object being referenced in this operation.

Label is a formatted string, like system_u:object_r:sepgsql_table_t:s0. It is not a property depending on characteristics of a certain kind of object, so we can apply common credentials on either database objects or others.

PostgreSQL 9.1 or later supports SECURITY LABEL statement that allows to assign a security label on specified database objects, if user wants to change label from the creation default. Also SELinux provides an interface to obtain security label of the peer process that connected to.

These facilities enable to integrate SELinux model within access controls to database objects. Because it makes access control decision according to a common centralized security policy (a set of rules), its decision will be always consistent independent from the way to store information assets.


The sepgsql module requires the following packages to install. Please check it at first.

Linux kernel
v2.6.28 or later built with SELinux enabled
v2.0.93 or later
This library provides a set of APIs to communicate with SELinux in kernel.
Newer than timestamp of 20110117.
The default security policy provides a set of access control rules.

SE-PostgreSQL needs SELinux being available on the platform. You can check the current setting using sestatus.

$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /selinux
Current mode:                   enforcing
Mode from config file:          enforcing
Policy version:                 24
Policy from config file:        targeted

If disabled or not-installed, you need to set up SELinux anyway prior to SE-PostgreSQL installation.

On the compile time, add --with-selinux option to the configure script to check existence of the libselinux, and to set a flag whether we build this contrib module, or not.

$ ./configure --enable-debug --enable-cassert --with-selinux
$ make
$ make install

Next to the initdb, add '$libdir/sepgsql' to shared_preload_libraries in postgresql.conf.

It enables to load sepgsql plugin on starting up of postmaster process.

Then, run the sepgsql.sql script for each databases. It installs functions corresponding to security label management, and tries to assign initial labels on the target objects.

The following instruction assumes your installation is under the /usr/local/pgsql directory, and the database cluster is in /usr/local/pgsql/data. Substitute your paths appropriately.

$ initdb -D /usr/local/pgsql/data
$ vi /usr/local/pgsql/data/postgresql.conf
$ for DBNAME in template0 template1 postgres;
    postgres --single -F -O -c exit_on_error=true    \
             -D /usr/local/pgsql/data $DBNAME        \
        < /usr/local/pgsql/share/contrib/sepgsql.sql > /dev/null

If all the installation process was done with no errors, start postmaster process. SE-PostgreSQL shall prevent violated accesses according to the security policy of SELinux.

Regression Tests

The regression test of this module requires a few more configurations on the platform system, in addition to the above installation process. See the following steps.

First, install the policy package for regression test. The sepgsql-regtest.pp is a special purpose policy package that provides a set of rules to be allowed during the regression test cases. It shall be installed at /usr/local/pgsql/share/contrib directory in the default setup.

You need to install this policy package using semodule command which enables to link supplied policy packages and load them into the kernel space. If you could install it correctly, semodule -l prints sepgsql-regtest as a part of policy packages currently available.

$ su
# semodule -u /usr/local/pgsql/share/contrib/sepgsql-regtest.pp
# semodule -l
sepgsql-regtest 1.03

Second, turn on the sepgsql_regression_test_mode. We don't enable all the rules in the sepgsql-regtest.pp in the default, for your system's safety. The sepgsql_regression_test_mode parameter of SELinux is associated with rules to launch regression test. It can be turned on using setsebool command.

$ su
# setsebool sepgsql_regression_test_mode on
# getsebool sepgsql_regression_test_mode
sepgsql_regression_test_mode --> on

Last, kick the regression test from the unconfined_t domain.

This test policy is designed to kick each test cases from the unconfined_t domain that is a default choice in most of the known SELinux installation base. So, you don't need to set up anything special, as long as you didn't change default configuration of SELinux before.

The id command tells us the current working domain. Confirm your shell is now performing with unconfined_t domain as follows.

$ id -Z

If not an expected one, you should revert this configuration. The #External_Resources section will give you some useful hints.

Then, you can kick regression test. If we have no problem here, all the tests will be passed.

$ make -C contrib/sepgsql/ installcheck
../../src/test/regress/pg_regress --inputdir=. --psqldir=/usr/local/pgsql/bin \
    --dbname=contrib_regression --launcher ../../contrib/sepgsql/launcher     \
    label dml
(using postmaster on Unix socket, default port)
============== dropping database "contrib_regression" ==============
============== creating database "contrib_regression" ==============
============== running regression test queries        ==============
test label                    ... ok
test dml                      ... ok

 All 2 tests passed.

If pg_regress failed to launch psql command, here is an additional hint. When it tries to launch psql command with restrictive privileges, the psql must be labeled as bin_t. If not, try the following magic words.

$ restorecon -R /usr/local/pgsql

GUC Parameters

sepgsql.permissive (boolean)

This parameter enables to perform SE-PostgreSQL in permissive mode independent from the system setting. The default is off (according to the system setting). This parameter can only be set in the postgresql.conf file or on the server command line.

We have two performing mode except for disabled; The one is enforcing mode that checks the security policy on references and actually prevents violated accesses. The other is permissive mode that only checks the security policy, but does not prevents anything except for log generation. This log shall be utilized for debugging of the security policy itself.

When this parameter is on, SE-PostgreSQL performs in permissive mode, even if the platform system is working on enforcing mode. We recommend users to keep the default setting, except for the case when we develop security policy by ourself.

sepgsql.debug_audit (boolean)

This parameter enables to print audit messages independent from the policy setting. The default is off (according to the security policy setting).

The security policy of SELinux also has rules to control what accesses shall be logged, or not. In the default, any access violations are logged, but any allowed accesses are not logged.

When this parameter is on, all the possible logs shall be printed independently from the policy settings. We recommend to keep the variable turned off in normal cases to avoid noisy messages.


controlled object classes

This version of SE-PostgreSQL supports to assign a security label on these database object classes: schema, table, column, sequence, view and procedure. Other database object classes are not supported to assign security label on, right now.

A security label shall be automatically assigned to the supported database objects on their creation time. This label is called as a default security label. It is decided according to the security policy, or a pair of security label of the client and upper object for more correctly. A new database object basically inherits security label of the upper object. A new column inherits security label of its parent table for instance. If and when the security policy has special rules called as type-transition on a pair of the client and upper object, we can assign an individual label as a default. The upper object depends on sort of object classes as follows.

Its upper object is the current database.
Its upper object is the schema object which owns the new table.
Its upper object is the table object which owns the new column.
Its upper object is the schema object which owns the new sequence.
Its upper object is the schema object which owns the new view.
Its upper object is the schema object which owns the new procedure.

All the access control decision shall be made based on security label of object being reference in SELinux model, so we have no way to control accesses on these objects that don't have

DML Permissions

This section introduces what privileges shall be checked on DML statements; SELECT, INSERT, UPDATE and DELETE.

For tables, it checks db_table:select, db_table:insert, db_table:update or db_table:delete depending on the sort of statement; for all the appeared target tables. In addition, it also checks db_table:select for all the tables that contain columns to be referenced in WHERE, RETURNING or others.

UPDATE t1 SET t1.b = 'bbb' WHERE t1.a = 10;

In this case, we must have db_table:select, not only db_table:update, because it references t1.a within WHERE clause.

Note that the client has to be allowed to reference each tables, even if these tables are originally used in views and expanded, because it applies consistent rules independent from the way to reference contents of the tables.

For sequences, db_sequence:get_value shall be checked on SELECT. Right now, other DMLs are not supported to access sequence objects, so we also don't check here anymore. Please note that we don't check anything on execution of function to handle sequence objects such as lastval(), because of lack of object access hooks.

For views, db_view:expand shall be checked on SELECT. Note that it also check individual permissions on tables being expanded from the views. In this case, both of permissions have to be allowed.

For columns, it checks db_column:select on not only the columns being read by SELECT, but being referenced by WHERE clause or data source of UPDATE also. It also checks db_column:update and db_column:insert on the column being modified by UPDATE or INSERT.

UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;

In this case, it checks db_column:update on the t1.x being updated, db_column:{select update} on the t1.y being updated and referenced, and db_column:select on the t1.z being only referenced in the WHERE clause.

Unlike the default database privileges, column-level permissions never overwrite table-level permissions, so we have to be allowed in both of levels id est.

For procedures, we don't have any access control on execution of procedures. Its security label is only meaningful when we try to launch trusted procedures.

COPY TO/FROM provides similar functionalities with SELECT or INSERT from the viewpoint of access control. It checks permissions on appeared tables and columns according to the same criteria.

DDL Permissions

On SECURITY LABEL command, we check setattr and relabelfrom on the object being relabeled (Note that it still has an old security label), then relabelto on the supplied new label.

In the case when multiple label providers are installed and user tries to set a security label not-managed by SELinux, we should check only setattr here, but it is not unavailable due to the limitation of hooks.

Right now, SE-PostgreSQL does not control any other DDL operations.

Trusted Procedure

It is a similar idea to security definer functions or set-uid commands on operating systems. It enables to switch privilege of the client during execution of certain functions; being called as trusted procedures.

In SELinux model, it is a function with a special security label being configured as trusted procedure.

postgres=# CREATE TABLE customer (
               cid     int primary key,
               cname   text,
               credit  text
               IS 'system_u:object_r:sepgsql_secret_table_t:s0';
postgres=# CREATE FUNCTION show_credit(int) RETURNS text
               AS 'SELECT regexp_replace(credit, ^[0-9]+$, -xxxx, g)
                          FROM customer WHERE cid = $1'
           LANGUAGE sql;
               IS 'system_u:object_r:sepgsql_secret_table_t:s0';
postgres=# SELECT * FROM customer;
ERROR:  SELinux: security policy violation
postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
 cid | cname |     show_credit
  10 | alice | 1111-2222-3333-4444
  20 | bob   | 5555-6666-7777-8888
(2 rows)

In this case, we cannot touch column, but a trusted procedure of show_credit allows us to print credit card number with a bit modification.


This section introduces limitations of SE-PostgreSQL in this version.

Userspace access vector cache
SE-PostgreSQL tells SELinux its access control decision. It takes system call invocation being heavy, however, we can reduce number of the invocations using caching mechanism; called access vector cache in SELinux.
Because of code size, SE-PostgreSQL does not support this mechanism yet.
DDL Permissions
Now PostgreSQL does not provide a set of hooks on the DDL routines.
It means plugin modules cannot acquire control here, so SE-PostgreSQL does not check DDL Permissions right now.
Row-level access control
Now SE-PostgreSQL does not support row-level access control, because a few needed facilities are not provided yet. The one is security labels on users' tables. The other is behavior of optimizer. Also see Rules and Privileges for more details. We know similar issue on VIEW.
Covert channels
SE-PostgreSQL never tries to hide existence of a certain object, even if user is not allowed to reference. For example, we can infer an existence of invisible object using primary-key conflict, foreign-key violation, and so on, even if we cannot reference contents of these objects.

External Resources

SE-PostgreSQL Introduction
This wikipage provides a brief-overview, security design, architecture,
administration and future-plans for more details.
Fedora SELinux User Guide
This document provides wide spectrum of knowledge to administrate SELinux on your systems.
It primary focuses on Fedora, but the techniques are not limited to Fedora.
Fedora SELinux FAQ
This document provides FAQs about SELinux. It primary focuses on Fedora, but not limited to Fedora.