Configuration file format

Introduction

Once the sec-wall instance's directory has been initialized, it will contain a config.py file which is the central place for configuring any settings. The file is a regular Python source code file which means it must adhere to the Python's syntax.

As far as sec-wall is concerned, the file should simply contain at least several variables whose values must be within the expected range and type and it's absolutely none of sec-wall's business to understand where these values come from - in other words, the whole configuration may, for instance, be actually stored in LDAP , fetched through RabbitMQ and pumped into said variables using Telepathy . The chapters below all picture it like if the variables were manually set to static strings but it's worth emphasizing that there's no such a requirement, being written in Python, the config file may be turned highly dynamic.

A minimal config file

Here's how a minimal config file may look like. It isn't particularly stunning and useful because what it does basically is preventing any client application from passing through but it serves as an example of what is to be expected from the configuration format. And please, do not use it as-is for creating your own config file from scratch - that's because the INSTANCE_SECRET variable should be kept out of sight, it's the value many crypto operations depend on so it should be treated with the utmost care, the one below has become useless the moment it's been published online. Instead, you should simply initialize sec-wall in an empty directory.

# -*- coding: utf-8 -*-
 
# stdlib
import uuid
 
# Don't share it with anyone.
INSTANCE_SECRET = 'dda695cff20d43f7a8d9218b2006749f'
 
# May be shared with the outside world.
INSTANCE_UNIQUE = uuid.uuid4().hex
 
# ##############################################################################
 
def default():
return {
'ssl': True,
'ssl-cert': True,
'ssl-cert-commonName':INSTANCE_SECRET,
'host': 'http://' + INSTANCE_SECRET
}
 
urls = [
('/*', default()),
]

Variables

Configuration variables can be divided into two groups, several required ones and the prevailing majority of optional variables whose values will be set automatically unless overridden in the configuration file.

Required variables

The variables listed below are absolutely necessary for the proper functioning of a sec-wall's instance and new versions of sec-wall are free to assume they exist in the config file. They're created during the initialization process and you just need to make sure they don't get inadvertently deleted.

INSTANCE_SECRET

Default: A UUID4 identifier

A string used as a salt when cryptographically signing the requests and in the automatically generated default configuration - for denying the access to all client applications. The default value is a UUID4 which should be enough for most needs. There's really no reason for changing it but whatever you set the value to, be sure it's actually strong enough for your particular requirements. Do also note that the value should be sufficiently protected from prying eyes, it's meant to be a secret.

See also: add_invocation_id and sign_invocation_id

Samples:

INSTANCE_SECRET = '740aa279a1f74cd9903f44317e03ca41'

INSTANCE_SECRET = ':1@{8Q+W#Ilzk?:C23A{JD23A{UXQK@#:X{Z)A@#ALCSM#)'

INSTANCE_UNIQUE

Default: A UUID4 identifier regenerated on each sec-wall's restart

An identifier assigned to the instance for the purpose of uniquely identifying the requests transmitted through the proxy. Contrast with instance_name variable below, the conceptual difference between the two being that INSTANCE_UNIQUE is meant to guarantee the uniqueness of the message identifiers while instance_name is rather a human-friendly label assigned to the instance.

The combination of instance_name and INSTANCE_UNIQUE should be unique across all sec-wall instances in your environment as it would otherwise potentially lead to duplicate message identifiers being written to logs. To ensure it, the default value of INSTANCE_UNIQUE is a UUID4 identifier.

You probably don't want to change the default value but if you do, you just need to make sure the combination of INSTANCE_UNIQUE and instance_name and stays unique across all requests ever processed by your environment, hence the default value should make sense for everyone.

See also: instance_name.

urls

Default: A catch-all pattern which points to a configuration that requires the client SSL certificate's commonName field be equal to INSTANCE_SECRET

A list of 2-element lists or tuples each of which describes the URL pattern to be secured and the particular security configuration guarding the given URL pattern. Visit the chapter dedicated to configuring security for a particular pattern for more details.

Sample:

def default():
return {
'ssl': False,
'custom-http': True,
'custom-http-X-ABC':'foo',
'add-auth-info': True,
'sign-auth-info': True,
'host': 'http://localhost:17090/'
}
 
urls = [
('/*', default()),
]

Optional variables

The variables documented below are all optional and allow for a detailed customization of sec-wall instances although sane defaults will be used when they're not set explicitly. Note that what's listed below are only these options that are specific to a sec-wall instance as a whole, all the choices for configuring security of particular URLs are listed in a separate chapter.

add_default_if_not_found

Default: True

On startup, when reading the URLs to be secured, sec-wall makes a note of whether it has seen the catch-all /* URL pattern (that's a slash followed by an asterisk, meaning "any URL"). If all URLs have been read and no configuration for the /* pattern has been found, the boolean value of add_default_if_not_found dictates whether one should be added. The value to be added will be read from the default_url_config variable.

Note that the default values of add_default_if_not_found and default_url_config make for a very secure installation which prohibits any client connections unless explicitly allowed so there's probably no reason for ever changing it, but the option's here in case you really need to.

See also: default_url_config.

add_invocation_id

Default: True

A boolean value indicating whether the backend the requests are proxied over to and the client sending the requests in should receive an X-sec-wall-invocation-id header. Assuming INSTANCE_UNIQUE is configured according to the suggestions, the header's value uniquely identifies the given request and may be used, for instance, in reconcilliation processes, when backend and client applications are at odds over who sent what and when. Note that the value may be also cryptographically signed off if sign_invocation_id is True. Refer to the logging documentation for more details regarding the header's format.

See also: INSTANCE_UNIQUE and sign_invocation_id.

ca_certs

Default: An absolute value of '.' + crypto_dir + 'ca-cert.pem' where '.' denotes a directory the config.py file is in and crypto_dir is a base directory for keeping the crypto material. For instance, if config.py is in /home/sec-wall/production/ and crypto_dir will be set to its default value of ./crypto then ca_certs will be equal to /home/sec-wall/production/crypto/ca-cert.pem

A string representing the path to a list of concatenated PEM-encoded certificates of Certificate Authorities you're willing to trust. Note that the file should contain certificates of CAs signing the certificates of both clients and backend servers, they all should be stored in the same file.

See also: certfile, crypto_dir, keyfile, and server_type.

certfile

Default: An absolute value of '.' + crypto_dir + 'server-cert.pem' where '.' denotes a directory the config.py file is in and crypto_dir is a base directory for keeping the crypto material. For instance, if config.py is in /home/sec-wall/production/ and crypto_dir will be set to its default value of ./crypto then ca_certs will be equal to /home/sec-wall/production/crypto/server-cert.pem

Path to a PEM-encoded server certificate which is used if the instance is to be run in the HTTPS mode.

See also: ca_certs, crypto_dir, keyfile, and server_type.

client_cert_401_www_auth

Default: A string 'Transport mode="tls-client-certificate"'

Value to be used in the WWW-Authenticate header with the purpose of signaling to the client that its request wouldn't have been rejected straight away had the client sent a client certificate in. Inspired by the IETF discussion of the idea .

crypto_dir

Default: An absolute value of './crypto' + where '.' denotes a directory the config.py file is in.

The value of crypto_dir is used by default in ca_certs, certfile and keyfile so that the full path to the directory containing the crypto data doesn't have to be hardcoded in them directly.

See also: certfile, ca_certs, keyfile, and server_type.

default_url_config

Default:

{
'ssl': True,
'ssl-cert': True,
'ssl-cert-commonName': uuid4().hex, # Skipped importing the built-in 'uuid.uuid4' object
'host': 'http://{0}'.format(uuid4().hex),
'from-client-ignore': [],
'to-backend-add': {},
'from-backend-ignore': [],
'to-client-add': {}
}

The value to be used as a configuration for the catch-all /* URL pattern if add_default_if_not_found is True.

See also: add_default_if_not_found.

forbidden

Default:

['403', 'Forbidden', 'text/plain', str('You are not allowed to access this resource')]

A four-element list whose elements indicate what to return in case the client was forbidden access to a resource. The elements are:

  • HTTP response code
  • reason phrase
  • media type returned
  • human-friendly explanation returned to the client application

See also: internal_server_error, no_url_match and not_authorized.

from_backend_ignore

Default:

['Server']

A list of HTTP headers that should never be proxied from a backend to the client application. Use an empty list if you'd like to have the proxy return all the headers the backend application returns.

See also: server_tag.

host

Default:

'0.0.0.0'

Host to listen on.

See also: port.

instance_name

Default:

'default'

An operator-friendly name assigned to the given instance of sec-wall. Used for logging purposes. There's no real enforcement of the name being unique among the sec-wall instances running in your environment yet the name should be unique, otherwise resolving any problems or understanding the message flow may be difficult. Compare with INSTANCE_UNIQUE which servers a related purpose.

See also: INSTANCE_UNIQUE.

internal_server_error

Default:

['500', 'Internal Server Error', 'text/plain', str('Internal Server Error')]

A four-element list whose elements indicate what to return in case an internal server error has been encountered. The elements are:

  • HTTP response code
  • reason phrase
  • media type returned
  • human-friendly explanation returned to the client application

See also: forbidden, no_url_match and not_authorized.

keyfile

Default: An absolute value of '.' + crypto_dir + 'server-priv.pem' where '.' denotes a directory the config.py file is in and crypto_dir is a base directory for keeping the crypto material. For instance, if config.py is in /home/sec-wall/production/ and crypto_dir will be set to its default value of ./crypto then keyfile will be equal to /home/sec-wall/production/crypto/server-priv.pem

Path to a PEM-encoded server's private key which is used if the instance is to be run in the HTTPS mode.

See also: certfile, ca_certs, crypto_dir, and server_type.

log_file_config

Default: None

Path to a logging configuration file, must be in the format Python stdlib's logging.fileConfig function can understand . Configuration read from the file pointed to by this variable will override any other logging-related options set in the config.py file. Click here to read more about confiruging sec-wall's logging features.

See also: log_level, syslog_address and syslog_facility.

log_level

Default: 'INFO'

A string representing the log level the instance is running on. Must be one of the levels Python's logging module understands . Click here to read more about confiruging sec-wall's logging features.

See also: log_file_config, syslog_address and syslog_facility.

no_url_match

Default: ['404', 'Not Found', 'text/plain', str('Not Found')]

A four-element list whose elements indicate that although the client's auth data allowed it to establish a connection to the proxy, there was no configuration for the URL client has requested, in other words, the instance doesn't know anything about that URL. The elements are:

  • HTTP response code
  • reason phrase
  • media type returned
  • human-friendly explanation returned to the client application

See also: forbidden, internal_server_error and not_authorized.

not_authorized

Default: ['401', 'Not Authorized', 'text/plain', str('You are not authorized to access this resource')]

A four-element list whose elements indicate that the client connectin to the instance is not authorized to access the URL. The elements are:

  • HTTP response code
  • reason phrase
  • media type returned
  • human-friendly explanation returned to the client application

See also: forbidden, internal_server_error and no_url_match.

port

Default:

15100

Port to bind to. The value should be an integer, not a string.

See also: host.

quote_path_info

Default:

False

Set to True if you expect path info elements in URLs accessed by client applications to contain characters from outside of the ASCII range. Various spurious errors may pop up if the path info isn't ASCII-only and quote_path_info isn't True. For instance, if there's any chance you need to secure a URL along the lines of /ęóąśłżźćń then quote_path_info should be True.

See also: quote_query_string.

quote_query_string

Default:

False

Similar to quote_path_info, set to True if you expect query strings in URLs accessed by client applications to contain characters from outside of the ASCII range. Various spurious errors may pop up if the query string isn't ASCII-only and quote_query_string isn't True. If it's likely you'll be securing URLs similar to /foo?ęó=ąś then quote_query_string should be True.

See also: quote_path_info.

server_tag

Default:

'sec-wall/1.0.0' for version 1.0.0, or in general - 'sec-wall/' followed by a full sec-wall's version

Value to return to client applications in the 'Server' header' if the from_backend_ignore variable contains 'Server' - which it does by default. Set it, for instance, to 'Apache' or 'nginx' if for some reason you'd rather prefer client applications thought they were talking directly to the backend servers.

See also: from_backend_ignore.

server_type

Default:

'http'

Must be either 'http' or 'https' and says whether the instance should be running in plain HTTP or in HTTPS mode. Don't forget about ca_certs, certfile, and keyfile if you set it to 'https'.

See also: ca_certs, certfile, crypto_dir and keyfile.

sign_invocation_id

Default:

True

If add_invocation_id is True, whether to add an X-sec-wall-invocation-id-signed header. The header's value is computed as follows (pseudocode):

delimeter = ':'
X-sec-wall-invocation-id-signed = SHA256(INSTANCE_SECRET + delimeter + X-sec-wall-invocation-id)

See also: add_invocation_id and INSTANCE_SECRET.

syslog_address

Default:

b'/dev/log'

Address of the syslog to use by default. Note that if it's a string, it must be prepended with 'b' (just like the default is). In can also point to a UDP address in the form of a two-element list, such as ['127.0.0.1', 514]. See the logging documentation for more regarding sec-wall's approach to logging.

See also: log_file_config, log_level and syslog_facility.

syslog_facility

Default:

SysLogHandler.LOG_USER

The syslog's facility to use. It must be one of the symbolic values understood by the Python's logging machinery .

See also: log_file_config, log_level and syslog_address.

validation_precedence

Default:

['ssl-cert', 'basic-auth', 'digest-auth', 'wsse-pwd', 'custom-http', 'xpath']

Sets the precedence, the priority, of the config types. Used when there are conflicts in the urls variable. For instance, in the sample config below, both a custom HTTP header and an XPath expression have been configured. The HTTP header will be of a higher priority because 'custom-http' comes prior to 'xpath' on the validation_precedence list.

def my_config():
return {
'ssl': False,
'custom-http': True,
'custom-http-X-ABC':'foo',
'xpath': True,
'xpath-my-expr':"//foobar/@myattr='myvalue'",
'host': 'http://localhost:17090/'
}
 
urls = [
('/abc', my_config()),
]

Examples

There's a whole chapter devoted to nothing but showing various examples of sec-wall's configuration files, head over there for more information.

Exercises

Most of the sec-wall documentation's chapters end with an "Exercises" section which contains a set of assignments for you to tackle. They're meant to make yourself comfortable with the newly acquired material. If you feel like stumbling or would like to suggest any improvements, please let the project know. Thanks!
Here are the exercises prepared for this chapter:
  1. Create a new sec-wall instance and modify the INSTANCE_SECRET variable
  2. Try running sec-wall with at least one of the required variables missing. What happens when the instance is starting? What happens in runtime, when you access sec-wall from a browser?
  3. Create a new proxy, let the urls variable be an empty dictionary and set add_default_if_not_found to False. What happens if you access the proxy?
  4. Make use of ca_certs, certfile, crypto_dir, keyfile and server_type variables for configuring an instance to run using HTTPS instead of HTTP.
  5. Observe what headers the backend you're using returns and add them on the from_backend_ignore list. Confirm the result from your browser using Firebug or a similar tool.
  6. Use the host and port variables for running an instance on a different interface and port than it defaults to.
  7. Generate two new instances, set the instance_name of both to a different value and confirm the name is being logged by syslog when accessing the proxies.
  8. Use log_file_config and change the way logging messages are being emitted. For instance - but let that not bind you in any way - try configuring it to use ZeroMQ instead of syslog.
  9. Run syslog on a separate host and use the variable for configuring a sec-wall instance to use UDP for logging.
  10. Make use of the log_level variable and change the logging level to ERROR. Then to DEBUG.