sampledoc

Bcfg2 Data Encryption

New in version 1.3.0.

Bcfg2 supports encrypting some data on the disk, which can help protect sensitive data from other people who need access to the Bcfg2 repository but are perhaps not authorized to see all data. It supports multiple passphrases, which can be used to enforce separations between teams, environments, etc. Use of the encryption feature requires M2Crypto 0.18 or newer.

Note

This feature is not intended to secure the files against a malicious attacker who has gained access to your Bcfg2 server, as the encryption passphrases are held in plaintext in bcfg2.conf. This is only intended to make it easier to use a single Bcfg2 repository with multiple admins who should not necessarily have access to each other’s sensitive data.

Two basic types of data can be encrypted:

In general, Properties encryption is preferred for a few reasons:

  • It plays nicely with your VCS. If you change an encrypted Cfg file, then all you can see in your VCS log is that the file changed, no details about how it changed. With an encrypted Properties file, you can see which element changed (although obviously not the changed content).
  • It is faster when you have more than one passphrase. When decrypting a Cfg file, Bcfg2 simply brute-forces it with all known passphrases; when decrypting a Properties element, the passphrase is given by name so only one passphrase must be tried.
  • A Cfg file can only be encrypted with a single passphrase; Properties files can use different passphrases for different elements. If you are using different passphrases to segregate data amongst different teams, this lets teams collaborate more closely on files and other data.

Other types of data that can be encrypted are:

  • Text content of Path tags in Bundler
  • Passphrases in XML description files for generated SSH Keys

bcfg2-crypt

Encrypting and decrypting Cfg and Properties files can be done with the bcfg2-crypt tool, which mostly tries to do the right thing. I.e., it encrypts plaintext files, decrypts encrypted files, and automatically discovers if a file is Cfg or Properties. Its usage is thus generally very simple, e.g.:

bcfg2-crypt foo.conf
bcfg2-crypt foo.xml

Since the behavior of bcfg2-crypt varies significantly depending on whether you are dealing with a Cfg or Properties files, these are documented separately below. It’s also well worthwhile to familiarize yourself with the man page for bcfg2-crypt.

Encrypting Cfg Files

To encrypt a Cfg file, you can simply run:

bcfg2-crypt foo.conf

This will write the encrypted data to foo.conf.crypt. Once you are satisfied that the file has been encrypted as you wish, you can remove the plaintext version, or you can use the --remove flag of bcfg2-crypt.

To decrypt a file, simply run bcfg2-crypt again:

bcfg2-crypt foo.conf.crypt

On Cfg files, bcfg2-crypt is more-or-less equivalent to the following commands (encryption and decryption, respectively):

openssl enc -aes-256-cbc -k <passphrase> -in foo.conf \
    -out foo.conf.crypt -a
openssl enc -d -aes-256-cbc -k <passphrase> -in foo.conf.crypt \
    -out foo.conf -a

Those commands can be used in lieu of bcfg2-crypt if you hate convenience.

Encrypting Properties Files

To encrypt or decrypt a properties file, simply run:

bcfg2-crypt foo.xml

If the top-level tag of a Properties file is not <Properties>, then you need to use the --properties flag to bcfg2-crypt:

bcfg2-crypt --properties foo.xml

The first time you run bcfg2-crypt on a Properties file, it will encrypt all character data of all elements. Additionally, it will add encrypted="<key name>" to each element that has encrypted character data. It also adds encryption="true" to the top-level <Properties> tag as a flag to the server that it should try to decrypt the data in that file. (If you are using Properties schemas, you will need to make sure to add support for these attributes.) On subsequent runs, only those elements flagged with encrypted="*" are encrypted or decrypted.

To decrypt a Properties file, simply re-run bcfg2-crypt:

bcfg2-crypt foo.xml

This decrypts the encrypted elements, but it does not remove the encrypted attribute; this way, you can decrypt a Properties file, modify the contents, and then simply re-run bcfg2-crypt to encrypt it again. If you added elements that you also want to be encrypted, you can either add the encrypted attribute to them manually, or run:

bcfg2-crypt --xpath '*' foo.xml

You can also use the --xpath option to specify more restrictive XPath expressions to only encrypt a subset of elements, or to encrypt different elements with different passphrases. Alternatively, you can manally set the encrypted attribute on various elements and bcfg2-crypt will automatically do the right thing. You can also run bcfg2-crypt in interactive mode to interactively select which attributes should be encrypted:

bcfg2-crypt -I foo.xml

If you want to use different passphrases within a single Properties file, you must manually set the encrypted attribute.

Configuring Encryption

Passphrases

To configure encryption, add a [encryption] section to bcfg2.conf with any number of name-passphrase pairs.

For instance:

[encryption]
foo_team=P4ssphr4se
bar_team=Pa55phra5e

Note

The name of a passphrase cannot be algorithm or decrypt, which are reserved for other configuration options.

This would define two separate encryption passphrases, presumably for use by two separate teams. The passphrase names are completely arbitrary.

Note that this does entail a chicken-and-egg problem. In order for the Bcfg2 server to be able to decrypt encrypted files, the passphrases must exist in bcfg2.conf in plaintext; but, if you’re encrypting data, presumably you don’t want to include those plaintext passphrases in your Bcfg2 repository, so you’ll want to encrypt bcfg2.conf. The best way to solve this is:

  1. On your Bcfg2 server, manually add the [encryption] section to bcfg2.conf and restart the Bcfg2 server.
  2. Update bcfg2.conf in your Bcfg2 repository with the passphrases, and encrypt it.

The first (manual) step breaks the mutual dependency.

Algorithm

By default, Bcfg2 uses the AES-256-CBC cipher algorithm. If you wish to change this, you can set the algorithm option in the [encryption] section of bcfg2.conf:

[encryption]
algorithm = bf_cbc

The value of algorithm must be a valid OpenSSL cipher algorithm according the naming model of the Python M2Crypto module. To get a list of valid algorithms, you can run:

openssl list-cipher-algorithms | grep -v ' => ' | \
    tr 'A-Z-' 'a-z_' | sort -u

Lax vs. Strict decryption

By default, Bcfg2 expects to be able to decrypt every encrypted datum. Depending on how encryption is implemented at your site, though, that may not be possible. (For instance, if you use encryption to protect data for your production environment from your staging Bcfg2 server, then you would not expect the staging server to be able to decrypt everything.) In this case, you want to enable lax decryption in the [encryption] section of bcfg2.conf:

[encryption]
lax_decryption = true

This causes a failed decrypt to produce a warning only, not an error.

This can be overridden by individual XML files by setting lax_decryption="false" on the top-level tag (or, vice-versa; if strict is the default an XML file can specify lax_decryption="true".

Note that you could, for instance, set lax decryption by default, and then disable it on individual files.

Encryption API

Bcfg2.Server.Encryption provides a number of convenience methods for handling encryption in Bcfg2. See Bcfg2 Data Encryption for more details.

class Bcfg2.Server.Encryption.CLI(argv=None)[source]

Bases: object

The bcfg2-crypt CLI

run()[source]

Run bcfg2-crypt

class Bcfg2.Server.Encryption.CfgDecryptor(filename)[source]

Bases: Bcfg2.Server.Encryption.Decryptor

Decrypt Cfg files

decrypt()[source]

decrypt the given file, returning the plaintext data

class Bcfg2.Server.Encryption.CfgEncryptor(filename)[source]

Bases: Bcfg2.Server.Encryption.Encryptor

encryptor class for Cfg files

class Bcfg2.Server.Encryption.CryptoTool(filename)[source]

Bases: object

Generic decryption/encryption interface base object

get_destination_filename(original_filename)[source]

Get the filename where data should be written

write(data)[source]

write data to disk

Bcfg2.Server.Encryption.DECRYPT = 0

Constant representing the decryption operation for M2Crypto.EVP.Cipher, which uses a simple integer. This makes our code more readable.

exception Bcfg2.Server.Encryption.DecryptError[source]

Bases: exceptions.Exception

Exception raised when decryption fails.

class Bcfg2.Server.Encryption.Decryptor(filename)[source]

Bases: Bcfg2.Server.Encryption.CryptoTool

Decryptor interface

decrypt()[source]

decrypt the file, returning the encrypted data

Bcfg2.Server.Encryption.ENCRYPT = 1

Constant representing the encryption operation for M2Crypto.EVP.Cipher, which uses a simple integer. This makes our code more readable.

exception Bcfg2.Server.Encryption.EncryptError[source]

Bases: exceptions.Exception

Exception raised when encryption fails.

class Bcfg2.Server.Encryption.Encryptor(filename)[source]

Bases: Bcfg2.Server.Encryption.CryptoTool

encryptor interface

encrypt()[source]

encrypt the file, returning the encrypted data

Bcfg2.Server.Encryption.IV = '\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0'

Default initialization vector. For best security, you should use a unique IV for each message. ssl_encrypt() does this in an automated fashion.

exception Bcfg2.Server.Encryption.PassphraseError[source]

Bases: exceptions.Exception

Exception raised when there’s a problem determining the passphrase to encrypt or decrypt with

class Bcfg2.Server.Encryption.PropertiesCryptoMixin[source]

Bases: object

Mixin to provide some common methods for Properties crypto

class Bcfg2.Server.Encryption.PropertiesDecryptor(filename)[source]

Bases: Bcfg2.Server.Encryption.Decryptor, Bcfg2.Server.Encryption.PropertiesCryptoMixin

decryptor class for Properties files

class Bcfg2.Server.Encryption.PropertiesEncryptor(filename)[source]

Bases: Bcfg2.Server.Encryption.Encryptor, Bcfg2.Server.Encryption.PropertiesCryptoMixin

encryptor class for Properties files

Bcfg2.Server.Encryption.bruteforce_decrypt(crypted, passphrases=None, algorithm=None)[source]

Convenience method to decrypt the given encrypted string by trying the given passphrases or all passphrases sequentially until one is found that works.

Parameters:
  • crypted (string) – The data to decrypt
  • passphrases (list) – The passphrases to try.
  • algorithm (string) – The cipher algorithm to use
Returns:

string - The decrypted data

Raises :

M2Crypto.EVP.EVPError, if the data cannot be decrypted

Bcfg2.Server.Encryption.is_encrypted(val)[source]

Make a best guess if the value is encrypted or not. This just checks to see if val is a base64-encoded string whose content starts with “Salted__”, so it may have (rare) false positives. It will not have false negatives.

Bcfg2.Server.Encryption.print_xml(element, keep_text=False)[source]

Render an XML element for error output. This prefixes the line number and removes children for nicer display.

Parameters:
  • element (lxml.etree._Element) – The element to render
  • keep_text (boolean) – Do not discard text content from the element for display
Bcfg2.Server.Encryption.ssl_decrypt(data, passwd, algorithm=None)[source]

Decrypt openssl-encrypted data. This can decrypt data encrypted by ssl_encrypt(), or openssl enc. It performs a base64 decode first if the data is base64 encoded, and automatically determines the salt and initialization vector (both of which are embedded in the encrypted data).

Parameters:
  • data (string) – The encrypted data (either base64-encoded or raw binary) to decrypt
  • passwd (string) – The password to use to decrypt the data
  • algorithm (string) – The cipher algorithm to use
Returns:

string - The decrypted data

Bcfg2.Server.Encryption.ssl_encrypt(plaintext, passwd, algorithm=None, salt=None)[source]

Encrypt data in a format that is openssl compatible.

Parameters:
  • plaintext (string) – The plaintext data to encrypt
  • passwd (string) – The password to use to encrypt the data
  • algorithm (string) – The cipher algorithm to use
  • salt (bytes) – The salt to use. If none is provided, one will be randomly generated.
Returns:

string - The base64-encoded, salted, encrypted string. The string includes a trailing newline to make it fully compatible with openssl command-line tools.

Bcfg2.Server.Encryption.str_decrypt(crypted, key, iv='\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0', algorithm=None)[source]

Decrypt a string with a key. For a higher-level decryption interface, see ssl_decrypt().

Parameters:
  • crypted (string) – The raw binary encrypted data
  • key (string) – The encryption key to decrypt with
  • iv (string) – The initialization vector
  • algorithm (string) – The cipher algorithm to use
Returns:

string - The decrypted data

Bcfg2.Server.Encryption.str_encrypt(plaintext, key, iv='\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0', algorithm=None, salt=None)[source]

Encrypt a string with a key. For a higher-level encryption interface, see ssl_encrypt().

Parameters:
  • plaintext (string) – The plaintext data to encrypt
  • key (string) – The key to encrypt the data with
  • iv (string) – The initialization vector
  • algorithm (string) – The cipher algorithm to use
  • salt (string) – The salt to use
Returns:

string - The decrypted data