This manual is for version 2.2.8 of cfengine and was last updated on the 30 July 2008.
The purpose of the cfengine reference manual is to collect together and document the raw facts about the different components of cfengine. Once you have become proficient in the use of cfengine, you will no longer have need of the tutorial. The reference manual, on the other hand, changes with each version of cfengine. You will be able to use it online, or in printed form to find out the details you require to implement configurations in practice.
In order to install cfengine, you should first ensure that the following packages are installed.
The preferred method of installation is then
tar zxf cfengine-x.x.x.tar.gz
cd cfengine-x.x.x
./configure
make
make install
This results in binaries being installed in /usr/local/sbin. Since this is not necessarily a local file system on all hosts, users are encouraged to keep local copies of the binaries on each host, inside the cfengine trusted work directory.
In order to achieve the desired simplifications, it was decided to reserve a private work area for the cfengine tool-set. In cfengine 1.x, the administrator could choose the locations of configuration files, locks, and logging data independently. In cfengine 2.x, this diversity has been simplified to a single directory which defaults to /var/cfengine (similar to /var/cron):
/var/cfengine
/var/cfengine/bin
/var/cfengine/inputs
/var/cfengine/outputs
The installation location /usr/local/sbin is not necessarily a local file system, and cannot therefore be trusted to a) be present, and b) be authentic on an arbitrary system.
Similarly, a trusted cache of the input files must now be maintained
in the inputs subdirectory. When cfengine is invoked by the
scheduler, it reads only from this directory. It is up to the user to
keep this cache updated, on each host. This simplifies and
consolidates the cfengine resources in a single place. The environment
variable CFINPUTS still overrides this default location, as before,
but in its absence or when called from the scheduler, this becomes the
location of trusted files. A special configuration file update.conf is
parsed and run before the main configuration is parsed, which is
used to ensure that the currently caches policy is up-to-date. This has private
classes and variables.
If no value is set for CFINPUTS, then the default location
is the trusted cfengine directory
/var/cfengine/inputs.
The outputs directory is now a record of spooled run-reports. These are mailed to the administrator, as previously, or can be copied to another central location and viewed in an alternative browser..
A single class can be one of several things:
ultrix, sun4, etc.
This is referred to as a hard class.
Monday, Tuesday, Wednesday, ..).
Hr00, Hr01 ... Hr23).
Min00, Min17 ... Min45).
Min00_05, Min05_10 ... Min55_00)
Day1, Day2, ... Day31).
January, February, ... December).
Yr1997, Yr2004).
ipv4_192_0_0_1,
ipv4_192_0_0, ipv4_192_0, ipv4_192).
To see all of the classes define on a particular host, run
host# cfagent -p -v
as a privileged user. Note that some of the classes are set only
if a trusted link can be established with cfenvd, i.e. if both
are running with privilege, and the /var/cfengine/state/env_data
file is secure. More information about classes can be found in connection with
allclasses.
Filenames in Unix-like operating systems use for their directory separator the forward slash '/' character. All references to file locations must be absolute pathnames in cfengine, i.e. they must begin with a complete specification of which directory they are in. For example:
/etc/passwd
/usr/local/masterfiles/distfile
The only place where it makes sense to refer to a file without a complete directory specification is when searching through directories for different kinds of file, e.g.
tidy:
/home/user pattern=core age=0 recurse=inf
Here, one can write core without a path, because one is looking for any file of that name in a number of directories.
The Windows operating systems traditionally use a different filename convention. The following are all valid absolute file names under Windows:
c:\winnt
c:/winnt
/var/cfengine/inputs
//fileserver/share2/dir
The `drive' name “C:” in Windows refers to a partition or device. Unlike Unix, Windows does not integrate these seamlessly into a single file-tree. This is not a valid absolute filename:
\var\cfengine\inputs
Paths beginning with a backslash are assumed to be win32 paths. They must begin with a drive letter or double-slash server name.
It is possible to turn debugging output on or off on a running cfagent. This is useful for troubleshooting the cause of hangups, or for getting debugging output from a cfagent launched from cfexecd.
A running cfagent process that receives a SIGUSR1 will immediately begin to behave as if it had been invoked with the '-d2' option. A SIGUSR2 will cause a running cfagent to run as if the '-d2' option had not been invoked.
Note that this output is often quite verbose.
The very first thing you should do on every host is to establish a public-private key pair. To do this, you need to run the program
everyhost# cfkey
on every host. This program needs to produce random numbers, and needs a source of randomness. A good strategy is to install and run the cfenvd program for a week or two in advance of deploying cfengine 2, since cfenvd collects random events, which are an excellent source of entropy for random number generation. If you get the error message “PRNG not seeded”, it means that insufficient data were found in order to make a random key. In that case, run cfenvd for a few days more and try again.
The cfshow command was introduced in cfengine 2.1.11 in order to
provide a simple way to show some of the data stored by cfagent for
operational purposes.
everyhost# cfshow -a
everyhost# cfshow -l
everyhost# cfshow -c
everyhost# cfshow -s
everyhost# cfshow -p
everyhost# cfshow -f cfagent.conf -r linux.*
The command line options are
--active'--audit'--html and --xml options.
--checksum'--classes'--html'--locks'--last-seen' IP + 192.168.1.101 192.168.1.101 [Tue Jan 23 16:13] not seen for (6.42) hrs, Av 0.02 +/- 0.01 hrs
IP - 192.168.1.101 192.168.1.101 [Tue Jan 23 16:13] not seen for (6.42) hrs, Av 0.02 +/- 0.01 hrs
Lines marked with a + represent successful attempts made by cfagent on
the current host to connect to another host. Lines with a - are
connections attempted (but not necessarily succeeded) into cfservd from another
host's cfagent or cfrun.
--regex regex'--performance' (0.00 mins Tue Feb 13 19:05) Av 0.00 +/- 0.00 for Copy(localhost:/usr/local/sbin/cfagent > /var/cfengine/bin/cfagent)
(0.00 mins Tue Feb 13 19:05) Av 0.00 +/- 0.00 for Copy(localhost:/usr/local/sbin/cfenvd > /var/cfengine/bin/cfenvd)
(0.02 mins Tue Feb 13 19:05) Av 0.02 +/- 0.00 for Copy(localhost:/usr/local/sbin/cfexecd > /var/cfengine/bin/cfexecd)
(0.00 mins Tue Feb 13 19:05) Av 0.00 +/- 0.00 for Copy(localhost:/usr/local/sbin/cfservd > /var/cfengine/bin/cfservd)
(6.41 mins Tue Feb 13 18:50) Av 0.00 +/- 0.00 for Exec(/usr/bin/updatedb --prunepaths=/media)
(0.00 mins Tue Feb 13 19:05) Av 0.00 +/- 0.00 for Exec(/usr/sbin/ntpdate 128.39.74.16 > /dev/null)
--xml'Cfagent is the workhorse of cfengine. It interprets and computes the necessary strategies for implementing convergent maintenance. In order to carry out work efficiently, the agent groups similar actions together. The order of these actions is goverened by a list called the actionsequence.
In many cases, cfagent will be able to complete all its work in a single pass of the actionsequence. However, in complex configurations, it is hard to resolve all of the ordering dependencies automatically in a single pass. Cfagent keeps track both of all actions that have been performed and of those that might still need to be performed (given that some actions depend on the later outcomes of others). If there is a possibility that an action ordering dilemma might occur, it runs a second pass of the actionsequence to more quickly resolve the dependency (avoiding the wait for next scheduled run). No actions are performed twice however, since the agent checks off actions that have already been performed to avoid unnecessary duplication.
control:
classes::
domain = ( DNS-domain-name )
classes:
Class/Group definitions
import:
Files to import
# other items
|
Note that GNU long options are available with the syntax
--longoption. The long names are given in brackets.
--sysadm) Print only the name of the system administrator then quit.
--auto) Can be used to signify an automatic run of cfengine, as opposed
to a manual run. The distinction is not predetermined. Use of this option
currently causes cfengine to ignore locks. This option is reserved for future
development.
--force-net-copy) Normally cfengine detects attempts to copy
from a server via the network that will loop back to the localhost.
It then avoids using the network to make the copy. This option forces
cfengine to copy using the network. Yes, someone thinks this is useful!
--no-check-files) Do not check file systems for ownership / permissions etc.
--no-check-mounts) Check mount points for consistency. If this
option is specified then directories which lie in the “mount point”
area are checked to see whether there is anything mounted on them.
Normally this is off since not all machines use mounted file
systems in the same way. e.g. HPUX does not generally operate with
partitions, but nevertheless one might wish to mimick a partition-like
environment there, but it would be irritating to be informed that
nothing was mounted on the mount point.
--debug) Enable debugging output. Normally you will want to
send this to a file using the shell script command or a pipe.
-d1 shows only parsing output. -d2 shows only
runtime action output. -d0 shows both levels. Debugging ouput is
intended mainly for the author's convenience and is not a supported
feature. The details of this output may change at any time.
--define) Define a compound class symbol of the form
alpha.beta.gamma.
--no-edits) Suppress file editing.
--enforce-links) Globally force links to be created where plain
files or links already exist. Since this option
is a big hammer, you have to use it in interactive mode and
answer a yes/no query before cfengine will run like this.
--file) Parse filename after this switch. By default cfengine
looks for a file called cfengine.conf in the current directory.
--help) Help information. Display version banner and options
summary.
--no-hard-classes). Prevents cfengine from generating any built-in
class name information. Can be used for emulation purposes.
--no-ifconfig) Do not attempt to configure the local area
network interface.
--inform) Switches on the inform output level, whereby cfengine
reports everything it changes..
--no-copy) Do not copy/image any files.
--no-lock) Ignore locks when running.
--traverse-links) Normally cfengine does not follow symbolic
links when recursively parsing directories. This option will force it
to do so.
--delete-stale-links) Delete links which do not point to
existing files (except in user home directories, which are not touched).
--no-mount) Do not attempt to mount file systems or edit the
filesystem table.
--no-modules)
Ignore modules in actionsequence.
--recon,--dry-run,--just-print) No action. Only
print what has to be done without actually doing it.
--negate,--undefine) Cancel a set of classes,
or undefine (set value to false) a compound class of the form
alpha.beta.gamma.
--parse-only) Parse file and then stop. Used for checking the
syntax of a program. You do not have to be superuser to use this
option.
--no-processes) Do not execute the processes action.
--no-splay) Switch off host splaying (sleeping).
--quert) Query the values of the comma separated list of variable names.
--no-commands) Do not execute scripts or shell commands.
--silent) Silence run time warnings.
--no-tidy) Do not tidy file systems.
--use-env) Causes cfengine to generate an environment variable
`CFALLCLASSES' which can be read by child processes (scripts). This
variable contains a summary of all the currently defined classes at any
given time. This option causes some System V systems to generate a Bus
Error or segmentation fault. The same information is available from the
cfengine built-in variable $(allclasses) and can be passed as a
parameter to scripts.
When this variable grows too large for embedding one can also access a complete
list of current classes in /var/cfengine/state/allclasses.
--underscore-classes). When this option is set, cfengine adds
an underscore to the beginning of the hard system classes (like _sun4, _linux
etc. The longer compound classes are not underscored, since these are already
complex and would unlikely result in collisions.) This can be used to avoid naming conflicts if you are so
unjudicious as to name a host by the name of a hard class. Other classes
are not affected.
--verbose) Verbose mode. Prints detailed information about
actions and state.
--version) Print only the version string and then quit.
--no-preconf) Do not execute the cf.preconf net
configuration file.
--no-links) Do not execute the links section of a
program.
--no-warn,--quiet) Do not print warning
messages.
--schedule) Print the exec schedule for the LAN (used by cfexecd).
In version 2.0.4, an abbreviation for actionsequence exclusions was added:
$ cfagent --avoid resolve,copy
$ cfagent --just tidy --just shellcommands
Variables in cfengine 2 are defined in contexts. Variables in a given context refer to the different phases of execution of cfengine: global, update and main. In the "current" context, variables have the form
$(variable) ${variable}
and are expanded either on parsing or at execution. Variables that cannot be expanded remain as dollar strings. Variables belonging to a context that is not the current one may be referred to as
$(context.variable) or ${context.variable}
There is no difference between these forms as far as cfengine is concered. Some authors like to use the () form for cfengine variables, to distinguish them with shell variables in command strings. When using the () form in function arguments, they should be quoted to avoid parsing errors.
Consider the example:
$(global.env_time)
Some variables in cfengine are associative arrays (as made famous by Perl). Such arrays are referred to by square brackets:
$(array[key]) $(array[$(key)])
and so on. Note carefully that cfengine requires parentheses or braces around variable names. Unlike in the shell, they cannot be omitted.
A number of special functions can be used to set variables in cfengine.
You can import values from the execution of a shell command
by prefixing a command with the word exec. This method is deprecated
as of cfengine version 2; use the ExecResult function instead.
control:
# old method
listing = ( "exec /bin/ls -l" )
# new method
listing = ( ExecResult(/bin/ls -l) )
This sets the variable `listing' to the output of the command in the quotes. Some other built-in functions are
A(X,Y) control:
assoc_array = ( A(B,"is for bird") A(C,"is for cat") )
results in:
OBJECT: main
4569 : assoc_array[B]=is for bird
4630 : assoc_array[C]=is for cat
control:
binhost = ( A(linux,machine1) A(solaris,machine2) )
copy:
# Contact machine 1 for linux
# Contact machine 2 for solaris
/etc/source dest=/etc/receve server=$(binhost[$(class)])
ExecResult(command)ExecShellResult(command)RandomInt(a,b)ReadArray(filename,fileformat,separator,comment,Max number of bytes)autokeyReadArray tries to interpret the
file as a table of items separated with the separator character. Blank
lines and comments (to end of line) are ignored. Items are keyed
numerically starting from 1 to the maximum number in the file. The
newline $(n) is always considered to be a separator, no matter
what the current separator is.
textkeyReadArray tries to interpret the file as a list
of lines of the form:
key,value
ReadFile(filename,Max number of bytes)ReadTable(filename,fileformat,separator,comment,Max number of bytes)autokeyReadArray tries to interpret the
file as a table of items separated with the separator character. Blank
lines and comments (to end of line) are ignored. Items are keyed
numerically starting from 1 to the maximum number in the file. Any
lines that do not contain the correct number of separators cause the
function to fail without making any assignment.
textkeyReadArray tries to interpret the file as a list
of lines of the form:
key1,key2,value1
key3,key4,value2
This variable would then be references as $(table[key1][key2]).
ReadList(filename,fileformat,comment,Max number of bytes)linesReadList tries to interpret the file as a list of
items on separate lines. The value returned is a list formatted by the Split character.
hosts = ( ReadList(/var/cfengine/inputs/datafile,lines,#,1000) )
ReadTCP(host/IP,portnumber,send string,Max number of bytes)
one_host_only::
# USE WITH CAUTION !
probewww = ( ReadTCP(localhost,80,'GET index.html',1000) )
Or testing a network service:
control:
checkhost::
probesmtp = ( ReadTCP(localhost,25,"",1024) )
probewww = ( ReadTCP(project.iu.hio.no,80,"GET /viewcvs HTTP/1.0 ${n}${n}",1024) )
classes:
viewcvs_error = ( RegCmp(".*Python Traceback.*","${probewww}") )
alerts:
viewcvs_error::
"Received viewcvs error from web server"
SelectPartitionNeighbours(filename,comment,Policy,group size)
control:
allpeers = ( SelectPartitionNeighbours(/var/cfengine/inputs/cfrun.hosts,#,random,4) )
copy:
/data/file dest=/p2prepository/file server=$(allpeers)
SelectPartitionLeader(filename,comment,Policy,group size)
control:
leader = ( SelectPartitionLeader(/var/cfengine/inputs/cfrun.hosts,#,random,4) )
copy:
/data/file dest=/p2prepository/file server=$(leader)
control:
variable2 = ( RandomInt(0,23) )
variable3 = ( ExecResult(/bin/ls -a /opt) )
myexcerpt = ( ReadFile("/etc/services",220) )
listvar = ( ReadArray(/tmp/array,textkey,",","#",100) )
In the latter case, the file could look like this:
host$ more /tmp/array
one,String to tbe read
two,Nothing string
three,Everything comes in threes
and results in the definition of (verify with cfagent -p -d3):
OBJECT: main
960 : listvar[one]=String to tbe read
259 : listvar[two]=Nothing string
224 : listvar[three]=Everything comes in threes
Variables are referred to in either of two different ways, depending on
your taste. You can use the forms $(variable) or
${variable}. The variable in braces or parentheses can be the
name of any user defined macro, environment variable or one of the
following special built-in variables.
AllClassesarchbinservercfinputs_versionChecksumDatabaseChecksumUpdatesclasssun4, hpux). A constant.
datedomainEmailFromEmailMaxLinesinf, no maximum is enforced.
EmailTosysadm variable).
facultysite).
fqhosthostipaddressipv4[interface]
${global.ipv4[hme0]}
${global.ipv4[eth0]}
MaxCfenginesostype$(arch).
OutputPrefix OutputPrefix = ( "cfengine:$(host):")
RepCharsite$(faculty) and may be used interchangeably.
smtpserversplitsysadmtimezonecontrol.
UnderscoreClassesversionyearThese variables are kept special because they play a special role in setting up a system configuration. You are encouraged to use them to define fully generalized rules in your programs. Variables can be used to advantage in defining filenames, directory names and in passing arguments to shell commands. The judicious use of variables can reduce many definitions to a single one if you plan carefully.
|
NOTE: the above control variables are not case sensitive, unlike user macros, so you should not define your own macros with these names. |
The following variables are also reserved and may be used to produce troublesome special characters in strings.
crcolondblquote"
dollarlfnquote'.
spctabVariables can be used as iterators in some situations. Iteration over lists is currently rather limited in cfengine and is something to be improved on in a future version. When a variable is used as an iterator, a character is chosen to represent a list separator, as in the shell `IFS' variable. The default separator is the colon `:' character:
control:
listvar = ( one:two:three:four )
The action that contains a variable to be interpreted as a list appears as separate actions, one for each case:
shellcommand:
"/bin/echo $(listvar)"
is equivalent to
shellcommand:
"/bin/echo one"
"/bin/echo two"
"/bin/echo three"
"/bin/echo four"
If multiple iterators are used, these are handled as nested loops:
cfengine::/bin/echo one 1: one 1
cfengine::/bin/echo one 2: one 2
cfengine::/bin/echo one 3: one 3
cfengine::/bin/echo one 4: one 4
cfengine::/bin/echo two 1: two 1
cfengine::/bin/echo two 2: two 2
cfengine::/bin/echo two 3: two 3
cfengine::/bin/echo two 4: two 4
cfengine::/bin/echo three: three 1
cfengine::/bin/echo three: three 2
cfengine::/bin/echo three: three 3
cfengine::/bin/echo three: three 4
cfengine::/bin/echo four : four 1
cfengine::/bin/echo four : four 2
cfengine::/bin/echo four : four 3
cfengine::/bin/echo four : four 4
Where iterators are not allowed, the implied lists are treated as scalars:
alerts:
amnexus::
"do $(list1) $(list2)"
e.g.
cfengine:: do one:two:three:four 1:2:3:4
Iterative expansion is currently restricted to:
A cfengine action looks like this:
action-type:
compound-class::
declaration
A single class is an identifier that may consist of any alphanumeric character or the underscore, just like identifiers in ordinary programming languages. Classes that are derived from data like IP addresses or host names convert any other characters (like `.' or `-') into underscores. A single class can be one of several things:
ultrix, sun4, etc.
This is referred to as a hard class.
Monday, Tuesday, Wednesday, ..).
Hr00, Hr01 ... Hr23).
Min00, Min17 ... Min45).
Min00_05, Min05_10 ... Min55_00)
Q1, Q2, Q3, Q4)
Hr00_Q1, Hr23_Q4 etc.)
Day1 ... Day31).
January, February, ... December).
Yr1997, Yr2004).
ipv4_192_0_0_1,
ipv4_192_0_0, ipv4_192_0, ipv4_192).
A compound class is a sequence of simple classes connected by dots or `pipe' symbols (vertical bars). For example:
myclass.sun4.Monday::
sun4|ultrix|osf::
A compound class evaluates to `true' if all of the individual classes
are separately true, thus in the above example the actions which follow
compound_class:: are only carried out if the host concerned is in
myclass, is of type sun4 and the day is Monday!
In the second example, the host parsing the file must be either of
type sun4 or ultrix or osf.
In other words, compound classes support two operators: AND and OR,
written `.' and `|' respectively.
From cfengine version 2.1.1,
I bit the bullet and added `&' as a synonym for the AND operator.
Cfengine doesn't
care how many of these operators you use (since it skips over blank
class names), so you could write either
solaris|irix::
or
solaris||irix::
depending on your taste. On the other hand, the order in which cfengine evaluates AND and OR operations does matter, and the rule is that AND takes priority over OR, so that `.' binds classes together tightly and all AND operations are evaluated before ORing the final results together. This is the usual behaviour in programming languages. You can use round parentheses in cfengine classes to override these preferences.
Cfengine allows you to define switch on and off dummy classes so that
you can use them to select certain subsets of action. In particular,
note that by defining your own classes, using them to make compound
rules of this type, and then switching them on and off, you can also
switch on and off the corresponding actions in a controlled way. The
command line options -D and -N can be used for this
purpose. See also addclasses in the Reference manual.
A logical NOT operator has been added to allow you to exclude
certain specific hosts in a more flexible way. The logical NOT
operator is (as in C and C++) `!'. For instance, the
following example would allow all hosts except for myhost:
action:
!myhost::
command
and similarly, so allow all hosts in a user-defined group mygroup,
except for myhost, you would write
action:
mygroup.!myhost::
command
which reads `mygroup AND NOT myhost'. The NOT operator can also be combined with OR. For instance
class1|!class2
would select hosts which were either in class 1, or those which were not in class 2.
Finally, there is a number of reserved classes. The following are hard classes for various operating system architectures. They do not need to be defined because each host knows what operating system it is running. Thus the appropriate one of these will always be defined on each host. Similarly the day of the week is clearly not open to definition, unless you are running cfengine from outer space. The reserved classes are:
ultrix, sun4, sun3, hpux, hpux10, aix, solaris, osf, irix4, irix, irix64
sco, freebsd, netbsd, openbsd, bsd4_3, newsos, solarisx86, aos,
nextstep, bsdos, linux, debian, cray, unix_sv, GnU, NT
If these classes are not sufficient to distinguish the hosts on your network, cfengine provides more specific classes which contain the name and release of the operating system. To find out what these look like for your systems you can run cfengine in `parse-only-verbose' mode:
cfagent -p -v
and these will be displayed. For example, Solaris 2.4 systems
generate the additional classes sunos_5_4 and sunos_sun4m,
sunos_sun4m_5_4.
Cfengine uses both the unqualified and fully host names as classes. Some
sites and operating systems use fully qualified names for their
hosts. i.e. uname -n returns to full domain qualified
hostname. This spoils the class matching algorithms for cfengine, so
cfengine automatically truncates names which contain a dot `.' at the
first `.' it encounters. If your hostnames contain dots (which do not
refer to a domain name, then cfengine will be confused. The moral is:
don't have dots in your host names! NOTE: in order to ensure that
the fully qualified name of the host becomes a class you must define the
domain variable. The dots in this string will be replaced by underscores.
In summary, the operator ordering in cfengine classes is as follows:
Cfengine provides a number of built-in functions for evaluating classes,
based on file tests. Using these built-in functions is quicker than
calling the shell test function. The time functions place their
arguments in chronological order.
AccessedBefore(f1,f2)ChangedBefore(f1,f2)ClassMatch(regexp) classes:
userdef = ( ClassMatch(.*linux.*) )
FileExists(file)GroupExists(groupname|gid)HostRange(basename,start-stop)IsDefined(variable-id)IsDefined(var), and not IsDefined(${var}))
IsDir(f)IsLink(f)IsPlain(f)IsNewerThan(f1,f2)IPRange(address-range)PrepModule(module,arg1 arg2...)Regcmp(regexp,string or list separated string)ReturnsZero(command)ReturnsZeroShell(command)Strcmp(s1,s2)UserExists(username|uid)IsGreaterThan(s1,s2)IsLessThan(s1,s2)
control:
actionsequence = ( files )
a = ( 2.12 )
b = ( 2.11 )
classes:
lt = ( IsLessThan(${a},${b}) )
gt = ( IsGreaterThan(${a},${b}) )
alerts:
lt:: "$(a) LESS THAN $(b)"
gt:: "$(a) GREATER THAN $(b)"
For example:
classes:
access_to_dir = ( ReturnsZero(/bin/cd /mydir) )
compare = ( ChangedBefore(/etc/passwd_master,/etc/passwd) )
isplain = ( IsPlain(/tmp/import) )
inrange = ( IPRange(128.39.89.10-15) )
CIDR = ( IPRange(128.39.89.10/24) )
compute_nodes = ( HostRange(cpu-,01-32)
gotinit = ( PrepModule(startup2,"arg1 arg2") )
acl:
class::
{ acl-alias
action
}
|
Cfengine's ACL feature is a common interface for managing
filesystem access control lists (ACLs). An access control list is an
extended file permission. It allows you to open or close a file to a
named list of users (without having to create a group for those users);
similarly, it allows you to open or close a file for a list of groups.
Several operating systems have access control lists, but each typically
has a different syntax and different user interface to this facility,
making it very awkward to use. This part of a cfengine configuration
simplifies the management of ACLs by providing a more convenient user
interface for controlling them and—as far as possible—a common
syntax.
An ACL may, by its very nature, contain a lot of information. Normally
you would set ACLs in a files command, See files, or a
copy command, See copy. It would be too cumbersome to repeat
all of the information in every command in your configuration, so
cfengine simplifies this by first associating an alias together with a
complex list of ACL information. This alias is then used to represent
the whole bundle of ACL entries in a files or copy
command. The form of an ACL is similar to the form of an
editfiles command. It is a bundle of information concerning a
file's permissions.
{ acl-alias
method:overwrite/append
fstype:posix/solaris/dfs/afs/hpux/nt
acl_type:user/group:permissions
acl_type:user/group:permissions
...
}
The name acl-alias can be any identifier containing alphanumeric characters and underscores. This is what you will use to refer to the ACL entries in practice. The method entry tells cfengine how to interpret the entries: should a file's ACLs be overwritten or only adjusted? Since the filesystems from different developers all use different models for ACLs, you must also tell cfengine what kind of filesystem the file resides on. Currently only Solaris and DCE/DFS ACLs are implemented.
NOTE: if you set both file permissions and ACLs the file permissions override the ACLs.
An access control list is build of any number of individual access control entries (ACEs). The ACEs has the following general syntax:
acl_type:user/group:permissions
The user or group is sometimes referred to as a key.
For an explanation of ACL types and their use, refer to your local
manual page. However, note that for each type of filesystem, there are
certain entries which must exist in an ACL. If you are creating a new
ACL from scratch, you must specify these. For example, in Solaris ACLs
you must have entries for user, group and other.
Under DFS you need what DFS calls a user_obj, group_obj
and an other_obj, and in some cases mask_obj. In cfengine
syntax these are called user:*:, other:*: and
mask:*:, as described below. If you are appending to an existing
entry, you do not have to re-specify these unless you want to change
them.
Cfengine can overwrite (replace) or append to one or more ACL entries.
overwritemethod:overwrite is the default. This sets the ACL according to
the specified entries which follow. The existing ACL will be
overwritten completely.
appendmethod:append adds or modifies one or more specified ACL entries.
If an entry already exists for the specified type and user/group, the
specified permission bits will be added to the old permissions. If there
is no ACL entry for the given type and user/group, a new entry will be
appended.
The individual bits in an ACE may be either added subtracted or set equal to a specified mask. The `+' symbol means add, the `-' symbol subtract and `=' means set equal to. Here are some examples:
acltype:id/*:mask
user:mark:+rx,-w
user:ds:=r
user:jacobs:noaccess
user:forgiven:default
user:*:rw
group:*:r
other:*:r
The keyword noaccess means set all access bits to zero for that
user, i.e. remove all permissions. The keyword default means
remove the named user from the access crontrol list altogether, so that
the default permissions apply. A star/asterisk in the centre field
indicates that the user or group ID is implicitly specified as of the
owner of the file, or that no ID is applicable at all (as is the case for `other').
Under Solaris, the ACL type can be one of the following:
user
group
mask
other
default_user
default_group
default_mask
default_other
A user or group can be specified to the user, group, default_user and default_group types. Solaris ACL permissions are the normal UNIX permissions bits `rwx', where:
r - Grants read privileges.
w - Grants write privileges.
x - Grants execute privileges.
In DCE, the ACL type can be one of the following:
other
mask
any
unauthenticated
user
group
foreign_other
foreign_user
foreign_group
The user, group, foreign_user and foreign_group
types require that you specify a user or group. The DCE documentation
refers to types user_obj, group_obj and so on. In the
cfengine implementation, the ugly `_obj' suffix has been dropped to
make these more in keeping with the POSIX names. user_obj::, is
equivalent to user:*: is cfengine. The star/asterisk implies that
the ACL applies to the owner of the file object.
DFS permissions are comprised of the bits `crwxid', where:
c - Grants control privileges, to modify an acl.
r - Grants read privileges.
w - Grants write privileges.
x - Grants execute privileges.
i - Grants insert privileges.
d - Grants delete privileges.
See the DCE/DFS documentation for more information about this.
It is not possible to set ACLs in foreign cells currently using cfengine, but you can still have all of your ACL definitions in the same file. You must however arrange for the file to be executed on the server for the cell concerned. Note also that you must perform a DCE login (normally as user `cell_admin') in order to set ACLs on files which are not owned by the owner of the cfengine-process. This is because you must have a valid security ticket.
NT ACEs are written as follows:
acl_type:user/group:permissions:accesstype
The actual change consists of the extra field containing the access type. A star/asterisk in the field for user/group would normally imply that the ACL applies to the owner of the file object. However this functionality is as of today not yet implemented.
In NT, the ACL type can be one of the following:
user
group
Both types require that you specify the name of a user or a group.
NT permissions are comprised of the bits `rwxdpo', where:
r - Read privileges
w - Write privileges
x - Execute privileges
d - Delete privileges
p - Privileges to change the permissions on the file
o - Privileges to take ownership of the file
In addition to any combination of these bits, the word noaccess
or default can be used as explained in the previous section. NT
comes with some standard, predefined permissions. The standards are only
a predefined combination of the different bits specified above and are
provided with cfengine as well. You can use the standards by setting the
permission to read, change or all. The bit
implementation of each standard is as on NT:
read - rx
change - rwxd
all - rwxdpo
where the bits follow the earlier definition. The keywords mentioned above can only be used alone, and not in combination with `+', `-', `=' and/or other permission bits.
NT defines several different access types, of which only two are used in connection with the ACL type that is implemented in cfengine for NT. The access type can be one of the following:
allowed
denied
Intuitively, allowed access grants the specified permissions to
the user, whilst denied denies the user the specified
permissions. If no access type is specified, the default is
allowed. This enables cfengine's behaviour as on UNIX systems
without any changes to the configuration file. If the permissions
noaccess or default is used, the access type will be
irrelevant.
Here is an example of a configuration file for an NT ACL:
control:
actionsequence = ( files )
domain = ( iu.hioslo.no )
files:
$(HOME)/tt acl=acl_alias1 action=fixall
acl:
{ acl_alias1
method:overwrite
fstype:nt
user:gustafb:rwx:allowed
user:mark:all:allowed
user:toreo:read:allowed
user:torej:default:allowed
user:ds2:+rwx:allowed
group:dummy:all:denied
group:iu:read:allowed
group:root:all:allowed
group:guest:dpo:denied
}
Here is an example of a configuration file for one Solaris ACL and one DCE/DFS ACL:
control:
actionsequence = ( files )
domain = ( iu.hioslo.no )
files:
$(HOME)/tt acl=acl_alias1 action=fixall
/:/bigfile acl=acl_alias2 action=fixall
acl:
{ acl_alias1
method:overwrite
fstype:posix
user:*:rwx
user:mark:=rwx
user:sowille:=rx
user:toreo:=rx
user:torej:default
user:ds2:+rwx
group:*:rx
group:iu:r
group:root:x
mask:*:rx
other:*:rx
default_user:*:=rw
default_user:mark:+rwx
default_user:ds:=rwx
default_group::=r
default_group:iu:+r
default_mask::w
default_other::rwx
}
{ acl_alias2
method:overwrite
fstype:dfs
user:*:rwxcid
group:*:rxd
other:*:wxir
mask:*:rxw
user:/.../iu.hioslo.no/cell_admin:rc
group:/.../iu.hioslo.no/acct-admin:rwxcid
user:/.../iu.hioslo.no/root:rx
}
Alerts are normally just messages that are printed when classes become activated
in order to alert the system administrator to some condition that has arisen.
Alerts can also be special functions, like ShowState() that generate
system output.
Alerts cannot belong to the class any, that would generate a message from
every host. In a huge network this could result in vast amounts of Email. This behaviour
can be forced, however, by creating an alias for the class `any' that is defined on
the affected hosts.
alerts:
class::
quoted message
ifelapsed=time
audit=true/false
ShowState(parameter)
SysLog(priority,message)
SetState(name,ttl,policy)
UnSetState(name)
FriendStatus(hours)
PrintFile(filename,lines)
alerts:
myclass::
"Reminder: say hello every hour" ifelapsed=60
nfsd_in_high_dev2::
"High NFS server access rate 2dev at $(host) value $(value_nfsd_in) av $(average_nfsd_in) pm $(stddev_nfsd_in)"
ShowState(incoming.nfs)
# ROOT PROCS
anomaly_hosts.RootProcs_high_dev2::
"RootProc anomaly high 2 dev on $(host) value $(value_rootprocs) av $(average_rootprocs) pm $(stddev_rootprocs)"
ShowState(rootprocs)
The ShowState() function reports on state gathered by the cfenvd
daemon.
ShowState(incoming.tcpsyn)
ShowState(outgoing.smtp)
ShowState(incoming.www)
ShowState(outgoing.www)
ShowState(procs)
ShowState(rootprocs)
ShowState(otherprocs)
ShowState(users)
To limit the frequency of alerts, you can set locking times:
# ROOT PROCS
anomaly_hosts.RootProcs_high_dev2::
"RootProc anomaly high 2 dev on $(host) value $(value_rootprocs) av $(average_rootprocs) pm $(stddev_rootprocs)"
ShowState(rootprocs) ifelapsed=10 expireafter=20
Alerts can also be channeled directly to syslog, to avoid extraneous console messages or email.
SysLog(LOG_ERR,"Test syslog message")
One application for alerts is to pass signals from one cfengine to another by persistent, shared memory. For example, suppose a short-lived anomaly event triggers a class that relates to a security alert. The event class might be too short-lived to be followed up by cfagent in full. One could thus set a long term class that would trigger up several follow-up checks. A persistent class could also be used to exclude an operation for an interval of time.
Persistent class memory can be added through a system alert functions to give timer behaviour. For example, consider setting a class that acts like a non-resettable timer. It is defined for exactly 10 minutes before expiring.
SetState("preserved_class",10,Preserve)
Or to set a class that acts as a resettable timer. It is defined for 60 minutes unless the SetState call is called again to extend its lifetime.
SetState(non_preserved_class,60,Reset)
Existing persistent classes can be deleted with:
UnsetState(myclass)
The FriendStatus function is available from version 2.1.4 and
displays a message if hosts that normally have a cfengine protocol
connection with the current host have not connected for more than than
specified number of hours. If the number of hours is set to zero,
cfengine uses a machine-learned expectation value for the time and uses
this to report. The friend status of a host is thus the expectation that
there is a problem with a remote peer. Expected contact rates of more than
the variable LastSeenExpireAfter are ignored as spurious, See lastseenexpireafter.
The PrintFile function can be used to display short excerpts from text files.
The arguments are the filename and a maximum number of lines to be printed.
The binservers declaration need only be used if you are using
cfengine's model for mounting NFS filesystems. This declaration informs
hosts of which other hosts on the network possess filesystems containing
software (binary files) which client hosts should mount. This includes
resources like programs in /usr/local and so on. A host may have
several binary servers, since there may be several machines to which
disks are physically attached. In most cases, on a well organized
network, there will be only one architecture server per UNIX
platform type, for instance a SunOS server, an ULTRIX server and so on.
Binary servers are defined as follows:
binservers:
physics.sun4:: sunserver sunserver2
physics.linux:: linuxserver
The meaning of this declaration is the following. All hosts of type
sun4 which are members of the group physics should mount
any binaries declared in the mountables resource list which
belong to hosts sunserver or sunserver2. Similarly all
linux machines should mount binary filesystems in the mountables
list from linuxserver.
Cfengine knows the difference between binaries and home directories in
the mountables list, because home directories match the pattern
given by homepattern. See homepattern. See homeservers.
Note that every host is a binary server for itself, so that the first
binary server (and that with highest priority) is always the current
host. This ensures that local filesystems are always used in preference
to NFS mounted filesystems. This is only relevant in connection with
the variable $(binserver).
This information is used to configure the network interface for each host.
Every local area network has a convention for determining which internet
address is used for broadcast requests. Normally this is an address of
the form aaa.bbb.ccc.255 or aaa.bbb.ccc.0. The difference
between these two forms is whether all of the bits in the last number
are ones or zeroes respectively. You must find out which convention is
used at your establishment and tell cfengine using a declaration of the
form:
broadcast:
any::
ones # or zeros, or zeroes
In most cases you can use the generic class any, since all of the
hosts on the same subnet have to use the same convention. If your
configuration file encompasses several different subnets with different
conventions then you will need to use a more specific.
Cfengine computes the actual value of the broadcast address using the value specified above and the netmask See netmask.
The fundamental piece of any cfengine script or configuration file is the control section. If you omit this part of a cfengine script, it will not do anything! The control section is used to define certain variables, set default values and define the order in which the various actions you have defined will be carried out. Because cfengine is a declarative or descriptive language, the order in which actions appear in the file does not necessarily reflect the order in which they are executed. The syntax of declarations here is:
control:
classes::
variable = ( list or value function(args) )
|
The control section is a sequence of declarations which looks something like the following example:
control:
site = ( univ )
domain = ( univ.edu )
sysadm = ( admin@computing.univ.edu )
netmask = ( 255.255.252.0 )
timezone = ( EDT )
nfstype = ( nfs )
childlibpath = ( /usr/local:/mylibs )
sensiblesize = ( 1000 )
sensiblecount = ( 2 )
editfilesize = ( 4000 )
actionsequence =
(
links.some
mountall
links.others
files
)
myvariable = ( something )
mymacro = ( somethingelse )
myrandom = ( RandomInt(3,6) )
myexcerpt = ( ReadFile("/etc/services",220))
Parentheses are required when making a declaring information in cfengine. Note that a limited number of built-in functions exists:
ExecResult(command) Executes the named shell command and
inserts the output into the variable. Note that, when this is used in
cfengine built-in list variables, any spaces are interpreted as list
separators. In other lists, normal rules for iteration apply.
RandomInt(a,b)
Is substituted for a random number between (a,b).
ReadFile(filename,Max number of bytes)
A maximum number of bytes is read from the named file and placed in a variable.
The meaning of each of these lines is described below.
The AbortClasses list is a list of class identifiers that will
result in the abortion of the current cfagent instanitation with an error message
containing the name of the offending class.
AbortClasses = ( emergency nologin_exists )
This mechanism allows one to make controlled exceptions at the agent level. For example
control:
actionsequence = ( shellcommands )
AbortClasses = ( danger_will_robinson )
shellcommands:
"shellcom 1"
"shellcom 2" define=ok elsedefine=danger_will_robinson
The access list is a list of users who are to be allowed to
execute a cfengine program. If the list does not exist then all users
are allowed to run a program.
access = ( user1 user2 ... )
The list may consist of either numerical user identifiers or valid usernames from the password database. For example:
access = ( mark aurora 22 456 )
would restrict a script to users mark, aurora and user id 22 and 456.
The action sequence determines the order in which collective actions are carried out. Here is an example containing the full list of possibilities:
actionsequence =
(
mountall # mount filesystems in fstab
mountinfo # scan mounted filesystems
checktimezone # check timezone
netconfig # check net interface config
resolve # check resolver setup
unmount # unmount any filesystems
packages # install/upgrade/remove packages
shellcommands # execute shell commands
editfiles # edit files
addmounts # add new filesystems to system
directories # make any directories
links # check and maintain links (single and child)
mailcheck # check mailserver
mountall # (again)
required # check required filesystems
tidy # tidy files
disable # disable files
files # check file permissions
copy # make a copy/image of a master file
processes # signal / check processes
module:name # execute a user-defined module
)
Here is a more complete description of the meaning of these keywords.
addmountsmountinfo, so it should normally only be
called after mountinfo. If the filesystem already appears
to be in the filesystem table, a warning is issued.
checktimezonedirectoriesdirectories
section of the program. It builds new directories.
disabledisable
section of the program.
editfileseditfiles
section of the program.
filesfiles
section of the program.
linkslinks
section of the program.
mailcheckmailserver section of the cfengine program. If the current host
is the same as the mailserver (the host which has the physical spool
directory disk) nothing is done. Otherwise the filesystem table is
edited so as to include the mail directory.
module module:mytests
"module:mytests arg1 arg2 .."
declares a user defined module which can potentially set the classes
class1 etc. Classes returned by the module must be declared so
that cfengine knows to pay attention to rules which use these classes
when parsing; this is done using AddInstallable. If
arguments are passed to the module, the whole string must be quoted like
a shellcommand. See Writing plugin modules. Whether or not these
classes become set or not depends on the behaviour of your module. The
classes continue to apply for all actions which occur after the module's
execution. The module must be owned by the user executing cfengine or
root (for security reasons), it must be named
module:module-name and must lie in a special directory,
See moduledirectory.
mountalladdmounts and
mailcheck to be actually mounted. This should probably be called
both before mountinfo and after addmounts etc. A short
timeout is placed on this operation to avoid hanging RPC connections
when parsing NFS mounted file systems.
mountinfonetconfigrequiredrequired
section of the program. It checks for the absence of
important NFS resources.
resolvepackagespackages section
of the program. This will query the system's package database
for the specified packages, at the specified versions, set
classes based on whether or not those packages exist, and
optionally install, upgrade or remove those packages using a
pre-defined package manager command.
shellcommandsshellcommands
section of the program.
tidytidy
section of the program.
unmountunmount
section of the program. The filesystem table is edited
so as to remove the unwanted filesystems and the unmount
operation is executed.
processesprocesses section
of the program.
Under normal circumstances this coarse ordering is enough to suit most purposes. In some cases you might want to, say, only perform half the link operations before mounting filesystems and then, say, perform the remainder. You can do this (and similar things) by using the idea of defining and undefining classes. See Defining classes.
The syntax
actionsequence =
(
links.firstpass.include
...
links.secondpass
)
means that cfengine first executes links with the classes
firstpass and include defined. Later it executes
links with secondpass defined. You can use this method of
adding classes to distinguish more finely the flow of control in
programs.
A note about style: if you define and undefine lots of classes to do
what you want to do, you might stop and ask yourself if your
groups are defined as well as they should be. See groups.
Programming in cfengine is about doing a lot for only a little
writing. If you find yourself writing a lot, you are probably not going
about things in the right way.
AddClasses = ( list of identifiers )
The AddClasses directive is used to define a list of class
attributes for the current host. Normally only the hard classes defined
by the system are `true' for a given host. It is convenient though to
be able to define classes of your own to label certain actions, mainly
so that they can later be excluded so as to cut short or filter out
certain actions. This can be done in two ways. See actionsequence.
To define a list of classes for the current session, you write:
AddClasses = ( exclude shortversion )
This is equivalent to (though more permanent than) defining
classes on the command line with the -D option.
You can now use these to qualify actions. For example
any.exclude::
...
Under normal circumstances exclude is always true — because you
have defined it to be so, but you can undefine it in two ways so
as to prevent the action from being carried out. One way is to undefine
a class on the command line when you invoke cfengine:
| host# cfengine -N exclude |
or
| host# cfengine -N exclude.shortversion host# cfengine -N a.b.c.d |
These commands run cfengine with the named classes undefined. That means that actions labelled with these classes are excluded during that run.
Another way to restrict classes is to add a list of classes to be undefined in the actionsequence. See next section.
AddInstallable = ( list of identifiers )
Some actions in your cfengine program will be labelled by classes which
only become defined at run time using a define= option. Cfengine
is not always able to see these classes until it meets them and tries to
save space by only loading actions for classes which is believes will
become defined at some point in the program. This can lead to some
actions being missed if the action is parsed before the place where the
class gets switched on, since cfengine is a one-pass interpreter,. To
help cfengine determine classes which might become defined during
a run, you can declare them in this list. It does no harm to declare
classes here anyway.
Here is an example where you need to declare a class because of the ordering
of the actions.
control:
AddInstallable = ( myclass )
files:
myclass::
/tmp/test mode=644 action=fixall
copy:
/tmp/foo dest=/tmp/test define=myclass
If we remove the declaration, then when cfengine meets the files command, it skips it because it knows nothing about the class `myclass'—when the copy command follows, it is too late. Remember that imported files are always parsed after the main program so definitions made in imported files always come later than things in the main program.
Normally cfagent warns about redefinitions of variables during parsing. This is presumed to be a mistake. To avoid this behaviour, add the name of the variable to this list, and the warning disappears.
control:
actionsequence = ( copy )
AllowRedefinitionOf = ( cfrep )
cfrep = ( bla )
cfrep = ( blo )
If this variable is set to true then cfengine conducts extensive auditing of its actions to a database in the work directory. When rules are applied, their locations and policy version are recorded also so that it is possible to see exactly which rule was applied and when. It is assumed that the version is recorded as below:
control:
cfinputs_version = ( 1.2.1 )
Auditing = true
This variable acts as the global default behaviour and may be
overriden locally by audit=true/false attributes, where applicable.
control:
hup_syslogd::
autodefine = ( /etc/syslog.c* )
Referring to the class that prefixes the command, autodefine is
a list of file patterns that will define the said class, if a named file is copied
in any statement. This helps to avoid having to write a large number
of file-specific copy: lines with define=class configured. In the example
above, the class hup_syslogd would be defined if /etc/syslog.conf
is copied at any time.
BinaryPaddingChar = ( \0 )
This specifies the type of character used to pad strings of unequal
length in editfiles during binary editing. The default value is
the space character, since this is normally used to edit filenames
or text messages within program code.
If this is set to a specific IP address of an IP configured interface, cfagent will use that address for outgoing connections. On Multi-homed hosts this allows one to restrict the traffic to a known interface. An interface must be configured with an IP address in order to be bound.