Cfg¶
The Cfg plugin provides a repository to describe configuration file contents for clients. In its simplest form, the Cfg repository is just a directory tree modeled off of the directory tree on your client machines.
The Cfg Repository¶
The Cfg plugin is enabled by including Cfg on the plugins line of
the [server] section of your Bcfg2 server config file. The repository
itself lives in /var/lib/bcfg2/Cfg
, assuming you are using the default
repository location of /var/lib/bcfg2
. The contents of this directory
are a series of directories corresponding to the real-life locations of
the files on your clients, starting at the root level. For example:
% ls Cfg
bin/ boot/ etc/ opt/ root/ usr/ var/
Specific config files go in like-named directories in this
heirarchy. For example the password file, /etc/passwd
, goes
in Cfg/etc/passwd/passwd
, while the ssh pam module config file,
/etc/pam.d/sshd
, goes in Cfg/etc/pam.d/sshd/sshd
. The reason for
the like-name directory is to allow multiple versions of each file to
exist, as described below. Note that these files are exact copies of what
will appear on the client machine (except when using templates – see
below).
Group-Specific Files¶
It is often the case that you want one version of a config file for
all of your machines except those in a particular group. For example,
/etc/fstab
should look alike on all of your desktop machines, but
should be different on your file servers. Bcfg2 can handle this case
through use of group-specific files.
As mentioned above, all Cfg entries live in like-named directories
at the end of their directory tree. In the case of fstab, the file at
Cfg/etc/fstab/fstab
will be handed out by default to any client that
asks for a copy of /etc/fstab
. Group-specific files are located in
the same directory and are named with the following syntax:
/path/to/filename/filename.GNN_groupname
NN is a priority number where 00 is lowest and 99
is highest, and groupname is the name of a group defined in
Metadata/groups.xml
. Back to our fstab example, we might have a
Cfg/etc/fstab/
directory that looks like this:
fstab
fstab.G50_server
fstab.G99_fileserver
By default, clients will receive the plain fstab file when they request
/etc/fstab
. Any machine that is in the server group, however, will
instead receive the fstab.G50_server
file. Finally, any machine that
is in the fileserver group will receive the fstab.G99_fileserver
file, even if they are also in the server group.
Host-Specific Files¶
Similar to the case with group-specific files, there are cases where a specific machine should have a different version of a file than all others. This can be accomplished with host-specific files. The format of a host-specific file name is:
/path/to/filename/filename.H_host.example.com
Host-specific files have a higher priority than group specific files. Again, the fstab example:
fstab
fstab.G50_server
fstab.G99_fileserver
fstab.H_host.example.com
In this case, host.example.com will always get the host-specific version, even if it is part of the server or fileserver (or both) classes.
Note
If you have the ability to choose between using a group-specific and a host-specific file, it is almost always best to use a group-specific one. That way if a hostname changes or an extra copy of a particular client is built, it will get the same changes as the original.
Templates¶
Genshi Templates¶
Genshi templates allow you to use the Genshi templating system. Genshi templates
should be named with a .genshi
extension, e.g.:
% ls Cfg/etc/motd
info.xml motd.genshi
See the genshi documentation for examples of Genshi syntax.
Troubleshooting¶
When developing a template, you can see what the template would generate on a client with bcfg2-info:
bcfg2-info buildfile <path> <hostname>
E.g.:
bcfg2-info buildfile /etc/foo.conf foo.example.com
To generate a file with an altsrc attribute, you can run:
bcfg2-info buildfile /etc/foo/foo.conf --altsrc=/etc/foo.conf \
foo.example.com
Sometimes, it’s useful to be able to do more in-depth troubleshooting
by running the template manually. To do this, run bcfg2-info
debug
, and, once in the Python interpreter, run:
metadata = self.build_metadata("<hostname>")
source_path = "<full path to template>"
name = source_path[len(self.setup['repo']):]
Then, run:
import os
from genshi.template import TemplateLoader, NewTextTemplate
template = TemplateLoader().load(source_path, cls=NewTextTemplate)
data = dict(metadata=metadata,
source_path=source_path,
path=source_path,
name=name,
repo=self.setup['repo'])
print(template.generate(**data).render())
This gives you more fine-grained control over how your template is rendered. E.g., you can tweak the values of the variables passed to the template, or evaluate the template manually, line-by-line, and so on.
You can also use this approach to render templates that depend on
altsrc tags by setting
source_path
to the path to the template, and setting name
to the path
to the file to be generated, e.g.:
metadata = self.build_metadata("foo.example.com")
source_path = "/Cfg/etc/sysconfig/network-scripts/ifcfg-template/ifcfg-template.genshi"
name = "/etc/sysconfig/network-scripts/ifcfg-bond0"
Error handling¶
Situations may arise where a templated file cannot be generated due to missing or incomplete information. A TemplateError can be raised to force a bind failure and prevent sending an incomplete file to the client. For example, this template:
{% python
from genshi.template import TemplateError
grp = None
for g in metadata.groups:
if g.startswith('ganglia-gmond-'):
grp = g
break
else:
raise TemplateError, "Missing group"
%}\
will fail to bind if the client is not a member of a group starting with “ganglia-gmond-”. The syslogs on the server will contain this message:
bcfg2-server[5957]: Genshi template error: Missing group
bcfg2-server[5957]: Failed to bind entry: Path /etc/ganglia/gmond.conf
...indicating the bind failure and message raised with the TemplateError.
Handling Dollar Signs¶
In a Genshi template, $
is a special character and must be escaped
by doubling, i.e., $$
. For instance, to embed the Subversion
$Id$
keyword in a Genshi template, you would have to do $$Id$$
.
Examples¶
Cheetah Templates¶
Cheetah templates allow you to use the cheetah templating system. Cheetah templates should be
named with a .cheetah
extension, e.g.:
% ls Cfg/etc/motd
info.xml motd.cheetah
Comments and Cheetah¶
As Cheetah processes your templates it will consider hash “#” style comments to be actual comments in the template and will strip them from the final config file. If you would like to preserve the comment in the final config file you need to escape the hash character ‘#’ which will tell Cheetah (and Python) that you do in fact want the comment to appear in the final config file.:
# This is a comment in my template which will be stripped when it's processed through Cheetah
\# This comment will appear in the generated config file.
Jinja2 Templates¶
Jinja2 templates allow you to use the jinja2 templating system. Jinja2 templates should be
named with a .jinja2
extension, e.g.:
% ls Cfg/etc/motd
info.xml motd.jinja2
Inside Templates¶
Several variables are pre-defined inside templates:
Name | Description |
---|---|
metadata | Client metadata |
name | The value of the name attribute as specified in
the Path entry in Bcfg2. |
source_path | The path to the template file on the filesystem |
repo | The path to the Bcfg2 repository on the filesystem |
path | In Genshi templates, path is a synonym for
source_path . In Cheetah templates and Jinja2
templates, it’s a synonym for name . For this
reason, use of path is discouraged, and it may be
deprecated in a future release. |
To access these variables in a Genshi template, you can simply use the name, e.g.:
Path to this file: ${name}
Similarly, in a Jinja2 template:
Path to this file: {{ name }}
In a Cheetah template, the variables are properties of self
,
e.g.:
Path to this file: $self.name
Notes on Using Templates¶
Templates can be host and group specific as well. Deltas will not be processed for any Genshi, Cheetah, or Jinja2 base file.
Note
If you are using templating in combination with host-specific
or group-specific files, you will need to ensure that the .genshi
.cheetah
or .jinja2
extension is at the end of the filename.
Using the examples from above for host.example.com and group server
you would have the following:
Cfg/etc/fstab/fstab.H_host.example.com.genshi
Cfg/etc/fstab/fstab.G50_server.cheetah
You can mix Genshi and Cheetah when using different host-specific or group-specific files. For example:
Cfg/etc/fstab/fstab.H_host.example.com.genshi
Cfg/etc/fstab/fstab.G50_server.cheetah
Encrypted Files¶
New in version 1.3.0.
Bcfg2 allows you to encrypt files stored in Cfg/
to protect the
data in them from other people who need access to the repository. See
also Encrypted Properties data for
information on encrypting elements in Properties files, which is often
more friendly for tracking changes in a VCS.
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.
See Bcfg2 Data Encryption for more details on encryption in Bcfg2 in general.
Encrypting Files¶
An encrypted file should end with .crypt
, e.g.:
Cfg/etc/foo.conf
Cfg/etc/foo.conf/foo.conf.crypt
Cfg/etc/foo.conf/foo.conf.G10_foo.crypt
Encrypted Genshi, Cheetah, and Jinja2 templates can have the extensions in either order, e.g.:
Cfg/etc/foo.conf/foo.conf.crypt.genshi
Cfg/etc/foo.conf/foo.conf.G10_foo.genshi.crypt
Cfg/etc/foo.conf/foo.conf.H_bar.example.com.crypt.cheetah
To encrypt or decrypt a file, use bcfg2-crypt.
SSH Keys¶
New in version 1.3.0.
Cfg can also be used to automatically create and distribute SSH key
pairs and the authorized_keys
file.
Keys can be created one of two ways:
- Host-specific keys, where each client has its own key pair. This is the default.
- Group-specific keys. To do this, you must set
category
in eitherbcfg2.conf
(see “Configuration” below) or inprivkey.xml
. Keys created for a given client will be specific to that client’s group in the specified category.
Group-specific keys are useful if, for instance, you have multiple distinct environments (development, testing, production, for example) and want to maintain separate keys for each environment.
This feature actually creates static keys, much like the SSHbase plugin creates SSH certificates. It doesn’t generate them on the fly for each request; it generates the key once, then saves it to the filesystem.
Creating key pairs¶
To create an SSH key pair, you need to define how the private key will
be created in privkey.xml
. For instance, to create
/home/foo/.ssh/id_rsa
, you would create
/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa/privkey.xml
.
This will create both the private key and the public key; the latter
is created by appending .pub
to the private key filename. It is
not possible to change the public key filename.
You may optionally also create a corresponding pubkey.xml
, which
will allow the key pair to be created when the public key is
requested. (For the example above, you’d create
/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa.pub/pubkey.xml
. This can
speed up the propagation of SSH keys throughout your managed systems,
particularly if you use the authorized_keys
generation feature.
privkey.xml
¶
privkey.xml
contains a top-level PrivateKey
element, and is
structured as follows:
- element PrivateKey¶
- Top-level tag for describing a generated SSH key pair.
- Attributes:
Name Description Values Required Default category
Create keys specific to the given category, instead of specific to the category given inbcfg2.conf
.string
No None lax_decryption
Override the global lax_decryption setting inbcfg2.conf
.true
|false
No None perhost
Create keys on a per-host basis (rather than on a per-group basis).true
|false
No None priority
Create group-specific keys with the given priority.positiveInteger
No 50
- Attribute groups:
- Child elements:
- element Passphrase¶
-
Type:
PassphraseType
- element Params¶
-
Type:
PrivateKeyParamsType
- element Group¶
-
Type:
PrivateKeyGroupType
- element Client¶
-
Type:
PrivateKeyGroupType
- Element groups:
py:genshiElements
- complexType PassphraseType¶
- Specify the private key passphrase.
- complexType PrivateKeyParamsType¶
- Specify parameters for creating the private key
- Attributes:
Name Description Values Required Default bits
Number of bits in the key. See ssh-keygen(1) for defaults.positiveInteger
No None type
Key type to create.rsa
|dsa
No rsa
- Attribute groups:
- complexType PrivateKeyGroupType¶
- A PrivateKeyGroupType is a tag used to provide logic. Child entries of a PrivateKeyGroupType tag only apply to machines that match the condition specified – either membership in a group, or a matching client name.
negate
can be set to negate the sense of the match.- Attributes:
Name Description Values Required Default name
The name of the client or group to match on. Child entries will only apply to this client or group (unlessnegate
is set).string
No None negate
Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name.true
|false
No None - Attribute groups:
- Child elements:
- Element groups:
py:genshiElements
- group py:genshiElements¶
- Most Genshi templating directives can be used either as standalone elements or as attributes on existing elements. This element group defines the standalone tags.
- Elements:
- element py:with¶
-
Type:
py:withType
- element py:replace¶
-
Type:
py:replaceType
- element py:choose¶
-
Type:
py:chooseType
- element py:for¶
-
Type:
py:forType
- element py:match¶
-
Type:
py:matchType
- element py:def¶
-
Type:
py:defType
- complexType py:withType¶
- Attributes:
Name Description Values Required Default py:vars
A semicolon-delimited list of variables to define and their values.string
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:replaceType¶
- Attributes:
Name Description Values Required Default py:value
The value to replace the contents with.string
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:chooseType¶
- Attributes:
Name Description Values Required Default py:test
Iftest
is set, the childpy:when
directives are tested for equality to the value of the expression.string
No None - Child elements:
- element py:when¶
The
when
directive is used insidepy:chooseType
orchoose
to handle a single specific condition.Type:
py:ifType
- element py:otherwise¶
-
Type:
py:otherwiseType
- Text content:
- Any
- complexType py:forType¶
- Attributes:
Name Description Values Required Default py:each
The loop iteratorstring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:ifType¶
- Attributes:
Name Description Values Required Default py:test
The statement giving the value to teststring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:matchType¶
- Attributes:
Name Description Values Required Default py:path
XPath expression to search for in the template.string
Yes None py:buffer
Whether the matched content should be buffered in memory. Buffering can improve performance a bit at the cost of needing more memory during rendering. Buffering is required for match templates that contain more than one invocation of theselect()
function. If there is only one call, and the matched content can potentially be very long, consider disabling buffering to avoid excessive memory use.true
|false
No true
py:once
Whether the engine should stop looking for more matching elements after the first match. Use this on match templates that match elements that can only occur once in the stream, such as the <head> or <body> elements in an HTML template, or elements with a specific ID.true
|false
No false
py:recursive
Whether the match template should be applied to its own output. Note that once implies non-recursive behavior, so this attribute only needs to be set for match templates that don’t also have once set.true
|false
No true
- Text content:
- Any
Any arbitrary child elements allowed
- complexType py:defType¶
- Attributes:
Name Description Values Required Default py:function
The function prototypestring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:otherwiseType¶
- The
otherwise
directive is used insidepy:chooseType
orchoose
to handle all conditions not handled by apy:when
.- Text content:
- Any
Any arbitrary child elements allowed
See Bcfg2 Data Encryption for more details on encryption in Bcfg2 in general.
pubkey.xml
¶
pubkey.xml
only ever contains a single line:
<PublicKey/>
- element PublicKey¶
- Top-level tag for flagging a generated SSH public key.
It acts only as a flag to Bcfg2 that a key pair should be generated, if
none exists, using the associated privkey.xml
file. The path to
privkey.xml
is determined by removing .pub
from the directory
containing pubkey.xml
. I.e., if you create
/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa.pub/pubkey.xml
, then Bcfg2
will use /var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa/privkey.xml
to
create the key pair.
Use of pubkey.xml
is optional, but is recommended. If you do not
use pubkey.xml
files, you may encounter two problems:
- On the first Bcfg2 client run on a given client, the private keys
may be present but the public keys may not be. This will be fixed
by running
bcfg2
again. - If you are including an automatically created public key in
authorized_keys
, it will not be created until the client the key is for requests the key pair.
As an example of this latter scenario, suppose that your
authorized_keys.xml
allows access to foo.example.com from
/root/.ssh/id_rsa.pub
for bar.example.com. If bar.example.com has
not run the Bcfg2 client, then no key pair will have been generated,
and generating the foo.example.com authorized_keys
file will
create a warning. But if you create
Cfg/root/.ssh/id_rsa.pub/pubkey.xml
, then building
authorized_keys
for foo.example.com will create root’s keypair for
bar.example.com.
Note
In order to use pubkey.xml
, there must be a corresponding
privkey.xml
. You cannot, for instance, populate a directory
with manually-generated private SSH keys, drop pubkey.xml
in
the related public key directory, and expect Bcfg2 to generate the
public keys. It will not.
Examples¶
privkey.xml
can, at its simplest, be very simple indeed:
<PrivateKey/>
This will create a private key with all defaults. Or it can be more complex:
<PrivateKey category="environment"/>
<Params bits="1024" type="dsa"/>
<Group name="secure">
<Passphrase encrypted="secure">U2FsdGVkX19xACol83uyPELP94s4CmngD12oU6PLLuE=</Passphrase>
</Group>
</PrivateKey>
This creates a 1024-bit DSA key for each group in the environment
category, and keys for clients in the secure
group will be
protected with the given (encrypted) passphrase.
To complete the example, assume that this file was saved at
/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa/privkey.xml
. If a client
in the development
group, which is a group in the environment
category, requests the private key, then the following files would be
created:
/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa/id_rsa.G50_development
/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa.pub/id_rsa.pub.G50_development
/var/lib/bcfg2/Cfg/home/foo/.ssh/id_rsa.pub
would be created if it
did not exist.
Subsequent clients that were also members of the development
environment would get the keys that have already been generated.
pubkey.xml
always contains a single empty tag:
<PublicKey/>
Generating authorized_keys
¶
authorized_keys
can be automatically generated from public SSH
keys that exist in the Cfg tree. The keys in question can be
generated from privkey.xml
, or they can be manually created.
If a key doesn’t exist when authorized_keys
is generated, the key
will only be created if pubkey.xml
exists. If that is not the
case, a warning will be produced.
To generate authorized_keys
, create authorized_keys.xml
, e.g.:
/var/lib/bcfg2/Cfg/root/.ssh/authorized_keys/authorized_keys.xml
.
authorized_keys.xml
¶
authorized_keys.xml
is structured as follows:
- element AuthorizedKeys¶
-
Type:
AuthorizedKeysType
- complexType AuthorizedKeysType¶
- Top-level tag for describing a generated SSH key pair.
- Attributes:
Name Description Values Required Default lax_decryption
Override the global lax_decryption setting inbcfg2.conf
.true
|false
No None - Attribute groups:
- Child elements:
- element Group¶
-
Type:
AuthorizedKeysGroupType
- element Client¶
-
Type:
AuthorizedKeysGroupType
- Element groups:
py:genshiElements
- complexType AllowType¶
- Allow access from a public key, given either as text content, or described by the attributes.
- Attributes:
Name Description Values Required Default category
Use a public key specific to the group in the given category, instead of the category specified inbcfg2.conf
.string
No None from
The path of the public key to allow.string
No None group
Use a public key specific to the given group, instead of the public key specific to the appropriate category group of the current client.string
No None host
Use a public key specific to the given host.string
No None - Attribute groups:
- Child elements:
- element Option¶
-
Type:
AuthorizedKeysOptionType
- Element groups:
py:genshiElements
- Text content:
- Any
- complexType AuthorizedKeysGroupType¶
- An AuthorizedKeysGroupType is a tag used to provide logic. Child entries of an AuthorizedKeysGroupType tag only apply to machines that match the condition specified – either membership in a group, or a matching client name.
negate
can be set to negate the sense of the match.- Attributes:
Name Description Values Required Default name
The name of the client or group to match on. Child entries will only apply to this client or group (unlessnegate
is set).string
No None negate
Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name.true
|false
No None - Attribute groups:
- Child elements:
- Element groups:
py:genshiElements
- group py:genshiElements¶
- Most Genshi templating directives can be used either as standalone elements or as attributes on existing elements. This element group defines the standalone tags.
- Elements:
- element py:with¶
-
Type:
py:withType
- element py:replace¶
-
Type:
py:replaceType
- element py:choose¶
-
Type:
py:chooseType
- element py:for¶
-
Type:
py:forType
- element py:match¶
-
Type:
py:matchType
- element py:def¶
-
Type:
py:defType
- complexType AuthorizedKeysOptionType¶
- Specify options for public key authentication and connection. See sshd(8) for details on allowable options.
- complexType py:withType¶
- Attributes:
Name Description Values Required Default py:vars
A semicolon-delimited list of variables to define and their values.string
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:replaceType¶
- Attributes:
Name Description Values Required Default py:value
The value to replace the contents with.string
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:chooseType¶
- Attributes:
Name Description Values Required Default py:test
Iftest
is set, the childpy:when
directives are tested for equality to the value of the expression.string
No None - Child elements:
- element py:when¶
The
when
directive is used insidepy:chooseType
orchoose
to handle a single specific condition.Type:
py:ifType
- element py:otherwise¶
-
Type:
py:otherwiseType
- Text content:
- Any
- complexType py:forType¶
- Attributes:
Name Description Values Required Default py:each
The loop iteratorstring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:ifType¶
- Attributes:
Name Description Values Required Default py:test
The statement giving the value to teststring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:matchType¶
- Attributes:
Name Description Values Required Default py:path
XPath expression to search for in the template.string
Yes None py:buffer
Whether the matched content should be buffered in memory. Buffering can improve performance a bit at the cost of needing more memory during rendering. Buffering is required for match templates that contain more than one invocation of theselect()
function. If there is only one call, and the matched content can potentially be very long, consider disabling buffering to avoid excessive memory use.true
|false
No true
py:once
Whether the engine should stop looking for more matching elements after the first match. Use this on match templates that match elements that can only occur once in the stream, such as the <head> or <body> elements in an HTML template, or elements with a specific ID.true
|false
No false
py:recursive
Whether the match template should be applied to its own output. Note that once implies non-recursive behavior, so this attribute only needs to be set for match templates that don’t also have once set.true
|false
No true
- Text content:
- Any
Any arbitrary child elements allowed
- complexType py:defType¶
- Attributes:
Name Description Values Required Default py:function
The function prototypestring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:otherwiseType¶
- The
otherwise
directive is used insidepy:chooseType
orchoose
to handle all conditions not handled by apy:when
.- Text content:
- Any
Any arbitrary child elements allowed
Example¶
<AuthorizedKeys>
<Group name="some_group">
<Allow from="/root/.ssh/id_rsa.pub"/>
<Allow from="/root/.ssh/id_rsa.pub" group="test"/>
</Group>
<Allow from="/root/.ssh/id_rsa.pub" host="foo.example.com"/>
<Allow from="/home/foo_user/.ssh/id_rsa.pub">
<Option name="command" value="/home/foo_user/.ssh/ssh_command_filter"/>
<Option name="no-X11-forwarding"/>
</Allow>
<Allow>
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDw/rgKQeARRAHK5bQQhAAe1b+gzdtqBXWrZIQ6cIaLgxqj76TwZ3DY4A6aW9RgC4zzd0p4a9MfsScUIB4+UeZsx9GopUj4U6H8Vz7S3pXxrr4E9logVLuSfOLFbI/wMWNRuOANqquLYQ+JYWKeP4kagkVp0aAWp7mH5IOI0rp0A6qE2you4ep9N/nKvHDrtypwhYBWprsgTUXXMHnAWGmyuHGYWxNYBV9AARPdAvZfb8ggtuwibcOULlyK4DdVNbDTAN1/BDBE1ve6WZDcrc386KhqUGj/yoRyPjNZ46uZiOjRr3cdY6yUZoCwzzxvm5vle6mEbLjHgjGEMQMArzM9 vendor@example.com
</Allow>
</AuthorizedKeys>
Note
authorized_keys.xml
allows you to specify the group whose
public key should be allowed. This retrieves the public key
specific to that group (if it exists), not the public key for
all hosts in that group. This is due to the performance penalties
that would be imposed by that approach.
Similarly, it is not possible to allow access from all keys for a given user (i.e., at a given path).
Hopefully, the performance concerns can be resolved in a future release and these features can be added.
SSL Keys and Certificates¶
Cfg can also create SSL keys and certs on the fly, and store the generated data in the repo so that subsequent requests do not result in repeated key/cert recreation. In the event that a new key or cert is needed, the old file can simply be removed from the repository, and the next time that host checks in, a new file will be created. If that file happens to be the key, any dependent certificates will also be regenerated.
See also Automated Bcfg2 SSL Authentication for a detailed example that uses the SSL key management feature to automate Bcfg2 certificate authentication.
Getting started¶
In order to use the SSL certificate generation feature, you must first have at least one CA configured on your system. For details on setting up your own OpenSSL based CA, please see http://www.openssl.org/docs/apps/ca.html for details of the suggested directory layout and configuration directives.
For SSL cert generation to work, the openssl.cnf (or other configuration file) for that CA must contain full (not relative) paths.
Add a section to your
/etc/bcfg2.conf
calledsslca_foo
, replacing foo with the name you wish to give your CA so you can reference it in certificate definitions. (If you only have one CA, you can name itsslca_default
, and it will be the default CA for all other operations.)Under that section, add a
config
option that gives the location of theopenssl.cnf
file for your CA.If necessary, add a
passphrase
option containing the passphrase for the CA’s private key. If no passphrase is entry exists, it is assumed that the private key is stored unencrypted.Optionally, add a
chaincert
option that points to the location of your ssl chaining certificate. This is used when preexisting certificate hostfiles are found, so that they can be validated and only regenerated if they no longer meet the specification. If you’re using a self signing CA this would be the CA cert that you generated. If the chain cert is a root CA cert (e.g., if it is a self-signing CA), also add an entryroot_ca = true
. Ifchaincert
is omitted, certificate verification will not be performed.Once all this is done, you should have a section in your
/etc/bcfg2.conf
that looks similar to the following:[sslca_default] config = /etc/pki/CA/openssl.cnf passphrase = youReallyThinkIdShareThis? chaincert = /etc/pki/CA/chaincert.crt root_ca = true
You are now ready to create key and certificate definitions. For this example we’ll assume you’ve added Path entries for the key,
/etc/pki/tls/private/localhost.key
, and the certificate,/etc/pki/tls/certs/localhost.crt
to a bundle.Within the
Cfg/etc/pki/tls/private/localhost.key
directory, create a sslkey.xml file containing the following:<KeyInfo/>
This will cause the generation of an SSL key when a client requests that Path. (By default, it will be a 2048-bit RSA key; see sslkey.xml for details on how to change the key type and size.)
Similarly, create sslcert.xml in
Cfg/etc/pki/tls/certs/localhost.cfg/
, containing the following:<CertInfo> <Cert key="/etc/pki/tls/private/localhost.key" ca="foo"/> </CertInfo>
When a client requests the cert path, a certificate will be generated using the key hostfile at the specified key location, using the CA matching the
ca
attribute. ie.ca="foo"
will match[sslca_default]
in your/etc/bcfg2.conf
The Bcfg2 bundle example contains entries to automate the process of setting up a CA.
Configuration¶
bcfg2.conf
¶
In bcfg2.conf
, you must declare your CA(s) in [sslca_<name>]
sections. At least one is required. Valid options are detailed
below, in Cfg Configuration.
Only the config
option is required; i.e., the simplest possible CA
section is:
[sslca_default]
config = /etc/pki/CA/openssl.cnf
sslcert.xml
¶
- schema sslca-cert.xsd¶
- Schema for SSL Keys and Certificates
sslcert.xml
- element CertInfo¶
-
Type:
CertInfoType
- complexType CertInfoType¶
- Top-level tag for describing an SSLCA generated certificate.
- Attributes:
Name Description Values Required Default lax_decryption
Override the global lax_decryption setting inbcfg2.conf
.true
|false
No None - Child elements:
- element Cert¶
-
- Attributes:
Name Description Values Required Default key
The full path to the key entry to use for this certificate. This is the client path; e.g., for a key defined at/var/lib/bcfg2/SSLCA/etc/pki/tls/private/foo.key/sslkey.xml
, key should be/etc/pki/tls/private/foo.key
.string
Yes None append_chain
Append the CA chain certificate to the generated certificate (e.g., to produce a certificate in the format required by Nginx.)true
|false
No false
c
Override the country set in the CA configstring
No None ca
The name of the CA (from bcfg2.conf) to use to generate this certificate.string
No default
days
Time (in days) the certificate will be valid for.integer
No 365
emailaddress
Override the email address set in the CA configstring
No None format
The certificate format to produce.pem
No pem
l
Override the location set in the CA configstring
No None o
Override the organization set in the CA configstring
No None ou
Override the organizational unit set in the CA configstring
No None st
Override the state set in the CA configstring
No None - Attribute groups:
- element Group¶
-
Type:
SSLCACertGroupType
- element Client¶
-
Type:
SSLCACertGroupType
- Element groups:
py:genshiElements
- complexType SSLCACertGroupType¶
- An SSLCACertGroupType is a tag used to provide logic. Child entries of an SSLCACertGroupType tag only apply to machines that match the condition specified – either membership in a group, or a matching client name.
negate
can be set to negate the sense of the match.- Attributes:
Name Description Values Required Default name
The name of the client or group to match on. Child entries will only apply to this client or group (unlessnegate
is set).string
No None negate
Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name.true
|false
No None - Attribute groups:
- Child elements:
- Element groups:
py:genshiElements
- group py:genshiElements¶
- Most Genshi templating directives can be used either as standalone elements or as attributes on existing elements. This element group defines the standalone tags.
- Elements:
- element py:with¶
-
Type:
py:withType
- element py:replace¶
-
Type:
py:replaceType
- element py:choose¶
-
Type:
py:chooseType
- element py:for¶
-
Type:
py:forType
- element py:match¶
-
Type:
py:matchType
- element py:def¶
-
Type:
py:defType
- complexType py:withType¶
- Attributes:
Name Description Values Required Default py:vars
A semicolon-delimited list of variables to define and their values.string
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:replaceType¶
- Attributes:
Name Description Values Required Default py:value
The value to replace the contents with.string
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:chooseType¶
- Attributes:
Name Description Values Required Default py:test
Iftest
is set, the childpy:when
directives are tested for equality to the value of the expression.string
No None - Child elements:
- element py:when¶
The
when
directive is used insidepy:chooseType
orchoose
to handle a single specific condition.Type:
py:ifType
- element py:otherwise¶
-
Type:
py:otherwiseType
- Text content:
- Any
- complexType py:forType¶
- Attributes:
Name Description Values Required Default py:each
The loop iteratorstring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:ifType¶
- Attributes:
Name Description Values Required Default py:test
The statement giving the value to teststring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:matchType¶
- Attributes:
Name Description Values Required Default py:path
XPath expression to search for in the template.string
Yes None py:buffer
Whether the matched content should be buffered in memory. Buffering can improve performance a bit at the cost of needing more memory during rendering. Buffering is required for match templates that contain more than one invocation of theselect()
function. If there is only one call, and the matched content can potentially be very long, consider disabling buffering to avoid excessive memory use.true
|false
No true
py:once
Whether the engine should stop looking for more matching elements after the first match. Use this on match templates that match elements that can only occur once in the stream, such as the <head> or <body> elements in an HTML template, or elements with a specific ID.true
|false
No false
py:recursive
Whether the match template should be applied to its own output. Note that once implies non-recursive behavior, so this attribute only needs to be set for match templates that don’t also have once set.true
|false
No true
- Text content:
- Any
Any arbitrary child elements allowed
- complexType py:defType¶
- Attributes:
Name Description Values Required Default py:function
The function prototypestring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:otherwiseType¶
- The
otherwise
directive is used insidepy:chooseType
orchoose
to handle all conditions not handled by apy:when
.- Text content:
- Any
Any arbitrary child elements allowed
Example¶
<CertInfo>
<subjectAltName>test.example.com</subjectAltName>
<Group name="apache">
<Cert key="/etc/pki/tls/private/foo.key" days="730"/>
</Group>
<Group name="nginx">
<Cert key="/etc/pki/tls/private/foo.key" days="730"
append_chain="true"/>
</Group>
</CertInfo>
sslkey.xml
¶
- schema sslca-key.xsd¶
- Schema for SSL Keys and Certificates
sslkey.xml
- element KeyInfo¶
-
Type:
KeyInfoType
- complexType KeyInfoType¶
- Top-level tag for describing an SSLCA generated key.
- Attributes:
Name Description Values Required Default category
Create keys specific to the given category, instead of specific to the category given inbcfg2.conf
.string
No None perhost
Create keys on a per-host basis (rather than on a per-group basis).true
|false
No None priority
Create group-specific keys with the given priority.positiveInteger
No 50
- Child elements:
- element Key¶
-
- Attributes:
Name Description Values Required Default bits
The key lengthnonNegativeInteger
No 2048
type
The key typestring
No rsa
- Attribute groups:
- element Group¶
-
Type:
SSLCAKeyGroupType
- element Client¶
-
Type:
SSLCAKeyGroupType
- Element groups:
py:genshiElements
- complexType SSLCAKeyGroupType¶
- An SSLCAKeyGroupType is a tag used to provide logic. Child entries of an SSLCAKeyGroupType tag only apply to machines that match the condition specified – either membership in a group, or a matching client name.
negate
can be set to negate the sense of the match.- Attributes:
Name Description Values Required Default name
The name of the client or group to match on. Child entries will only apply to this client or group (unlessnegate
is set).string
No None negate
Negate the sense of the match, so that child entries only apply to a client if it is not a member of the given group or does not have the given name.true
|false
No None - Attribute groups:
- Child elements:
- Element groups:
py:genshiElements
- group py:genshiElements¶
- Most Genshi templating directives can be used either as standalone elements or as attributes on existing elements. This element group defines the standalone tags.
- Elements:
- element py:with¶
-
Type:
py:withType
- element py:replace¶
-
Type:
py:replaceType
- element py:choose¶
-
Type:
py:chooseType
- element py:for¶
-
Type:
py:forType
- element py:match¶
-
Type:
py:matchType
- element py:def¶
-
Type:
py:defType
- complexType py:withType¶
- Attributes:
Name Description Values Required Default py:vars
A semicolon-delimited list of variables to define and their values.string
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:replaceType¶
- Attributes:
Name Description Values Required Default py:value
The value to replace the contents with.string
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:chooseType¶
- Attributes:
Name Description Values Required Default py:test
Iftest
is set, the childpy:when
directives are tested for equality to the value of the expression.string
No None - Child elements:
- element py:when¶
The
when
directive is used insidepy:chooseType
orchoose
to handle a single specific condition.Type:
py:ifType
- element py:otherwise¶
-
Type:
py:otherwiseType
- Text content:
- Any
- complexType py:forType¶
- Attributes:
Name Description Values Required Default py:each
The loop iteratorstring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:ifType¶
- Attributes:
Name Description Values Required Default py:test
The statement giving the value to teststring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:matchType¶
- Attributes:
Name Description Values Required Default py:path
XPath expression to search for in the template.string
Yes None py:buffer
Whether the matched content should be buffered in memory. Buffering can improve performance a bit at the cost of needing more memory during rendering. Buffering is required for match templates that contain more than one invocation of theselect()
function. If there is only one call, and the matched content can potentially be very long, consider disabling buffering to avoid excessive memory use.true
|false
No true
py:once
Whether the engine should stop looking for more matching elements after the first match. Use this on match templates that match elements that can only occur once in the stream, such as the <head> or <body> elements in an HTML template, or elements with a specific ID.true
|false
No false
py:recursive
Whether the match template should be applied to its own output. Note that once implies non-recursive behavior, so this attribute only needs to be set for match templates that don’t also have once set.true
|false
No true
- Text content:
- Any
Any arbitrary child elements allowed
- complexType py:defType¶
- Attributes:
Name Description Values Required Default py:function
The function prototypestring
Yes None - Text content:
- Any
Any arbitrary child elements allowed
- complexType py:otherwiseType¶
- The
otherwise
directive is used insidepy:chooseType
orchoose
to handle all conditions not handled by apy:when
.- Text content:
- Any
Any arbitrary child elements allowed
Example¶
<KeyInfo>
<Group name="fast">
<Key type="rsa" bits="1024"/>
</Group>
<Group name="secure">
<Key type="rsa" bits="4096"/>
</Group>
</KeyInfo>
Content Validation¶
To ensure that files with invalid content are not pushed out, you can
provide a content validation script that will be run against each
file. Create a file called :test
inside the directory for the
file you want to test. For example:
Cfg/etc/sudoers/:test
You can also create host- and group-specific validators:
Cfg/etc/sudoers/:test.G80_foogroup
Cfg/etc/sudoers/:test.H_bar.example.com
A validator script has the following attributes:
- It must be executable, or specify a valid bangpath;
- The entire content of the file is passed to the validator on stdin;
- The validator is not called with any flags or arguments;
- The validator must return 0 on success and non-zero on failure; and
- The validator must output a sensible error message on failure.
For sudoers
, a very simple validator is:
#!/bin/sh
visudo -cf -
This uses the visudo
command’s built-in validation.
If you wish to disable validation, this can be done with the following
setting in bcfg2.conf
:
[cfg]
validation=no
If you have a very large number of validators, you may wish to disable
validation by default to avoid slowing down the generation of
configurations on the server, and use bcfg2-test
(for instance, as
a post-commit hook or as part of a code review process) to run
validation. You can do this by setting validation=no
in
bcfg2.conf
as described above, and then calling bcfg2-test
with the --cfg-validation
flag.
File permissions¶
File permissions for entries handled by Cfg are controlled via the use of info.xml files. Note that you cannot use both a Permissions entry and a Path entry to handle the same file.
Cfg Configuration¶
The behavior of many bits of the Cfg plugin can be configured in
bcfg2.conf
with the following options.
In addition to privkey.xml
and authorized_keys.xml
, described
above, the behavior of the SSH key generation feature can be
influenced by several options in the [sshkeys]
section of
bcfg2.conf
:
Section | Option | Description | Values | Default |
---|---|---|---|---|
cfg |
passphrase |
Use the named passphrase to encrypt created data on the
filesystem. (E.g., SSH and SSL keys.) The passphrase
must be defined in the [encryption] section. |
String | None |
cfg |
category |
Generate data (e.g., SSH keys, SSL keys and certs) specific to groups in the given category. It is best to pick a category that all clients have a group from. | String | None |
cfg |
validation |
Whether or not to perform Content Validation specific to groups in the given category. It is best to pick a category that all clients have a group from. | Boolean | True |
sshkeys |
passphrase |
Override the global Cfg passphrase with a specific passphrase for encrypting created SSH private keys. | String | None |
sshkeys |
category |
Override the global Cfg category with a specific category for created SSH keys. | String | None |
sslca |
passphrase |
Override the global Cfg passphrase with a specific passphrase for encrypting created SSL keys. | String | None |
sslca |
category |
Override the global Cfg category with a specific category for created SSL keys and certs. | String | None |
sslca_* |
config |
Path to the openssl config for the CA | String | None |
sslca_* |
passphrase |
Passphrase for the CA private key | String | None |
sslca_* |
chaincert |
Path to the SSL chaining certificate for verification | String | None |
sslca_* |
root_ca |
Whether or not <chaincert> is a root CA (as
opposed to an intermediate cert) |
Boolean | False |
See Bcfg2 Data Encryption for more details on encryption in Bcfg2 in general.