diff --git a/documentation/content/en/articles/rc-scripting/_index.adoc b/documentation/content/en/articles/rc-scripting/_index.adoc index cdfa656e53..6511be73a4 100644 --- a/documentation/content/en/articles/rc-scripting/_index.adoc +++ b/documentation/content/en/articles/rc-scripting/_index.adoc @@ -1,846 +1,921 @@ --- title: Practical rc.d scripting in BSD authors: - author: Yar Tikhiy email: yar@FreeBSD.org copyright: 2005-2006, 2012 The FreeBSD Project description: A guide to writing new rc.d scripts and understanding those already written trademarks: ["freebsd", "netbsd", "general"] tags: ["rc.d", "scripting", "guide", "tutorial", "FreeBSD"] --- = Practical rc.d scripting in BSD :doctype: article :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :source-highlighter: rouge :experimental: :images-path: articles/rc-scripting/ ifdef::env-beastie[] ifdef::backend-html5[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] :imagesdir: ../../../images/{images-path} endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [.abstract-title] Abstract Beginners may find it difficult to relate the facts from the formal documentation on the BSD [.filename]#rc.d# framework with the practical tasks of [.filename]#rc.d# scripting. In this article, we consider a few typical cases of increasing complexity, show [.filename]#rc.d# features suited for each case, and discuss how they work. Such an examination should provide reference points for further study of the design and efficient application of [.filename]#rc.d#. ''' toc::[] [[rcng-intro]] == Introduction The historical BSD had a monolithic startup script, [.filename]#/etc/rc#. It was invoked by man:init[8] at system boot time and performed all userland tasks required for multi-user operation: checking and mounting file systems, setting up the network, starting daemons, and so on. The precise list of tasks was not the same in every system; admins needed to customize it. With few exceptions, [.filename]#/etc/rc# had to be modified, and true hackers liked it. The real problem with the monolithic approach was that it provided no control over the individual components started from [.filename]#/etc/rc#. For instance, [.filename]#/etc/rc# could not restart a single daemon. The system admin had to find the daemon process by hand, kill it, wait until it actually exited, then browse through [.filename]#/etc/rc# for the flags, and finally type the full command line to start the daemon again. The task would become even more difficult and prone to errors if the service to restart consisted of more than one daemon or demanded additional actions. In a few words, the single script failed to fulfil what scripts are for: to make the system admin's life easier. Later there was an attempt to split out some parts of [.filename]#/etc/rc# for the sake of starting the most important subsystems separately. The notorious example was [.filename]#/etc/netstart# to bring up networking. It did allow for accessing the network from single-user mode, but it did not integrate well into the automatic startup process because parts of its code needed to interleave with actions essentially unrelated to networking. That was why [.filename]#/etc/netstart# mutated into [.filename]#/etc/rc.network#. The latter was no longer an ordinary script; it comprised of large, tangled man:sh[1] functions called from [.filename]#/etc/rc# at different stages of system startup. However, as the startup tasks grew diverse and sophisticated, the "quasi-modular" approach became even more of a drag than the monolithic [.filename]#/etc/rc# had been. Without a clean and well-designed framework, the startup scripts had to bend over backwards to satisfy the needs of rapidly developing BSD-based operating systems. It became obvious at last that more steps are necessary on the way to a fine-grained and extensible [.filename]#rc# system. Thus BSD [.filename]#rc.d# was born. Its acknowledged fathers were Luke Mewburn and the NetBSD community. Later it was imported into FreeBSD. Its name refers to the location of system scripts for individual services, which is in [.filename]#/etc/rc.d#. Soon we will learn about more components of the [.filename]#rc.d# system and see how the individual scripts are invoked. The basic ideas behind BSD [.filename]#rc.d# are _fine modularity_ and __code reuse__. _Fine modularity_ means that each basic "service" such as a system daemon or primitive startup task gets its own man:sh[1] script able to start the service, stop it, reload it, check its status. A particular action is chosen by the command-line argument to the script. The [.filename]#/etc/rc# script still drives system startup, but now it merely invokes the smaller scripts one by one with the `start` argument. It is easy to perform shutdown tasks as well by running the same set of scripts with the `stop` argument, which is done by [.filename]#/etc/rc.shutdown#. Note how closely this follows the Unix way of having a set of small specialized tools, each fulfilling its task as well as possible. _Code reuse_ means that common operations are implemented as man:sh[1] functions and collected in [.filename]#/etc/rc.subr#. Now a typical script can be just a few lines' worth of man:sh[1] code. Finally, an important part of the [.filename]#rc.d# framework is man:rcorder[8], which helps [.filename]#/etc/rc# to run the small scripts orderly with respect to dependencies between them. It can help [.filename]#/etc/rc.shutdown#, too, because the proper order for the shutdown sequence is opposite to that of startup. The BSD [.filename]#rc.d# design is described in <>, and the [.filename]#rc.d# components are documented in great detail in <>. However, it might not appear obvious to an [.filename]#rc.d# newbie how to tie the numerous bits and pieces together to create a well-styled script for a particular task. Therefore this article will try a different approach to describe [.filename]#rc.d#. It will show which features should be used in a number of typical cases, and why. Note that this is not a how-to document because our aim is not at giving ready-made recipes, but at showing a few easy entrances into the [.filename]#rc.d# realm. Neither is this article a replacement for the relevant manual pages. Do not hesitate to refer to them for more formal and complete documentation while reading this article. There are prerequisites to understanding this article. First of all, you should be familiar with the man:sh[1] scripting language to master [.filename]#rc.d#. In addition, you should know how the system performs userland startup and shutdown tasks, which is described in man:rc[8]. This article focuses on the FreeBSD branch of [.filename]#rc.d#. Nevertheless, it may be useful to NetBSD developers, too, because the two branches of BSD [.filename]#rc.d# not only share the same design but also stay similar in their aspects visible to script authors. [[rcng-task]] == Outlining the task A little consideration before starting `$EDITOR` will not hurt. To write a well-tempered [.filename]#rc.d# script for a system service, we should be able to answer the following questions first: * Is the service mandatory or optional? * Will the script serve a single program, e.g., a daemon, or perform more complex actions? * Which other services will our service depend on, and vice versa? From the examples that follow we will see why it is important to know the answers to these questions. [[rcng-dummy]] == A dummy script The following script just emits a message each time the system boots up: [.programlisting] .... #!/bin/sh <.> . /etc/rc.subr <.> name="dummy" <.> start_cmd="${name}_start" <.> stop_cmd=":" <.> dummy_start() <.> { echo "Nothing started." } load_rc_config $name <.> run_rc_command "$1" <.> .... Things to note are: ➊ An interpreted script should begin with the magic "shebang" line. That line specifies the interpreter program for the script. Due to the shebang line, the script can be invoked exactly like a binary program provided that it has the execute bit set. (See man:chmod[1].) For example, a system admin can run our script manually, from the command line: [source,shell] .... # /etc/rc.d/dummy start .... [NOTE] ==== To be properly managed by the [.filename]#rc.d# framework, its scripts need to be written in the man:sh[1] language. If you have a service or port that uses a binary control utility or a startup routine written in another language, install that element in [.filename]#/usr/sbin# (for the system) or [.filename]#/usr/local/sbin# (for ports) and call it from a man:sh[1] script in the appropriate [.filename]#rc.d# directory. ==== [TIP] ==== If you would like to learn the details of why [.filename]#rc.d# scripts must be written in the man:sh[1] language, see how [.filename]#/etc/rc# invokes them by means of `run_rc_script`, then study the implementation of `run_rc_script` in [.filename]#/etc/rc.subr#. ==== ➋ In [.filename]#/etc/rc.subr#, a number of man:sh[1] functions are defined for an [.filename]#rc.d# script to use. The functions are documented in man:rc.subr[8]. While it is theoretically possible to write an [.filename]#rc.d# script without ever using man:rc.subr[8], its functions prove extremely handy and make the job an order of magnitude easier. So it is no surprise that everybody resorts to man:rc.subr[8] in [.filename]#rc.d# scripts. We are not going to be an exception. An [.filename]#rc.d# script must "source"[.filename]#/etc/rc.subr# (include it using "`.`") _before_ it calls man:rc.subr[8] functions so that man:sh[1] has an opportunity to learn the functions. The preferred style is to source [.filename]#/etc/rc.subr# first of all. [NOTE] ==== Some useful functions related to networking are provided by another include file, [.filename]#/etc/network.subr#. ==== ➌ [[name-var]]The mandatory variable `name` specifies the name of our script. It is required by man:rc.subr[8]. That is, each [.filename]#rc.d# script _must_ set `name` before it calls man:rc.subr[8] functions. Now it is the right time to choose a unique name for our script once and for all. We will use it in a number of places while developing the script. For a start, let us give the same name to the script file, too. [NOTE] ==== The current style of [.filename]#rc.d# scripting is to enclose values assigned to variables in double quotes. Keep in mind that it is just a style issue that may not always be applicable. You can safely omit quotes from around simple words without man:sh[1] metacharacters in them, while in certain cases you will need single quotes to prevent any interpretation of the value by man:sh[1]. A programmer should be able to tell the language syntax from style conventions and use both of them wisely. ==== ➍ The main idea behind man:rc.subr[8] is that an [.filename]#rc.d# script provides handlers, or methods, for man:rc.subr[8] to invoke. In particular, `start`, `stop`, and other arguments to an [.filename]#rc.d# script are handled this way. A method is a man:sh[1] expression stored in a variable named `argument_cmd`, where _argument_ corresponds to what can be specified on the script's command line. We will see later how man:rc.subr[8] provides default methods for the standard arguments. [NOTE] ==== To make the code in [.filename]#rc.d# more uniform, it is common to use `${name}` wherever appropriate. Thus a number of lines can be just copied from one script to another. ==== ➎ We should keep in mind that man:rc.subr[8] provides default methods for the standard arguments. Consequently, we must override a standard method with a no-op man:sh[1] expression if we want it to do nothing. ➏ The body of a sophisticated method can be implemented as a function. It is a good idea to make the function name meaningful. [IMPORTANT] ==== It is strongly recommended to add the prefix `${name}` to the names of all functions defined in our script so they never clash with the functions from man:rc.subr[8] or another common include file. ==== ➐ This call to man:rc.subr[8] loads man:rc.conf[5] variables. Our script makes no use of them yet, but it still is recommended to load man:rc.conf[5] because there can be man:rc.conf[5] variables controlling man:rc.subr[8] itself. ➑ Usually this is the last command in an [.filename]#rc.d# script. It invokes the man:rc.subr[8] machinery to perform the requested action using the variables and methods our script has provided. [[rcng-confdummy]] == A configurable dummy script Now let us add some controls to our dummy script. As you may know, [.filename]#rc.d# scripts are controlled with man:rc.conf[5]. Fortunately, man:rc.subr[8] hides all the complications from us. The following script uses man:rc.conf[5] via man:rc.subr[8] to see whether it is enabled in the first place, and to fetch a message to show at boot time. These two tasks in fact are independent. On the one hand, an [.filename]#rc.d# script can just support enabling and disabling its service. On the other hand, a mandatory [.filename]#rc.d# script can have configuration variables. We will do both things in the same script though: [.programlisting] .... #!/bin/sh . /etc/rc.subr name=dummy rcvar=dummy_enable <.> start_cmd="${name}_start" stop_cmd=":" load_rc_config $name <.> : ${dummy_enable:=no} <.> : ${dummy_msg="Nothing started."} <.> dummy_start() { echo "$dummy_msg" <.> } run_rc_command "$1" .... What changed in this example? ➊ The variable `rcvar` specifies the name of the ON/OFF knob variable. ➋ Now `load_rc_config` is invoked earlier in the script, before any man:rc.conf[5] variables are accessed. [NOTE] ==== While examining [.filename]#rc.d# scripts, keep in mind that man:sh[1] defers the evaluation of expressions in a function until the latter is called. Therefore it is not an error to invoke `load_rc_config` as late as just before `run_rc_command` and still access man:rc.conf[5] variables from the method functions exported to `run_rc_command`. This is because the method functions are to be called by `run_rc_command`, which is invoked _after_ `load_rc_config`. ==== ➌ A warning will be emitted by `run_rc_command` if `rcvar` itself is set, but the indicated knob variable is unset. If your [.filename]#rc.d# script is for the base system, you should add a default setting for the knob to [.filename]#/etc/defaults/rc.conf# and document it in man:rc.conf[5]. Otherwise it is your script that should provide a default setting for the knob. The canonical approach to the latter case is shown in the example. [NOTE] ==== You can make man:rc.subr[8] act as though the knob is set to `ON`, irrespective of its current setting, by prefixing the argument to the script with `one` or `force`, as in `onestart` or `forcestop`. Keep in mind though that `force` has other dangerous effects we will touch upon below, while `one` just overrides the ON/OFF knob. E.g., assume that `dummy_enable` is `OFF`. The following command will run the `start` method in spite of the setting: [source,shell] .... # /etc/rc.d/dummy onestart .... ==== ➍ Now the message to be shown at boot time is no longer hard-coded in the script. It is specified by an man:rc.conf[5] variable named `dummy_msg`. This is a trivial example of how man:rc.conf[5] variables can control an [.filename]#rc.d# script. [IMPORTANT] ==== The names of all man:rc.conf[5] variables used exclusively by our script _must_ have the same prefix: `${name}_`. For example: `dummy_mode`, `dummy_state_file`, and so on. ==== [NOTE] ==== While it is possible to use a shorter name internally, e.g., just `msg`, adding the unique prefix `${name}_` to all global names introduced by our script will save us from possible collisions with the man:rc.subr[8] namespace. As a rule, [.filename]#rc.d# scripts of the base system need not provide defaults for their man:rc.conf[5] variables because the defaults should be set in [.filename]#/etc/defaults/rc.conf# instead. On the other hand, [.filename]#rc.d# scripts for ports should provide the defaults as shown in the example. ==== ➎ Here we use `dummy_msg` to actually control our script, i.e., to emit a variable message. Use of a shell function is overkill here, since it only runs a single command; an equally valid alternative is: [.programlisting] .... start_cmd="echo \"$dummy_msg\"" .... [[rcng-daemon]] == Startup and shutdown of a simple daemon We said earlier that man:rc.subr[8] could provide default methods. Obviously, such defaults cannot be too general. They are suited for the common case of starting and shutting down a simple daemon program. Let us assume now that we need to write an [.filename]#rc.d# script for such a daemon called `mumbled`. Here it is: [.programlisting] .... #!/bin/sh . /etc/rc.subr name=mumbled rcvar=mumbled_enable command="/usr/sbin/${name}" <.> load_rc_config $name run_rc_command "$1" .... Pleasingly simple, isn't it? Let us examine our little script. The only new thing to note is as follows: ➊ The `command` variable is meaningful to man:rc.subr[8]. If it is set, man:rc.subr[8] will act according to the scenario of serving a conventional daemon. In particular, the default methods will be provided for such arguments: `start`, `stop`, `restart`, `poll`, and `status`. The daemon will be started by running `$command` with command-line flags specified by `$mumbled_flags`. Thus all the input data for the default `start` method are available in the variables set by our script. Unlike `start`, other methods may require additional information about the process started. For instance, `stop` must know the PID of the process to terminate it. In the present case, man:rc.subr[8] will scan through the list of all processes, looking for a process with its name equal to `procname`. The latter is another variable of meaning to man:rc.subr[8], and its value defaults to that of `command`. In other words, when we set `command`, `procname` is effectively set to the same value. This enables our script to kill the daemon and to check if it is running in the first place. [NOTE] ==== Some programs are in fact executable scripts. The system runs such a script by starting its interpreter and passing the name of the script to it as a command-line argument. This is reflected in the list of processes, which can confuse man:rc.subr[8]. You should additionally set `command_interpreter` to let man:rc.subr[8] know the actual name of the process if `$command` is a script. For each [.filename]#rc.d# script, there is an optional man:rc.conf[5] variable that takes precedence over `command`. Its name is constructed as follows: `${name}_program`, where `name` is the mandatory variable we discussed <>. E.g., in this case it will be `mumbled_program`. It is man:rc.subr[8] that arranges `${name}_program` to override `command`. Of course, man:sh[1] will permit you to set `${name}_program` from man:rc.conf[5] or the script itself even if `command` is unset. In that case, the special properties of `${name}_program` are lost, and it becomes an ordinary variable your script can use for its own purposes. However, the sole use of `${name}_program` is discouraged because using it together with `command` became an idiom of [.filename]#rc.d# scripting. ==== For more detailed information on default methods, refer to man:rc.subr[8]. [[rcng-daemon-adv]] == Startup and shutdown of an advanced daemon Let us add some meat onto the bones of the previous script and make it more complex and featureful. The default methods can do a good job for us, but we may need some of their aspects tweaked. Now we will learn how to tune the default methods to our needs. [.programlisting] .... #!/bin/sh . /etc/rc.subr name=mumbled rcvar=mumbled_enable command="/usr/sbin/${name}" command_args="mock arguments > /dev/null 2>&1" <.> pidfile="/var/run/${name}.pid" <.> required_files="/etc/${name}.conf /usr/share/misc/${name}.rules" <.> sig_reload="USR1" <.> start_precmd="${name}_prestart" <.> stop_postcmd="echo Bye-bye" <.> extra_commands="reload plugh xyzzy" <.> plugh_cmd="mumbled_plugh" <.> xyzzy_cmd="echo 'Nothing happens.'" mumbled_prestart() { if checkyesno mumbled_smart; then <.> rc_flags="-o smart ${rc_flags}" <.> fi case "$mumbled_mode" in foo) rc_flags="-frotz ${rc_flags}" ;; bar) rc_flags="-baz ${rc_flags}" ;; *) warn "Invalid value for mumbled_mode" <.> return 1 <.> ;; esac run_rc_command xyzzy <.> return 0 } mumbled_plugh() <.> { echo 'A hollow voice says "plugh".' } load_rc_config $name run_rc_command "$1" .... ➊ Additional arguments to `$command` can be passed in `command_args`. They will be added to the command line after `$mumbled_flags`. Since the final command line is passed to `eval` for its actual execution, input and output redirections can be specified in `command_args`. [NOTE] ==== _Never_ include dashed options, like `-X` or `--foo`, in `command_args`. The contents of `command_args` will appear at the end of the final command line, hence they are likely to follow arguments present in `${name}_flags`; but most commands will not recognize dashed options after ordinary arguments. A better way of passing additional options to `$command` is to add them to the beginning of `${name}_flags`. Another way is to modify `rc_flags` <>. ==== ➋ A good-mannered daemon should create a _pidfile_ so that its process can be found more easily and reliably. The variable `pidfile`, if set, tells man:rc.subr[8] where it can find the pidfile for its default methods to use. [NOTE] ==== In fact, man:rc.subr[8] will also use the pidfile to see if the daemon is already running before starting it. This check can be skipped by using the `faststart` argument. ==== ➌ If the daemon cannot run unless certain files exist, just list them in `required_files`, and man:rc.subr[8] will check that those files do exist before starting the daemon. There also are `required_dirs` and `required_vars` for directories and environment variables, respectively. They all are described in detail in man:rc.subr[8]. [NOTE] ==== The default method from man:rc.subr[8] can be forced to skip the prerequisite checks by using `forcestart` as the argument to the script. ==== ➍ We can customize signals to send to the daemon in case they differ from the well-known ones. In particular, `sig_reload` specifies the signal that makes the daemon reload its configuration; it is SIGHUP by default. Another signal is sent to stop the daemon process; the default is SIGTERM, but this can be changed by setting `sig_stop` appropriately. [NOTE] ==== The signal names should be specified to man:rc.subr[8] without the `SIG` prefix, as it is shown in the example. The FreeBSD version of man:kill[1] can recognize the `SIG` prefix, but the versions from other OS types may not. ==== ➎➏ Performing additional tasks before or after the default methods is easy. For each command-argument supported by our script, we can define `argument_precmd` and `argument_postcmd`. These man:sh[1] commands are invoked before and after the respective method, as it is evident from their names. [NOTE] ==== Overriding a default method with a custom `argument_cmd` still does not prevent us from making use of `argument_precmd` or `argument_postcmd` if we need to. In particular, the former is good for checking custom, sophisticated conditions that should be met before performing the command itself. Using `argument_precmd` along with `argument_cmd` lets us logically separate the checks from the action. Do not forget that you can cram any valid man:sh[1] expressions into the methods, pre-, and post-commands you define. Just invoking a function that makes the real job is a good style in most cases, but never let style limit your understanding of what is going on behind the curtain. ==== ➐ If we would like to implement custom arguments, which can also be thought of as _commands_ to our script, we need to list them in `extra_commands` and provide methods to handle them. [NOTE] ==== The `reload` command is special. On the one hand, it has a preset method in man:rc.subr[8]. On the other hand, `reload` is not offered by default. The reason is that not all daemons use the same reload mechanism and some have nothing to reload at all. So we need to ask explicitly that the builtin functionality be provided. We can do so via `extra_commands`. What do we get from the default method for `reload`? Quite often daemons reload their configuration upon reception of a signal - typically, SIGHUP. Therefore man:rc.subr[8] attempts to reload the daemon by sending a signal to it. The signal is preset to SIGHUP but can be customized via `sig_reload` if necessary. ==== ➑⓮ Our script supports two non-standard commands, `plugh` and `xyzzy`. We saw them listed in `extra_commands`, and now it is time to provide methods for them. The method for `xyzzy` is just inlined while that for `plugh` is implemented as the `mumbled_plugh` function. Non-standard commands are not invoked during startup or shutdown. Usually they are for the system admin's convenience. They can also be used from other subsystems, e.g., man:devd[8] if specified in man:devd.conf[5]. The full list of available commands can be found in the usage line printed by man:rc.subr[8] when the script is invoked without arguments. For example, here is the usage line from the script under study: [source,shell] .... # /etc/rc.d/mumbled Usage: /etc/rc.d/mumbled [fast|force|one](start|stop|restart|rcvar|reload|plugh|xyzzy|status|poll) .... ⓭ A script can invoke its own standard or non-standard commands if needed. This may look similar to calling functions, but we know that commands and shell functions are not always the same thing. For instance, `xyzzy` is not implemented as a function here. In addition, there can be a pre-command and post-command, which should be invoked orderly. So the proper way for a script to run its own command is by means of man:rc.subr[8], as shown in the example. ➒ A handy function named `checkyesno` is provided by man:rc.subr[8]. It takes a variable name as its argument and returns a zero exit code if and only if the variable is set to `YES`, or `TRUE`, or `ON`, or `1`, case insensitive; a non-zero exit code is returned otherwise. In the latter case, the function tests the variable for being set to `NO`, `FALSE`, `OFF`, or `0`, case insensitive; it prints a warning message if the variable contains anything else, i.e., junk. Keep in mind that for man:sh[1] a zero exit code means true and a non-zero exit code means false. [IMPORTANT] ==== The `checkyesno` function takes a __variable name__. Do not pass the expanded _value_ of a variable to it; it will not work as expected. The following is the correct usage of `checkyesno`: [.programlisting] .... if checkyesno mumbled_enable; then foo fi .... On the contrary, calling `checkyesno` as shown below will not work - at least not as expected: [.programlisting] .... if checkyesno "${mumbled_enable}"; then foo fi .... ==== ➓ [[rc-flags]]We can affect the flags to be passed to `$command` by modifying `rc_flags` in `$start_precmd`. ⓫ In certain cases we may need to emit an important message that should go to `syslog` as well. This can be done easily with the following man:rc.subr[8] functions: `debug`, `info`, `warn`, and `err`. The latter function then exits the script with the code specified. ⓬ The exit codes from methods and their pre-commands are not just ignored by default. If `argument_precmd` returns a non-zero exit code, the main method will not be performed. In turn, `argument_postcmd` will not be invoked unless the main method returns a zero exit code. [NOTE] ==== However, man:rc.subr[8] can be instructed from the command line to ignore those exit codes and invoke all commands anyway by prefixing an argument with `force`, as in `forcestart`. ==== [[rcng-hookup]] == Connecting a script to the rc.d framework After a script has been written, it needs to be integrated into [.filename]#rc.d#. The crucial step is to install the script in [.filename]#/etc/rc.d# (for the base system) or [.filename]#/usr/local/etc/rc.d# (for ports). Both [.filename]#bsd.prog.mk# and [.filename]#bsd.port.mk# provide convenient hooks for that, and usually you do not have to worry about the proper ownership and mode. System scripts should be installed from [.filename]#src/libexec/rc/rc.d# through the [.filename]#Makefile# found there. Port scripts can be installed using `USE_RC_SUBR` as described extref:{porters-handbook}[in the Porter's Handbook, rc-scripts]. However, we should consider beforehand the place of our script in the system startup sequence. The service handled by our script is likely to depend on other services. For instance, a network daemon cannot function without the network interfaces and routing up and running. Even if a service seems to demand nothing, it can hardly start before the basic filesystems have been checked and mounted. We mentioned man:rcorder[8] already. Now it is time to have a close look at it. In a nutshell, man:rcorder[8] takes a set of files, examines their contents, and prints a dependency-ordered list of files from the set to `stdout`. The point is to keep dependency information _inside_ the files so that each file can speak for itself only. A file can specify the following information: * the names of the "conditions" (which means services to us) it __provides__; * the names of the "conditions" it __requires__; * the names of the "conditions" this file should run __before__; * additional _keywords_ that can be used to select a subset from the whole set of files (man:rcorder[8] can be instructed via options to include or omit the files having particular keywords listed.) It is no surprise that man:rcorder[8] can handle only text files with a syntax close to that of man:sh[1]. That is, special lines understood by man:rcorder[8] look like man:sh[1] comments. The syntax of such special lines is rather rigid to simplify their processing. See man:rcorder[8] for details. Besides using man:rcorder[8] special lines, a script can insist on its dependency upon another service by just starting it forcibly. This can be needed when the other service is optional and will not start by itself because the system admin has disabled it mistakenly in man:rc.conf[5]. With this general knowledge in mind, let us consider the simple daemon script enhanced with dependency stuff: [.programlisting] .... #!/bin/sh # PROVIDE: mumbled oldmumble <.> # REQUIRE: DAEMON cleanvar frotz <.> # BEFORE: LOGIN <.> # KEYWORD: nojail shutdown <.> . /etc/rc.subr name=mumbled rcvar=mumbled_enable command="/usr/sbin/${name}" start_precmd="${name}_prestart" mumbled_prestart() { if ! checkyesno frotz_enable && \ ! /etc/rc.d/frotz forcestatus 1>/dev/null 2>&1; then force_depend frotz || return 1 <.> fi return 0 } load_rc_config $name run_rc_command "$1" .... As before, detailed analysis follows: ➊ That line declares the names of "conditions" our script provides. Now other scripts can record a dependency on our script by those names. [NOTE] ==== Usually a script specifies a single condition provided. However, nothing prevents us from listing several conditions there, e.g., for compatibility reasons. In any case, the name of the main, or the only, `PROVIDE:` condition should be the same as `${name}`. ==== ➋➌ So our script indicates which "conditions" provided by other scripts it depends on. According to the lines, our script asks man:rcorder[8] to put it after the script(s) providing [.filename]#DAEMON# and [.filename]#cleanvar#, but before that providing [.filename]#LOGIN#. [NOTE] ==== The `BEFORE:` line should not be abused to work around an incomplete dependency list in the other script. The appropriate case for using `BEFORE:` is when the other script does not care about ours, but our script can do its task better if run before the other one. A typical real-life example is the network interfaces vs. the firewall: While the interfaces do not depend on the firewall in doing their job, the system security will benefit from the firewall being ready before there is any network traffic. Besides conditions corresponding to a single service each, there are meta-conditions and their "placeholder" scripts used to ensure that certain groups of operations are performed before others. These are denoted by [.filename]#UPPERCASE# names. Their list and purposes can be found in man:rc[8]. Keep in mind that putting a service name in the `REQUIRE:` line does not guarantee that the service will actually be running by the time our script starts. The required service may fail to start or just be disabled in man:rc.conf[5]. Obviously, man:rcorder[8] cannot track such details, and man:rc[8] will not do that either. Consequently, the application started by our script should be able to cope with any required services being unavailable. In certain cases, we can help it as discussed <> ==== [[keywords]]➍ As we remember from the above text, man:rcorder[8] keywords can be used to select or leave out some scripts. Namely any man:rcorder[8] consumer can specify through `-k` and `-s` options which keywords are on the "keep list" and "skip list", respectively. From all the files to be dependency sorted, man:rcorder[8] will pick only those having a keyword from the keep list (unless empty) and not having a keyword from the skip list. In FreeBSD, man:rcorder[8] is used by [.filename]#/etc/rc# and [.filename]#/etc/rc.shutdown#. These two scripts define the standard list of FreeBSD [.filename]#rc.d# keywords and their meanings as follows: nojail:: The service is not for man:jail[8] environment. The automatic startup and shutdown procedures will ignore the script if inside a jail. nostart:: The service is to be started manually or not started at all. The automatic startup procedure will ignore the script. In conjunction with the [.filename]#shutdown# keyword, this can be used to write scripts that do something only at system shutdown. shutdown:: This keyword is to be listed __explicitly__ if the service needs to be stopped before system shutdown. [NOTE] ==== When the system is going to shut down, [.filename]#/etc/rc.shutdown# runs. It assumes that most [.filename]#rc.d# scripts have nothing to do at that time. Therefore [.filename]#/etc/rc.shutdown# selectively invokes [.filename]#rc.d# scripts with the [.filename]#shutdown# keyword, effectively ignoring the rest of the scripts. For even faster shutdown, [.filename]#/etc/rc.shutdown# passes the [.filename]#faststop# command to the scripts it runs so that they skip preliminary checks, e.g., the pidfile check. As dependent services should be stopped before their prerequisites, [.filename]#/etc/rc.shutdown# runs the scripts in reverse dependency order. If writing a real [.filename]#rc.d# script, you should consider whether it is relevant at system shutdown time. E.g., if your script does its work in response to the [.filename]#start# command only, then you need not to include this keyword. However, if your script manages a service, it is probably a good idea to stop it before the system proceeds to the final stage of its shutdown sequence described in man:halt[8]. In particular, a service should be stopped explicitly if it needs considerable time or special actions to shut down cleanly. A typical example of such a service is a database engine. ==== [[forcedep]]➎ To begin with, `force_depend` should be used with much care. It is generally better to revise the hierarchy of configuration variables for your [.filename]#rc.d# scripts if they are interdependent. If you still cannot do without `force_depend`, the example offers an idiom of how to invoke it conditionally. In the example, our `mumbled` daemon requires that another one, `frotz`, be started in advance. However, `frotz` is optional, too; and man:rcorder[8] knows nothing about such details. Fortunately, our script has access to all man:rc.conf[5] variables. If `frotz_enable` is true, we hope for the best and rely on [.filename]#rc.d# to have started `frotz`. Otherwise we forcibly check the status of `frotz`. Finally, we enforce our dependency on `frotz` if it is found to be not running. A warning message will be emitted by `force_depend` because it should be invoked only if a misconfiguration has been detected. [[rcng-args]] == Giving more flexibility to an rc.d script When invoked during startup or shutdown, an [.filename]#rc.d# script is supposed to act on the entire subsystem it is responsible for. E.g., [.filename]#/etc/rc.d/netif# should start or stop all network interfaces described by man:rc.conf[5]. Either task can be uniquely indicated by a single command argument such as `start` or `stop`. Between startup and shutdown, [.filename]#rc.d# scripts help the admin to control the running system, and it is when the need for more flexibility and precision arises. For instance, the admin may want to add the settings of a new network interface to man:rc.conf[5] and then to start it without interfering with the operation of the existing interfaces. Next time the admin may need to shut down a single network interface. In the spirit of the command line, the respective [.filename]#rc.d# script calls for an extra argument, the interface name. Fortunately, man:rc.subr[8] allows for passing any number of arguments to script's methods (within the system limits). Due to that, the changes in the script itself can be minimal. How can man:rc.subr[8] gain access to the extra command-line arguments. Should it just grab them directly? Not by any means. Firstly, an man:sh[1] function has no access to the positional parameters of its caller, but man:rc.subr[8] is just a sack of such functions. Secondly, the good manner of [.filename]#rc.d# dictates that it is for the main script to decide which arguments are to be passed to its methods. So the approach adopted by man:rc.subr[8] is as follows: `run_rc_command` passes on all its arguments but the first one to the respective method verbatim. The first, omitted, argument is the name of the method itself: `start`, `stop`, etc. It will be shifted out by `run_rc_command`, so what is `$2` in the original command line will be presented as `$1` to the method, and so on. To illustrate this opportunity, let us modify the primitive dummy script so that its messages depend on the additional arguments supplied. Here we go: [.programlisting] .... #!/bin/sh . /etc/rc.subr name="dummy" start_cmd="${name}_start" stop_cmd=":" kiss_cmd="${name}_kiss" extra_commands="kiss" dummy_start() { if [ $# -gt 0 ]; then <.> echo "Greeting message: $*" else echo "Nothing started." fi } dummy_kiss() { echo -n "A ghost gives you a kiss" if [ $# -gt 0 ]; then <.> echo -n " and whispers: $*" fi case "$*" in *[.!?]) echo ;; *) echo . ;; esac } load_rc_config $name run_rc_command "$@" <.> .... What essential changes can we notice in the script? ➊ All arguments you type after `start` can end up as positional parameters to the respective method. We can use them in any way according to our task, skills, and fancy. In the current example, we just pass all of them to man:echo[1] as one string in the next line - note `$*` within the double quotes. Here is how the script can be invoked now: [source,shell] .... # /etc/rc.d/dummy start Nothing started. # /etc/rc.d/dummy start Hello world! Greeting message: Hello world! .... ➋ The same applies to any method our script provides, not only to a standard one. We have added a custom method named `kiss`, and it can take advantage of the extra arguments not less than `start` does. E.g.: [source,shell] .... # /etc/rc.d/dummy kiss A ghost gives you a kiss. # /etc/rc.d/dummy kiss Once I was Etaoin Shrdlu... A ghost gives you a kiss and whispers: Once I was Etaoin Shrdlu... .... ➌ If we want just to pass all extra arguments to any method, we can merely substitute `"$@"` for `"$1"` in the last line of our script, where we invoke `run_rc_command`. [IMPORTANT] ==== An man:sh[1] programmer ought to understand the subtle difference between `$*` and `$@` as the ways to designate all positional parameters. For its in-depth discussion, refer to a good handbook on man:sh[1] scripting. _Do not_ use the expressions until you fully understand them because their misuse will result in buggy and insecure scripts. ==== [NOTE] ==== Currently `run_rc_command` may have a bug that prevents it from keeping the original boundaries between arguments. That is, arguments with embedded whitespace may not be processed correctly. The bug stems from `$*` misuse. ==== +[[rcng-service-jails]] +== Making a script ready for Service Jails + +Scripts which start a long running service are suitable for service jails, and should come with a suitable service jail configuration. + +Some examples of scripts which are not suitable to run in a service jail: + +* any script which in the start command only changes a runtime setting for programs or the kernel, +* or tries to mount something, +* or finds and deletes files + +Scripts not suitable to run in a service jail need to prevent the use within service jails. + +A script with a long running service which needs to do something listed above before the start or after the stop, can either be split-up into two scripts with dependencies, or use the precommand and postcommand parts of the script to perform this action. + +By default, only the start and stop parts of a script are run within a service jail, the rest is run outside the jail. +As such any setting used in the start/stop parts of the script can not be set from e.g. a precommand. + +To make a script ready for use with extref:../../books/handbook/jails/#service-jails[Service Jails], only one more config line needs to be inserted: + +[.programlisting] +.... +#!/bin/sh + +. /etc/rc.subr + +name="dummy" +start_cmd="${name}_start" +stop_cmd=":" + +: ${dummy_svcj_options:=""} <.> + +dummy_start() +{ + echo "Nothing started." +} + +load_rc_config $name +run_rc_command "$1" +.... + +➊ If it makes sense that the script runs in a jail, it must have an overridable service jails configuration. +If it does not need network access or access to any other resource which is restricted in jails, an empty config like displayed is enough. + +Strictly speaking an empty config is not needed, but it explicitly describes that the script is service jails ready, and that it does not need additional jail permissions. +As such it is highly recommended to add such an empty config in such a case. +The most common option to use is "net_basic", which enables the use of the hosts IPv4 and IPv6 addresses. +All possible options are explained in man:rc.conf[5]. + +If a setting for the start/stop depends on variables from the rc-framework (e.g. set inside man:rc.conf[5]), this needs to be handled by ``load_rc_config`` and ``run_rc_command`` instead of inside a precommand. + +If for some reason a script can not be run within a service jail, e.g. because it is not possible to run or it does not make sense to run it in a jail, use the following: + +[.programlisting] +.... +#!/bin/sh + +. /etc/rc.subr + +name="dummy" +start_cmd="${name}_start" +stop_cmd=":" + +dummy_start() +{ + echo "Nothing started." +} + +load_rc_config $name +dummy_svcj="NO" # does not make sense to run in a svcj <.> +run_rc_command "$1" +.... + +➊ The disabling needs to happen after the ``load_rc_config`` call, else a man:rc.conf[5] setting may override it. + [[rcng-furthur]] == Further reading [[lukem]]http://www.mewburn.net/luke/papers/rc.d.pdf[The original article by Luke Mewburn] offers a general overview of [.filename]#rc.d# and detailed rationale for its design decisions. It provides insight on the whole [.filename]#rc.d# framework and its place in a modern BSD operating system. [[manpages]]The manual pages man:rc[8], man:rc.subr[8], and man:rcorder[8] document the [.filename]#rc.d# components in great detail. You cannot fully use the [.filename]#rc.d# power without studying the manual pages and referring to them while writing your own scripts. The major source of working, real-life examples is [.filename]#/etc/rc.d# in a live system. Its contents are easy and pleasant to read because most rough corners are hidden deep in man:rc.subr[8]. Keep in mind though that the [.filename]#/etc/rc.d# scripts were not written by angels, so they might suffer from bugs and suboptimal design decisions. Now you can improve them! diff --git a/documentation/content/en/books/handbook/config/_index.adoc b/documentation/content/en/books/handbook/config/_index.adoc index 2e77ef70bf..ee25617ad0 100644 --- a/documentation/content/en/books/handbook/config/_index.adoc +++ b/documentation/content/en/books/handbook/config/_index.adoc @@ -1,1586 +1,1588 @@ --- title: Chapter 14. Configuration, Services, Logging and Power Management part: Part III. System Administration prev: books/handbook/partiii next: books/handbook/boot description: This chapter explains much of the FreeBSD configuration files, how to enable or disable a service, how to configure the logging system and the power management area. tags: ["configuration", "services", "cron", "periodic", "logging", "configuration files", "sysctl", "swap", "power management"] showBookMenu: true weight: 18 path: "/books/handbook/config/" --- [[config-tuning]] = Configuration, Services, Logging and Power Management :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :sectnumoffset: 14 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/config/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[config-synopsis]] == Synopsis One of the important aspects of FreeBSD is proper system configuration. This chapter explains much of the FreeBSD configuration process, including some of the parameters which can be set to tune a FreeBSD system. Before reading this chapter, you should: * Understand UNIX(R) and FreeBSD basics (crossref:basics[basics,FreeBSD Basics]). After reading this chapter, you will know: * How to use the various configuration files in [.filename]#/etc#. * The basics of [.filename]#rc.conf# configuration and [.filename]#/usr/local/etc/rc.d# startup scripts. * How to tune FreeBSD using man:sysctl[8] variables. * How to configure the power management in FreeBSD. [[configtuning-configfiles]] == Configuration Files FreeBSD maintains a clear separation between the base system and third party applications and therefore this affects where the configuration files of these applications are located. FreeBSD base system configuration is located at the [.filename]#/etc# directory, and the [.filename]#/usr/local/etc# directory contains all the configuration files of the applications installed on the system through the ports collection and packages. The kernel state configuration is located in [.filename]#/etc/sysctl.conf#. In the section <>, the operation of man:sysctl[8] will be explained in more detail. For more information about the FreeBSD file system structure refer to man:hier[7]. As a general rule, configuration files do not use a standard on what syntax they must follow. Although it is true that the `#` character is normally used to comment a line and that each line has a configuration variable. [NOTE] ==== Some applications like man:pkg[8] are starting to use the link:https://github.com/vstakhov/libucl[Universal Configuration Language (UCL)]. ==== === The [.filename]#/etc# directory The [.filename]#/etc# directory contains all of the FreeBSD base system configuration files that are responsible for configuring FreeBSD. [CAUTION] ==== *Extreme* caution must be taken when modifying files in the [.filename]#/etc# directory; misconfiguration could make FreeBSD unbootable or malfunction. ==== [.informaltable] [cols="1,1", frame="none"] |=== |[.filename]#/etc# |System configuration files and scripts. |[.filename]#/etc/defaults# |Default system configuration files, see man:rc[8] for more information. |[.filename]#/etc/fstab# |man:fstab[5] contains descriptive information about the various file systems. |[.filename]#/etc/mail# |Extra man:sendmail[8] configuration and other MTA configuration files. |[.filename]#/etc/mtree# |mtree configuration files, see man: mtree[8] for more information. |[.filename]#/etc/pam.d# |Configuration files for the Pluggable Authentication Modules (PAM) library. |[.filename]#/etc/periodic# |Scripts that are run daily, weekly, and monthly, via man:cron[8], see man:periodic[8] for more information. |[.filename]#/etc/rc.d# |System and daemon startup/control scripts, see man:rc[8] for more information. |[.filename]#/etc/rc.conf# |Contains descriptive information about the local host name, configuration details for any potential network interfaces and which services should be started up at system initial boot time. More information in <> |[.filename]#/etc/security# |OpenBSM audit configuration files, see man:audit[8] for more information. |[.filename]#/etc/ppp# |ppp configuration files, see man:ppp[8] for more information. |[.filename]#/etc/ssh# |OpenSSH configuration files, see man:ssh[1] for more information. |[.filename]#/etc/ssl# |OpenSSL configuration files. |[.filename]#/etc/sysctl.conf# |Contains settings for the kernel. More information in <> |=== [[configtuning-sysctl]] === The sysctl utility The man:sysctl[8] utility is used to make changes to a running FreeBSD system. The man:sysctl[8] utility retrieves kernel state and allows processes with appropriate privilege to set kernel state. The state to be retrieved or set is described using a "Management Information Base" ("MIB") style name, described as a dotted set of components. .Management Information Base [.informaltable] [cols="1,1", frame="none"] |=== |sysctl |"Magic" numbers |kern |Kernel functions and features |vm |virtual memory |vfs |Filesystem |net |Network |debug |Debugging parameters |hw |Hardware |machdep |Machine dependent |user |Userland |p1003_1b |POSIX 1003.1B |=== At its core, man:sysctl[8] serves two functions: to read and to modify system settings. To view all readable variables: [source,shell] .... % sysctl -a .... The output should be similar to the following: [.programlisting] .... kern.ostype: FreeBSD ... vm.swap_enabled: 1 vm.overcommit: 0 vm.domain.0.pidctrl.kdd: 8 vm.domain.0.pidctrl.kid: 4 vm.domain.0.pidctrl.kpd: 3 ... vfs.zfs.sync_pass_rewrite: 2 vfs.zfs.sync_pass_dont_compress: 8 vfs.zfs.sync_pass_deferred_free: 2 .... To read a particular variable, specify its name: [source,shell] .... % sysctl kern.maxproc .... The output should be similar to the following: [.programlisting] .... kern.maxproc: 1044 .... The Management Information Base (MIB) is hierarchical and hence, specifying a prefix prints all the nodes hanging from it: [source,shell] .... % sysctl net .... The output should be similar to the following: [.programlisting] .... net.local.stream.recvspace: 8192 net.local.stream.sendspace: 8192 net.local.dgram.recvspace: 16384 net.local.dgram.maxdgram: 2048 net.local.seqpacket.recvspace: 8192 net.local.seqpacket.maxseqpacket: 8192 net.local.sockcount: 60 net.local.taskcount: 25 net.local.recycled: 0 net.local.deferred: 0 net.local.inflight: 0 net.inet.ip.portrange.randomtime: 1 net.inet.ip.portrange.randomcps: 9999 [...] .... To set a particular variable, use the _variable_=_value_ syntax: [source,shell] .... # sysctl kern.maxfiles=5000 .... The output should be similar to the following: [.programlisting] .... kern.maxfiles: 2088 -> 5000 .... [NOTE] ==== To keep the configuration after a reboot it is necessary to add these variables to the [.filename]#/etc/sysctl.conf# file as explained below. ==== [[configtuning-sysctlconf]] === The [.filename]#/etc/sysctl.conf# file The configuration file for man:sysctl[8], [.filename]#/etc/sysctl.conf#, looks much like [.filename]#/etc/rc.conf#. Values are set using a `variable=value` syntax. [NOTE] ==== The specified values are set after the system goes into multi-user mode. Not all variables are settable in this mode. ==== For example, to turn off logging of fatal signal exits and prevent users from seeing processes started by other users, the following tunables can be set in [.filename]#/etc/sysctl.conf#: [.programlisting] .... # Do not log fatal signal exits (e.g., sig 11) kern.logsigexit=0 # Prevent users from seeing information about processes that # are being run under another UID. security.bsd.see_other_uids=0 .... To obtain more information about what function a particular sysctl has, the following command can be executed: [source,shell] .... % sysctl -d kern.dfldsiz .... The output should be similar to the following: [.programlisting] .... kern.dfldsiz: Initial data size limit .... [[configtuning-core-configuration]] === Managing System-Specific Configuration The principal location for system configuration information is [.filename]#/etc/rc.conf#. This file contains a wide range of configuration information and it is read at system startup to configure the system. It provides the configuration information for the [.filename]#rc*# files. The entries in [.filename]#/etc/rc.conf# override the default settings in [.filename]#/etc/defaults/rc.conf#. [TIP] ==== The file [.filename]#/etc/defaults/rc.conf# containing the default settings should not be edited. Instead, all system-specific changes should be made to [.filename]#/etc/rc.conf#. ==== A number of strategies may be applied in clustered applications to separate site-wide configuration from system-specific configuration in order to reduce administration overhead. The recommended approach is to place system-specific configuration into [.filename]#/etc/rc.conf.local#. For example, these entries in [.filename]#/etc/rc.conf# apply to all systems: [.programlisting] .... sshd_enable="YES" keyrate="fast" defaultrouter="10.1.1.254" .... Whereas these entries in [.filename]#/etc/rc.conf.local# apply to this system only: [.programlisting] .... hostname="node1.example.org" ifconfig_fxp0="inet 10.1.1.1/8" .... Distribute [.filename]#/etc/rc.conf# to every system using an application such as rsync or puppet, while [.filename]#/etc/rc.conf.local# remains unique. Upgrading the system will not overwrite [.filename]#/etc/rc.conf#, so system configuration information will not be lost. [TIP] ==== Both [.filename]#/etc/rc.conf# and [.filename]#/etc/rc.conf.local# are parsed by man:sh[1]. This allows system operators to create complex configuration scenarios. Refer to man:rc.conf[5] for further information on this topic. ==== [[configtuning-rcd]] == Managing Services in FreeBSD FreeBSD uses the man:rc[8] system of startup scripts during system initialization and for managing services. The scripts listed in [.filename]#/etc/rc.d# provide basic services which can be controlled with the `start`, `stop`, and `restart` options to man:service[8]. A basic script may look similar to the following: [.programlisting] .... #!/bin/sh # # PROVIDE: utility # REQUIRE: DAEMON # KEYWORD: shutdown . /etc/rc.subr name=utility rcvar=utility_enable command="/usr/local/sbin/utility" load_rc_config $name # # DO NOT CHANGE THESE DEFAULT VALUES HERE # SET THEM IN THE /etc/rc.conf FILE # utility_enable=${utility_enable-"NO"} pidfile=${utility_pidfile-"/var/run/utility.pid"} run_rc_command "$1" .... Refer to extref:{rc-scripting}[this article] for instructions on how to create custom man:rc[8] scripts. [[configtuning-starting-services]] === Starting Services Many users install third party software on FreeBSD from the Ports Collection and require the installed services to be started upon system initialization. Services, such as package:security/openssh-portable[] or package:www/nginx[] are just two of the many software packages which may be started during system initialization. This section explains the procedures available for starting services. Since the man:rc[8] system is primarily intended to start and stop services at system startup and shutdown time, the `start`, `stop` and `restart` options will only perform their action if the appropriate [.filename]#/etc/rc.conf# variable is set. So the first step to start a service, like for example package:www/nginx[] is to add it to [.filename]#/etc/rc.conf# by executing the following command: [source,shell] .... # sysrc nginx_enable="YES" .... Then nginx can be started executing the following command: [source,shell] .... # service nginx start .... [TIP] ==== To `start`, `stop` or `restart` a service regardless of the settings in [.filename]#/etc/rc.conf#, these commands should be prefixed with "one". For instance, to start package:www/nginx[] regardless of the current [.filename]#/etc/rc.conf# setting, execute the following command: [source,shell] .... # service nginx onestart .... ==== +It is also possible to put a service automatically into a jail, see the corresponding crossref:jails[service-jails,Service Jails] explanation. + [[configtuning-status-services]] === Status of a Service To determine if a service is running, use the `status` subcommand. For example, to verify that package:www/nginx[] is running: [source,shell] .... # service nginx status .... The output should be similar to the following: [.programlisting] .... nginx is running as pid 27871. .... [[configtuning-reload-services]] === Reload a Service In some cases, it is also possible to `reload` a service. This attempts to send a signal to an individual service, forcing the service to reload its configuration files. In most cases, this means sending the service a `SIGHUP` signal. *Not all services support this feature.* The man:rc[8] system is used for network services and it also contributes to most of the system initialization. For instance, when the [.filename]#/etc/rc.d/bgfsck# script is executed, it prints out the following message: [source,shell] .... Starting background file system checks in 60 seconds. .... This script is used for background file system checks, which occur only during system initialization. Many system services depend on other services to function properly. For example, man:yp[8] and other RPC-based services may fail to start until after the man:rpcbind[8] service has started. Additional information can be found in man:rc[8] and man:rc.subr[8]. === Using Services to Start Services Other services can be started using man:inetd[8]. Working with man:inetd[8] and its configuration is described in depth in crossref:network-servers[network-inetd,“The inetd Super-Server”]. In some cases, it may make more sense to use man:cron[8] to start system services. This approach has a number of advantages as man:cron[8] runs these processes as the owner of the man:crontab[5]. This allows regular users to start and maintain their own applications. The `@reboot` feature of man:cron[8], may be used in place of the time specification. This causes the job to run when man:cron[8] is started, normally during system initialization. [[cron-periodic]] == Cron and Periodic Scheduling tasks to run at a certain day or time is a very common task on FreeBSD. The tool in charge of performing this task is man:cron[8]. In addition to tasks that can be scheduled by the user via man:cron[8], FreeBSD performs routine background tasks managed by man:periodic[8]. [[configtuning-cron]] === Cron The man:cron[8] utility runs in the background and regularly checks [.filename]#/etc/crontab# for tasks to execute and searches [.filename]#/var/cron/tabs# for custom crontab files. These files are used to schedule tasks which cron runs at the specified times. Each entry in a crontab defines a task to run and is known as a _cron job_. Two different types of configuration files are used: the system crontab, which should not be modified, and user crontabs, which can be created and edited as needed. The format used by these files is documented in man:crontab[5]. The format of the system crontab, [.filename]#/etc/crontab# includes a `who` column which does not exist in user crontabs. In the system crontab, cron runs the command as the user specified in this column. In a user crontab, all commands run as the user who created the crontab. User crontabs allow individual users to schedule their own tasks. The `root` user can also have a user [.filename]#crontab# which can be used to schedule tasks that do not exist in the system [.filename]#crontab#. Here is a sample entry from the system crontab, [.filename]#/etc/crontab#: [.programlisting] .... # /etc/crontab - root's crontab for FreeBSD # # <.> # SHELL=/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin <.> # #minute hour mday month wday who command <.> # # Save some entropy so that /dev/random can re-seed on boot. */11 * * * * operator /usr/libexec/save-entropy <.> # # Rotate log files every hour, if necessary. 0 * * * * root newsyslog # # Perform daily/weekly/monthly maintenance. 1 3 * * * root periodic daily 15 4 * * 6 root periodic weekly 30 5 1 * * root periodic monthly # # Adjust the time zone if the CMOS clock keeps local time, as opposed to # UTC time. See adjkerntz(8) for details. 1,31 0-5 * * * root adjkerntz -a .... <.> Lines that begin with the `+#+` character are comments. A comment can be placed in the file as a reminder of what and why a desired action is performed. Comments cannot be on the same line as a command or else they will be interpreted as part of the command; they must be on a new line. Blank lines are ignored. <.> The equals (`=`) character is used to define any environment settings. In this example, it is used to define the `SHELL` and `PATH`. If the `SHELL` is omitted, cron will use the default Bourne shell. If the `PATH` is omitted, the full path must be given to the command or script to run. <.> This line defines the seven fields used in a system crontab: `minute`, `hour`, `mday`, `month`, `wday`, `who`, and `command`. The `minute` field is the time in minutes when the specified command will be run, the `hour` is the hour when the specified command will be run, the `mday` is the day of the month, `month` is the month, and `wday` is the day of the week. These fields must be numeric values, representing the twenty-four hour clock, or a `*`, representing all values for that field. The `who` field only exists in the system crontab and specifies which user the command should be run as. The last field is the command to be executed. <.> This entry defines the values for this cron job. The `\*/11`, followed by several more `*` characters, specifies that `/usr/libexec/save-entropy` is invoked by `operator` every eleven minutes of every hour, of every day and day of the week, of every month. Commands can include any number of switches. However, commands which extend to multiple lines need to be broken with the backslash "\" continuation character. [[configtuning-installcrontab]] === Creating a User Crontab To create a user crontab, invoke `crontab` in editor mode: [source,shell] .... % crontab -e .... This will open the user's crontab using the default text editor. The first time a user runs this command, it will open an empty file. Once a user creates a crontab, this command will open that file for editing. It is useful to add these lines to the top of the crontab file in order to set the environment variables and to remember the meanings of the fields in the crontab: [.programlisting] .... SHELL=/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin # Order of crontab fields # minute hour mday month wday command .... Then add a line for each command or script to run, specifying the time to run the command. This example runs the specified custom Bourne shell script every day at two in the afternoon. Since the path to the script is not specified in `PATH`, the full path to the script is given: [.programlisting] .... 0 14 * * * /home/user/bin/mycustomscript.sh .... [TIP] ==== Before using a custom script, make sure it is executable and test it with the limited set of environment variables set by cron. To replicate the environment that would be used to run the above cron entry, use: [.programlisting] .... env -i SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin HOME=/home/user LOGNAME=user /home/user/bin/mycustomscript.sh .... The environment set by cron is discussed in man:crontab[5]. Checking that scripts operate correctly in a cron environment is especially important if they include any commands that delete files using wildcards. ==== When finished editing the crontab, save the file. It will automatically be installed, and cron will read the crontab and run its cron jobs at their specified times. To list the cron jobs in a crontab, use this command: [source,shell] .... % crontab -l .... The output should be similar to the following: [.programlisting] .... 0 14 * * * /home/user/bin/mycustomscript.sh .... To remove all of the cron jobs in a user crontab: [source,shell] .... % crontab -r .... The output should be similar to the following: [.programlisting] .... remove crontab for user? y .... [[configtuning-periodic]] === Periodic FreeBSD provides a set of system management scripts to check status of various subsystems, perform security-related checks, rotate log files, etc. These scripts are run on a periodic basis: daily. weekly, or monthly. The management of these tasks is performed by man:periodic[8] and its configuration resides in man:periodic.conf[5]. The periodic tasks are initiated by entries in the system crontab, shown above. Scripts executed by man:periodic[8] are located in [.filename]#/etc/periodic/# for base utilities and in [.filename]#/usr/local/etc/periodic/# for third-party software. They are organized in 4 subdirectories, daily, weekly, monthly and security. [[enable-disable-periodic]] === Enable or Disable Periodic Tasks FreeBSD has some scripts enabled by default to run periodically. To enable or disable a task, the first step is to edit [.filename]#/etc/periodic.conf# executing the following command: [source,shell] .... # ee /etc/periodic.conf .... And then to enable, for example, `daily_status_zfs_enable` put the following content in the file: [.programlisting] .... daily_status_zfs_enable="YES" .... To disable a task that is active by default, all that needs to be done is to change `YES` to `NO`. [[configuring-output-periodic-tasks]] === Configuring the Output of Periodic Tasks In [.filename]#/etc/periodic.conf# the variables `daily_output`, `weekly_output` and `monthly_output` specifies where to send the results of the script execution. By default the output of the periodic scripts are emailed to root, and therefore it is best to read root's mail or alias root to a mailbox that is monitored. To send the results to another email or to other emails, add the email addresses separated by spaces to [.filename]#/etc/periodic.conf#: [.programlisting] .... daily_output="email1@example.com email2@example.com" weekly_output="email1@example.com email2@example.com" monthly_output="email1@example.com email2@example.com" .... To log periodic output instead of receiving it as email, add the following lines to [.filename]#/etc/periodic.conf#. man:newsyslog[8] will rotate these files at the appropriate times: [.programlisting] .... daily_output=/var/log/daily.log weekly_output=/var/log/weekly.log monthly_output=/var/log/monthly.log .... [[configtuning-syslog]] == Configuring System Logging Generating and reading system logs is an important aspect of system administration. The information in system logs can be used to detect hardware and software issues as well as application and system configuration errors. This information also plays an important role in security auditing and incident response. Most system daemons and applications will generate log entries. FreeBSD provides a system logger, man:syslogd[8], to manage logging. By default, syslogd is enabled and started when the system boots. This section describes how to configure the FreeBSD system logger for both local and remote logging and how to perform log rotation and log management. === Configuring Local Logging The configuration file, [.filename]#/etc/syslog.conf#, controls what syslogd does with log entries as they are received. There are several parameters to control the handling of incoming events. The _facility_ describes which subsystem generated the message, such as the kernel or a daemon, and the _level_ describes the severity of the event that occurred. This makes it possible to configure if and where a log message is logged, depending on the facility and level. It is also possible to take action depending on the application that sent the message, and in the case of remote logging, the hostname of the machine generating the logging event. This configuration file contains one line per action, where the syntax for each line is a selector field followed by an action field. The syntax of the selector field is _facility.level_ which will match log messages from _facility_ at level _level_ or higher. It is also possible to add an optional comparison flag before the level to specify more precisely what is logged. Multiple selector fields can be used for the same action, and are separated with a semicolon (`;`). Using `*` will match everything. The action field denotes where to send the log message, such as to a file or remote log host. As an example, here is the default [.filename]#/etc/syslog.conf# from FreeBSD: [.programlisting] .... # Spaces ARE valid field separators in this file. However, # other *nix-like systems still insist on using tabs as field # separators. If you are sharing this file between systems, you # may want to use only tabs as field separators here. # Consult the syslog.conf(5) manpage. *.err;kern.warning;auth.notice;mail.crit /dev/console <.> *.notice;authpriv.none;kern.debug;lpr.info;mail.crit;news.err /var/log/messages security.* /var/log/security auth.info;authpriv.info /var/log/auth.log mail.info /var/log/maillog <.> cron.* /var/log/cron !-devd *.=debug /var/log/debug.log <.> *.emerg * daemon.info /var/log/daemon.log # uncomment this to log all writes to /dev/console to /var/log/console.log # touch /var/log/console.log and chmod it to mode 600 before it will work #console.info /var/log/console.log # uncomment this to enable logging of all log messages to /var/log/all.log # touch /var/log/all.log and chmod it to mode 600 before it will work #*.* /var/log/all.log # uncomment this to enable logging to a remote loghost named loghost #*.* @loghost # uncomment these if you're running inn # news.crit /var/log/news/news.crit # news.err /var/log/news/news.err # news.notice /var/log/news/news.notice # Uncomment this if you wish to see messages produced by devd # !devd # *.>=notice /var/log/devd.log <.> !* include /etc/syslog.d include /usr/local/etc/syslog.d .... <.> Matches all messages with a level of `err` or higher, as well as `kern.warning`, `auth.notice` and `mail.crit`, and sends these log messages to the console ([.filename]#/dev/console#). <.> Matches all messages from the `mail` facility at level `info` or above and logs the messages to [.filename]#/var/log/maillog#. <.> Uses a comparison flag (`=`) to only match messages at level `debug` and logs them to [.filename]#/var/log/debug.log#. <.> Is an example usage of a program specification. This makes the rules following it only valid for the specified program. In this case, only the messages generated by man:devd[8] are logged to [.filename]#/var/log/devd.log#. For more information about [.filename]#/etc/syslog.conf#, its syntax, and more advanced usage examples, see man:syslog.conf[5]. [[logging-facilities]] === Logging Facilities A facility describes the part of the system generating the message. Facilities are a way of separating the different messages so that it is easier for the user to consult the logs. .syslog facilities [options="header", cols="1,1"] |=== | Name | Description | auth | The authorization system: man:login[1], man:su[1], man:getty[8], etc. | authpriv | The same as auth, but logged to a file readable only by root. | console | Messages written to [.filename]#/dev/console# by the kernel console output driver. | cron | Messages written by the man:cron[8] daemon. | daemon | System daemons, such as man:routed[8], that are not provided for explicitly by other facilities. | ftp | The file transfer protocol daemons: man:ftpd[8], man:tftpd[8]. | kern | Messages generated by the kernel. These cannot be generated by any user processes. | lpr | The line printer spooling system: man:lpr[1], man:lpc[8], man:lpd[8], etc. | mail | The mail system. | mark | This facility adds a record every 20 minutes. | news | The network news system. | ntp | The network time protocol system. | security | Security subsystems, such as man:ipfw[4]. | syslog | Messages generated internally by syslogd(8). | user | Messages generated by random user processes. *This is the default facility identifier if none is specified*. | uucp | The Unix-to-Unix Copy system. An ancient protocol. Really weird to see messages from this facility. | local0 through local7 | Reserved for local use. |=== [[logging-levels]] === Logging Levels The level describes the severity of the message, and is a keyword from the following ordered list (higher to lower): .syslog levels [options="header", cols="1,1"] |=== | Name | Description | emerg | A panic condition. This is normally broadcast to all users. | alert | A condition that should be corrected immediately, such as a corrupted system database. | crit | Critical conditions, e.g., hard device errors. | err | Errors. | warning | Warning messages. | notice | Conditions that are not error conditions, but should possibly be handled specially. | info | Informational messages. | debug | Messages that contain information normally of use only when debugging a program. | none | This special level disables a particular facility. |=== [[read-log-messages]] === Read Log Messages By default FreeBSD log files use the format link:https://datatracker.ietf.org/doc/html/rfc3164[rfc3164], also known as The BSD syslog Protocol. Learn more about other formats and how to use them at man:syslog[8]. Typically the logs have the following syntax: [.programlisting] .... date time hostname program[pid]: the message .... The output of the [.filename]#/var/log/cron# file will be used as an example: [.programlisting] .... [...] Jul 16 12:40:00 FreeBSD /usr/sbin/cron[81519]: (root) CMD (/usr/libexec/atrun) Jul 16 12:44:00 FreeBSD /usr/sbin/cron[83072]: (operator) CMD (/usr/libexec/save-entropy) [...] .... Verbose logging, so the facility and the level on each message will be added, can be enabled in man:syslog[8] by running the following command: [source,shell] .... # sysrc syslogd_flags="-vv" .... Once the function is activated, the facility and the level will be displayed in the log as shown in the following example: [.programlisting] .... [...] Jul 16 17:40:00 FreeBSD /usr/sbin/cron[1016]: (root) CMD (/usr/libexec/atrun) Jul 16 17:44:00 FreeBSD /usr/sbin/cron[1030]: (operator) CMD (/usr/libexec/save-entropy) [...] .... === Log Management and Rotation Log files can grow quickly, taking up disk space and making it more difficult to locate useful information. In FreeBSD, man:newsyslog[8] is used to manage log files and attempt to mitigate this. This built-in program periodically rotates and compresses log files, and optionally creates missing log files and signals programs when log files are moved. [NOTE] ==== Since newsyslog is run from man:cron[8], it cannot rotate files more often than it is scheduled to run from man:cron[8]. In the default configuration, it runs every hour. ==== Here is the default configuration in FreeBSD, more information in man:newsyslog.conf[5]: [.programlisting] .... # configuration file for newsyslog # # Entries which do not specify the '/pid_file' field will cause the # syslogd process to be signalled when that log file is rotated. This # action is only appropriate for log files which are written to by the # syslogd process (ie, files listed in /etc/syslog.conf). If there # is no process which needs to be signalled when a given log file is # rotated, then the entry for that file should include the 'N' flag. # # Note: some sites will want to select more restrictive protections than the # defaults. In particular, it may be desirable to switch many of the 644 # entries to 640 or 600. For example, some sites will consider the # contents of maillog, messages, and lpd-errs to be confidential. In the # future, these defaults may change to more conservative ones. # # logfilename [owner:group] mode count size when flags [/pid_file] [sig_num] /var/log/all.log 600 7 * @T00 J /var/log/auth.log 600 7 1000 @0101T JC /var/log/console.log 600 5 1000 * J /var/log/cron 600 3 1000 * JC /var/log/daily.log 640 7 * @T00 JN /var/log/debug.log 600 7 1000 * JC /var/log/init.log 644 3 1000 * J /var/log/kerberos.log 600 7 1000 * J /var/log/maillog 640 7 * @T00 JC /var/log/messages 644 5 1000 @0101T JC /var/log/monthly.log 640 12 * $M1D0 JN /var/log/devd.log 644 3 1000 * JC /var/log/security 600 10 1000 * JC /var/log/utx.log 644 3 * @01T05 B /var/log/weekly.log 640 5 * $W6D0 JN /var/log/daemon.log 644 5 1000 @0101T JC /etc/newsyslog.conf.d/[!.]*.conf /usr/local/etc/newsyslog.conf.d/[!.]*.conf .... . `logfilename` - Name of the system log file to be archived. . `[owner:group]` - This optional field specifies the owner and group for the archive file. . `mode` - Specify the file mode of the log file and archives. Valid mode bits are 0666. (That is, read and write permissions for the rotated log may be specified for the owner, group, and others.) . `count` - Specify the maximum number of archive files which may exist. . `size` - When the size of the log file reaches size in kilobytes, the log file will be trimmed as described above. If this field contains an asterisk ('*'), the log file will not be trimmed based on size. . `when` - Consist of an interval, a specific time, or both. Supported options in man:newsyslog.conf[5]. . `flags` - Indicates the flags that newsyslog accepts, supported options in man:newsyslog.conf[5]. . `[/pid_file]` - This optional field specifies the file name containing a daemon's process ID or to find a group process ID. . `[sig_num]` - This optional field specifies the signal that will be sent to the daemon process. [NOTE] ==== The last two fields are optional and specify the name of the Process ID (PID) file of a process and a signal number to send to that process when the file is rotated. ==== [[network-syslogd]] === Configuring Remote Logging Monitoring the log files of multiple hosts can become unwieldy as the number of systems increases. Configuring centralized logging can reduce some of the administrative burden of log file administration. In FreeBSD, centralized log file aggregation, merging, and rotation can be configured using syslogd and newsyslog. This section demonstrates an example configuration, where host `A`, named `logserv.example.com`, will collect logging information for the local network. Host `B`, named `logclient.example.com`, will be configured to pass logging information to the logging server. ==== Log Server Configuration A log server is a system that has been configured to accept logging information from other hosts. Before configuring a log server, check the following: * If there is a firewall between the logging server and any logging clients, ensure that the firewall ruleset allows UDP port 514 for both the clients and the server. * The logging server and all client machines must have forward and reverse entries in the local DNS. If the network does not have a DNS server, create entries in each system's [.filename]#/etc/hosts#. Proper name resolution is required so that log entries are not rejected by the logging server. On the log server, edit [.filename]#/etc/syslog.conf# to specify the name of the client to receive log entries from, the logging facility to be used, and the name of the log to store the host's log entries. This example adds the hostname of `B`, logs all facilities, and stores the log entries in [.filename]#/var/log/logclient.log#. .Sample Log Server Configuration [example] ==== [.programlisting] .... +logclient.example.com *.* /var/log/logclient.log .... ==== When adding multiple log clients, add a similar two-line entry for each client. More information about the available facilities may be found in man:syslog.conf[5]. Next, execute the following commands: [source,shell] .... # sysrc syslogd_enable="YES" # sysrc syslogd_flags="-a logclient.example.com -v -v" .... The first entry starts syslogd at system boot. The second entry allows log entries from the specified client. The `-v -v` increases the verbosity of logged messages. This is useful for tweaking facilities as administrators are able to see what type of messages are being logged under each facility. Multiple `-a` options may be specified to allow logging from multiple clients. IP addresses and whole netblocks may also be specified. Refer to man:syslogd[8] for a full list of possible options. Finally, create the log file: [source,shell] .... # touch /var/log/logclient.log .... At this point, syslogd should be restarted and verified: [source,shell] .... # service syslogd restart # pgrep syslog .... If a PID is returned, the server restarted successfully, and client configuration can begin. If the server did not restart, consult [.filename]#/var/log/messages# for the error. ==== Log Client Configuration A logging client sends log entries to a logging server on the network. The client also keeps a local copy of its own logs. Once a logging server has been configured, execute the following commands on the logging client: [source,shell] .... # sysrc syslogd_enable="YES" # sysrc syslogd_flags="-s -v -v" .... The first entry enables syslogd on boot up. The second entry prevents logs from being accepted by this client from other hosts (`-s`) and increases the verbosity of logged messages. Next, define the logging server in the client's [.filename]#/etc/syslog.conf#. In this example, all logged facilities are sent to a remote system, denoted by the `@` symbol, with the specified hostname: [.programlisting] .... *.* @logserv.example.com .... After saving the edit, restart syslogd for the changes to take effect: [source,shell] .... # service syslogd restart .... To test that log messages are being sent across the network, use man:logger[1] on the client to send a message to syslogd: [source,shell] .... # logger "Test message from logclient" .... This message should now exist both in [.filename]#/var/log/messages# on the client and [.filename]#/var/log/logclient.log# on the log server. ==== Debugging Log Servers If no messages are being received on the log server, the cause is most likely a network connectivity issue, a hostname resolution issue, or a typo in a configuration file. To isolate the cause, ensure that both the logging server and the logging client are able to `ping` each other using the hostname specified in their [.filename]#/etc/rc.conf#. If this fails, check the network cabling, the firewall ruleset, and the hostname entries in the DNS server or [.filename]#/etc/hosts# on both the logging server and clients. Repeat until the `ping` is successful from both hosts. If the `ping` succeeds on both hosts but log messages are still not being received, temporarily increase logging verbosity to narrow down the configuration issue. In the following example, [.filename]#/var/log/logclient.log# on the logging server is empty and [.filename]#/var/log/messages# on the logging client does not indicate a reason for the failure. To increase debugging output, edit the `syslogd_flags` entry on the logging server and issue a restart: [source,shell] .... sysrc syslogd_flags="-d -a logclient.example.com -v -v" .... [source,shell] .... # service syslogd restart .... Debugging data similar to the following will flash on the console immediately after the restart: [.programlisting] .... logmsg: pri 56, flags 4, from logserv.example.com, msg syslogd: restart syslogd: restarted logmsg: pri 6, flags 4, from logserv.example.com, msg syslogd: kernel boot file is /boot/kernel/kernel Logging to FILE /var/log/messages syslogd: kernel boot file is /boot/kernel/kernel cvthname(192.168.1.10) validate: dgram from IP 192.168.1.10, port 514, name logclient.example.com; rejected in rule 0 due to name mismatch. .... In this example, the log messages are being rejected due to a typo which results in a hostname mismatch. The client's hostname should be `logclient`, not `logclien`. Fix the typo, issue a restart, and verify the results: [source,shell] .... # service syslogd restart .... The output should be similar to the following: [.programlisting] .... logmsg: pri 56, flags 4, from logserv.example.com, msg syslogd: restart syslogd: restarted logmsg: pri 6, flags 4, from logserv.example.com, msg syslogd: kernel boot file is /boot/kernel/kernel syslogd: kernel boot file is /boot/kernel/kernel logmsg: pri 166, flags 17, from logserv.example.com, msg Dec 10 20:55:02 logserv.example.com syslogd: exiting on signal 2 cvthname(192.168.1.10) validate: dgram from IP 192.168.1.10, port 514, name logclient.example.com; accepted in rule 0. logmsg: pri 15, flags 0, from logclient.example.com, msg Dec 11 02:01:28 trhodes: Test message 2 Logging to FILE /var/log/logclient.log Logging to FILE /var/log/messages .... At this point, the messages are being properly received and placed in the correct file. ==== Security Considerations As with any network service, security requirements should be considered before implementing a logging server. Log files may contain sensitive data about services enabled on the local host, user accounts, and configuration data. Network data sent from the client to the server will not be encrypted or password protected. If a need for encryption exists, consider using package:security/stunnel[], which will transmit the logging data over an encrypted tunnel. Local security is also an issue. Log files are not encrypted during use or after log rotation. Local users may access log files to gain additional insight into system configuration. Setting proper permissions on log files is critical. The built-in log rotator, newsyslog, supports setting permissions on newly created and rotated log files. Setting log files to mode `600` should prevent unwanted access by local users. Refer to man:newsyslog.conf[5] for additional information. [[acpi-overview]] == Power and Resource Management It is important to utilize hardware resources in an efficient manner. Power and resource management allows the operating system to monitor system limits and to possibly run some actions triggered by events related to those limits. [[acpi-config]] === ACPI configuration On FreeBSD the management of these resources is managed by the man:acpi[4] kernel device. [NOTE] ==== In FreeBSD the man:acpi[4] driver is loaded by default at system boot. This driver *cannot be unloaded after boot* because the system bus uses it for various hardware interactions. ==== In addition to man:acpi[4], FreeBSD has several dedicated kernel modules for various ACPI vendor subsystems. These modules will add some extra functionality like fan speed, keyboard backlit or screen brightness. The list can be obtained by running the following command: [source,shell] .... % ls /boot/kernel | grep acpi .... The output should be similar to the following: [.programlisting] .... acpi_asus.ko acpi_asus_wmi.ko acpi_dock.ko acpi_fujitsu.ko acpi_hp.ko acpi_ibm.ko acpi_panasonic.ko acpi_sony.ko acpi_toshiba.ko acpi_video.ko acpi_wmi.ko sdhci_acpi.ko uacpi.ko .... In the event that, for example, an IBM/Lenovo laptop is used, it will be necessary to load the module man:acpi_ibm[4] by executing the following command: [source,shell] .... # kldload acpi_ibm .... And add this line to [.filename]#/boot/loader.conf# to load it at boot: [.programlisting] .... acpi_ibm_load="YES" .... An alternative to the man:acpi_video[4] module is the man:backlight[9] driver. It provides a generic way for handling a panel backlight. The default GENERIC kernel includes this driver. The man:backlight[8] utility can be used to query and adjust the brightness of the panel backlight. In this example the brightness is decreased by 10%: [source,shell] .... % backlight decr 10 .... [[cpu-power-management]] === CPU Power Management CPU is the most consuming part of the system. Knowing how to improve CPU efficiency is a fundamental part of our system in order to save energy. In order to make proper use of the machine's resources in a correct way, FreeBSD supports technologies such as Intel Turbo Boost, AMD Turbo Core, Intel Speed Shift among others through the use of man:powerd[8] and man:cpufreq[4]. The first step will be to obtain the CPU information by executing the following command: [source,shell] .... % sysctl dev.cpu.0 <.> .... <.> In this case the `0` digit represents the first core of the CPU. The output should be similar to the following: [.programlisting] .... dev.cpu.0.cx_method: C1/mwait/hwc C2/mwait/hwc C3/mwait/hwc/bma dev.cpu.0.cx_usage_counters: 3507294 0 0 dev.cpu.0.cx_usage: 100.00% 0.00% 0.00% last 3804us dev.cpu.0.cx_lowest: C3 <1> dev.cpu.0.cx_supported: C1/1/1 C2/2/1 C3/3/57 <2> dev.cpu.0.freq_levels: 2267/35000 2266/35000 1600/15000 800/12000 <3> dev.cpu.0.freq: 1600 <4> dev.cpu.0.temperature: 40.0C <5> dev.cpu.0.coretemp.throttle_log: 0 dev.cpu.0.coretemp.tjmax: 105.0C dev.cpu.0.coretemp.resolution: 1 dev.cpu.0.coretemp.delta: 65 dev.cpu.0.%parent: acpi0 dev.cpu.0.%pnpinfo: _HID=none _UID=0 _CID=none dev.cpu.0.%location: handle=\_PR_.CPU0 dev.cpu.0.%driver: cpu dev.cpu.0.%desc: ACPI CPU .... <1> Lowest Cx state to use for idling the CPU. <2> CPU supported Cx states. <3> Currently available levels for the CPU (frequency/power usage). <4> Current active CPU frequency in MHz. <5> Current temperature of the CPU. [NOTE] ==== If the temperature information is not displayed, load the man:coretemp[4] module. In case of using an AMD CPU, load the man:amdtemp[4] module. ==== Once the CPU information is available the easiest way to configure power saving is to let man:powerd[8] take over. Enable man:powerd[8] service in [.filename]#/etc/rc.conf# to start at system boot: [source,shell] .... # sysrc powerd_enable=YES .... It will also be necessary to indicate certain parameters to man:powerd[8] to tell it how to manage the state of the CPU executing the following command: [source,shell] .... # sysrc powerd_flags="-a hiadaptive -i 25 -r 85 -N" .... . `-a`: Selects the mode to use while on AC power. . `hiadaptive`: Operation mode. More info at man:powerd[8]. . `-i`: Specifies the CPU load percent level when adaptive mode should begin to degrade performance to save power. . `-r`: Specifies the CPU load percent level where adaptive mode should consider the CPU running and increase performance. . `-N`: Treat "nice" time as idle for the purpose of load calculation; i.e., do not increase the CPU frequency if the CPU is only busy with "nice" processes. And then enable the service executing the following command: [source,shell] .... # service powerd start .... [[cpufreq]] === CPU Frequency Control FreeBSD includes a generic man:cpufreq[4] driver to allow the administrator, or software such as man:powerd[8] and package:sysutils/powerdxx[], to manage the frequency of the CPU to achieve the desired balance between performance and economy. A lower setting will save power while reducing the heat generated by the CPU. A higher setting will increase performance at the cost of using additional power and generating more heat. [[est]] === Intel(R) Enhanced Speed Step(TM) The Intel(R) Enhanced Speed Step(TM) driver, man:est[4], replaces the generic man:cpufreq[4] driver for CPUs that provide this feature. The CPU frequency can be statically adjusted using man:sysctl[8], or with the `/etc/rc.d/power_profile` startup script. Additional software, such as man:powerd[8] or package:sysutils/powerdxx[], can be used to automatically adjust the CPU frequency based on processor utilization. Each supported frequency, along with its expected power consumption, can be listed by examining the man:sysctl[3] tree: [source,shell] .... # sysctl dev.cpufreq.0.freq_driver dev.cpu.0.freq_levels dev.cpu.0.freq .... The output should be similar to the following: [.programlisting] .... dev.cpufreq.0.freq_driver: est0 dev.cpu.0.freq_levels: 3001/53000 3000/53000 2900/50301 2700/46082 2600/43525 2400/39557 2300/37137 2100/33398 2000/31112 1800/27610 1700/25455 1500/22171 1400/20144 1200/17084 1100/15181 900/12329 800/10550 dev.cpu.0.freq: 800 .... A frequency 1 MHz higher than the maximum frequency of the CPU indicates the Intel(R) Turbo Boost(TM) feature. [[hwpstate_intel]] === Intel Speed Shift(TM) Users running newer Intel(R) CPUs may find some differences in dynamic frequency control when upgrading to FreeBSD 13. A new driver for the Intel(R) Speed Shift(TM) feature set, available on certain SKUs, exposes the ability for the hardware to dynamically vary the core frequencies, including on a per core basis. FreeBSD 13 comes with the man:hwpstate_intel[4] driver to automatically enable Speed Shift(TM) control on equipped CPUs, replacing the older Enhanced Speed Step(TM) man:est[4] driver. The man:sysctl[8] `dev.cpufreq.%d.freq_driver` will indicate if the system is using Speed Shift. To determine which frequency control driver is being used, examining the `dev.cpufreq.0.freq_driver` oid. [source,shell] .... # sysctl dev.cpufreq.0.freq_driver .... The output should be similar to the following: [.programlisting] .... dev.cpufreq.0.freq_driver: hwpstate_intel0 .... This indicates that the new man:hwpstate_intel[4] driver is in use. On such systems, the oid `dev.cpu.%d.freq_levels` will show only the maximum CPU frequency, and will indicate a power consumption level of `-1`. The current CPU frequency can be determined by examining the `dev.cpu.%d.freq` oid. [source,shell] .... # sysctl dev.cpu.0.freq_levels dev.cpu.0.freq .... The output should be similar to the following: [.programlisting] .... dev.cpu.0.freq_levels: 3696/-1 dev.cpu.0.freq: 898 .... For more information, including on how to balance performance and energy use, and on how to disable this driver, refer to the man page man:hwpstate_intel[4]. [NOTE] ==== Users accustomed to using man:powerd[8] or package:sysutils/powerdxx[] will find these utilities have been superseded by the man:hwpstate_intel[4] driver and no longer work as expected. ==== [[graphics-card-power-management]] === Graphics Card Power Management Graphics cards have become a fundamental part of computing in recent years. Some graphics cards may have excessive power consumption. FreeBSD allows certain configurations to improve power consumption. In case of using a Intel(R) graphics card with the package:graphics/drm-kmod[] driver these options can be added to [.filename]#/boot/loader.conf#: [.programlisting] .... compat.linuxkpi.fastboot=1 <.> compat.linuxkpi.enable_dc=2 <.> compat.linuxkpi.enable_fbc=1 <.> .... <.> Try to skip unnecessary mode sets at boot time. <.> Enable power-saving display C-states. <.> Enable frame buffer compression for power savings === Suspend/Resume The suspend/resume function allows the machine to be kept in a state in which there is no a big energy consumption and allows the system to be resumed without having to lose the state of the running programs. [NOTE] ==== In order for the suspend/resume functionality to work correctly the graphics drivers must be loaded on the system. In non-KMS-supported graphics cards man:sc[4] must be used not to break the suspend/resume functionality. More information about which driver to use and how to configure it can be found at the crossref:x11[x11, The X Window System chapter]. ==== man:acpi[4] supports the next list of sleep states: .Supported Sleep States [options="header", cols="1,1"] |=== |S1 |Quick suspend to RAM. The CPU enters a lower power state, but most peripherals are left running. |S2 |Lower power state than S1, but with the same basic characteristics. Not supported by many systems. |S3 (Sleep mode) |Suspend to RAM. Most devices are powered off, and the system stops running except for memory refresh. |S4 (Hibernation) |Suspend to disk. All devices are powered off, and the system stops running. When resuming, the system starts as if from a cold power on. *Not yet supported by FreeBSD*. |S5 |System shuts down cleanly and powers off. |=== [[configure-suspend-resume]] ==== Configuring Suspend/Resume The first step will be to know which type of sleep states supports the hardware we are using executing the following command: [source,shell] .... % sysctl hw.acpi.supported_sleep_state .... The output should be similar to the following: [.programlisting] .... hw.acpi.supported_sleep_state: S3 S4 S5 .... [WARNING] ==== As stated above FreeBSD does *not* yet support the `S4` state. ==== man:acpiconf[8] can be used to check if the `S3` state works correctly by running the following command, if it succeeds, the screen should go black and the machine will turn off: [source,shell] .... # acpiconf -s 3 .... In the vast majority of cases the Suspend/Resume functionality wants to be used on a laptop. FreeBSD can be configured to enter the `S3` state when closing the lid by adding the following line to the [.filename]#/etc/sysctl.conf# file. [.programlisting] .... hw.acpi.lid_switch_state=S3 .... [[troubleshooting-suspend-resume]] ==== Troubleshooting in Suspend/Resume A lot of effort has been made to make the Suspend and Resume functions work properly and in the best way on FreeBSD. But currently the Suspend and Resume functions only work properly on some specific laptops. Some checks can be done in case it doesn't work properly. In some cases it is enough to turn off the bluetooth. In others it is enough loading the correct driver for the graphics card, etc. In case it doesn't work correctly, some tips can be found on the FreeBSD Wiki in the section link:https://wiki.freebsd.org/SuspendResume[Suspend/Resume]. [[adding-swap-space]] == Adding Swap Space Sometimes a FreeBSD system requires more swap space. This section describes two methods to increase swap space: adding swap to an existing partition or new hard drive, and creating a swap file on an existing file system. For information on how to encrypt swap space, which options exist, and why it should be done, refer to crossref:disks[swap-encrypting,“Encrypting Swap”]. [[new-drive-swap]] === Swap on a New Hard Drive or Existing Partition Adding a new drive for swap gives better performance than using a partition on an existing drive. Setting up partitions and drives is explained in crossref:disks[disks-adding,"Adding Disks"] while crossref:bsdinstall[configtuning-initial,"Designing the Partition Layout"] discusses partition layouts and swap partition size considerations. [WARNING] ==== It is possible to use any partition not currently mounted, even if it already contains data. Using `swapon` on a partition that contains data will overwrite and destroy that data. Make sure that the partition to be added as swap is really the intended partition before running `swapon`. ==== man:swapon[8] can be used to add a swap partition to the system executing the following command: [source,shell] .... # swapon /dev/ada1p2 .... To automatically add this swap partition on boot, add an entry to [.filename]#/etc/fstab#: [.programlisting] .... /dev/ada1p2 none swap sw 0 0 .... See man:fstab[5] for an explanation of the entries in [.filename]#/etc/fstab#. [[create-swapfile]] === Creating a Swap File [[swapfile-10-and-later]] These examples create a 512M swap file called [.filename]#/usr/swap0#. [WARNING] ==== Swap files on ZFS file systems are strongly discouraged, as swapping can lead to system hangs. ==== The first step is to create the swap file: [source,shell] .... # dd if=/dev/zero of=/usr/swap0 bs=1m count=512 .... The second step is to put the proper permissions on the new file: [source,shell] .... # chmod 0600 /usr/swap0 .... The third step is to inform the system about the swap file by adding a line to [.filename]#/etc/fstab#: [.programlisting] .... md none swap sw,file=/usr/swap0,late 0 0 .... Swap space will be added on system startup. To add swap space immediately, use man:swapon[8]: [source,shell] .... # swapon -aL .... diff --git a/documentation/content/en/books/handbook/jails/_index.adoc b/documentation/content/en/books/handbook/jails/_index.adoc index bf17248179..9465c4dccc 100644 --- a/documentation/content/en/books/handbook/jails/_index.adoc +++ b/documentation/content/en/books/handbook/jails/_index.adoc @@ -1,1156 +1,1223 @@ --- title: Chapter 17. Jails and Containers part: Part III. System Administration prev: books/handbook/security next: books/handbook/mac description: Jails improve on the concept of the traditional chroot environment in several ways tags: ["jails", "creating", "managing", "updating"] showBookMenu: true weight: 21 path: "/books/handbook/jails/" --- [[jails]] = Jails and Containers :doctype: book :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :sectnumoffset: 17 :partnums: :source-highlighter: rouge :experimental: :images-path: books/handbook/jails/ ifdef::env-beastie[] ifdef::backend-html5[] :imagesdir: ../../../../images/{images-path} endif::[] ifndef::book[] include::shared/authors.adoc[] include::shared/mirrors.adoc[] include::shared/releases.adoc[] include::shared/attributes/attributes-{{% lang %}}.adoc[] include::shared/{{% lang %}}/teams.adoc[] include::shared/{{% lang %}}/mailing-lists.adoc[] include::shared/{{% lang %}}/urls.adoc[] toc::[] endif::[] ifdef::backend-pdf,backend-epub3[] include::../../../../../shared/asciidoctor.adoc[] endif::[] endif::[] ifndef::env-beastie[] toc::[] include::../../../../../shared/asciidoctor.adoc[] endif::[] [[jails-synopsis]] == Synopsis Since system administration is a difficult task, many tools have been developed to make life easier for the administrator. These tools often enhance the way systems are installed, configured, and maintained. One of the tools which can be used to enhance the security of a FreeBSD system is _jails_. Jails have been available since FreeBSD 4.X and continue to be enhanced in their usefulness, performance, reliability, and security. Jails build upon the man:chroot[2] concept, which is used to change the root directory of a set of processes. This creates a safe environment, separate from the rest of the system. Processes created in the chrooted environment can not access files or resources outside of it. For that reason, compromising a service running in a chrooted environment should not allow the attacker to compromise the entire system. However, a chroot has several limitations. It is suited to easy tasks which do not require much flexibility or complex, advanced features. Over time, many ways have been found to escape from a chrooted environment, making it a less than ideal solution for securing services. Jails improve on the concept of the traditional chroot environment in several ways. In a traditional chroot environment, processes are only limited in the part of the file system they can access. The rest of the system resources, system users, running processes, and the networking subsystem are shared by the chrooted processes and the processes of the host system. Jails expand this model by virtualizing access to the file system, the set of users, and the networking subsystem. More fine-grained controls are available for tuning the access of a jailed environment. Jails can be considered as a type of operating system-level virtualization. This chapter covers: * What a jail is and what purpose it may serve in FreeBSD installations. * The different types of jail. * The different ways to configure the network for a jail. * The jail configuration file. * How to create the different types of jail. * How to start, stop, and restart a jail. * The basics of jail administration, both from inside and outside the jail. * How to upgrade the different types of jail. * A incomplete list of the different FreeBSD jail managers. [[jail-types]] == Jail Types Some administrators divide jails into different types, although the underlying technology is the same. Each administrator will have to assess what type of jail to create in each case depending on the problem they have to solve. Below can be found a list of the different types, their characteristics, and considerations for use. [[thick-jails]] === Thick Jails A thick jail is a traditional form of FreeBSD Jail. In a thick jail, a complete copy of the base system is replicated within the jail's environment. This means that the jail has its own separate instance of the FreeBSD base system, including libraries, executables, and configuration files. The jail can be thought of as an almost complete standalone FreeBSD installation, but running within the confines of the host system. This isolation ensures that the processes within the jail are kept separate from those on the host and other jails. Advantages of Thick Jails: * High degree of isolation: Processes within the jail are isolated from the host system and other jails. * Independence: Thick jails can have different versions of libraries, configurations, and software than the host system or other jails. * Security: Since the jail contains its own base system, vulnerabilities or issues affecting the jail environment won't directly impact the host or other jails. Disadvantages of Thick Jails: * Resource overhead: Because each jail maintains its own separate base system, thick jails consume more resources compared to thin jails. * Maintenance: Each jail requires its own maintenance and updates for its base system components. [[thin-jails]] === Thin Jails A thin jail shares the base system using OpenZFS snapshots or NullFS mounts from a template. Only a minimal subset of base system is duplicated for each thin jail, resulting in less resource consumption compared to a thick jail. However, this also means that thin jails have less isolation and independence compared to thick jails. Changes in shared components could potentially affect multiple thin jails simultaneously. In summary, a FreeBSD Thin Jail is a type of FreeBSD Jail that replicates a substantial portion, but not all, of the base system within the isolated environment. Advantages of Thin Jails: * Resource Efficiency: Thin jails are more resource-efficient compared to thick jails. Since they share most of the base system, they consume less disk space and memory. This makes it possible to run more jails on the same hardware without consuming excessive resources. * Faster Deployment: Creating and launching thin jails is generally faster compared to thick jails. This can be particularly advantageous when you need to rapidly deploy multiple instances. * Unified Maintenance: Since thin jails share the majority of their base system with the host system, updates and maintenance of common base system components (such as libraries and binaries) only need to be done once on the host. This simplifies the maintenance process compared to maintaining an individual base system for each thick jail. * Shared Resources: Thin jails can more easily share common resources such as libraries and binaries with the host system. This can potentially lead to more efficient disk caching and improved performance for applications within the jail. Disadvantages of Thin Jails: * Reduced Isolation: The primary disadvantage of thin jails is that they offer less isolation compared to thick jails. Since they share a significant portion of the template's base system, vulnerabilities or issues affecting shared components could potentially impact multiple jails simultaneously. * Security Concerns: The reduced isolation in thin jails could pose security risks, as a compromise in one jail might have a greater potential to affect other jails or the host system. * Dependency Conflicts: If multiple thin jails require different versions of the same libraries or software, managing dependencies can become complex. In some cases, this might require additional effort to ensure compatibility. * Compatibility Challenges: Applications within a thin jail might encounter compatibility issues if they assume a certain base system environment that differs from the shared components provided by the template. +[[service-jails]] +=== Service Jails + +A service jail shares the complete filesystem tree directly with the host (the jail root path is [.filename]#/#) and as such can access and modify any file on the host, and shares the same user accounts with the host. +By default it has no access to the network or other resources which are restricted in jails, but they can be configured to re-use the network of the host and to remove some of the jail-restrictions. +The use case for service jails is automatic confinement of services/daemons inside a jail with minimal configuration, and without any knowledge of the files needed by such service/daemon. +Service jails exist since FreeBSD 15. + +Advantages of Service Jails: + +* Zero Administration: A service jail ready service needs only one config line in [.filename]#/etc/rc.conf#, a service which is not service jails ready needs two config lines. +* Resource Efficiency: Service jails are more resource efficient than thin jails, as they do not need any additional disk space or network resource. +* Faster Deployment: Creating and launching service jails is generally faster compared to thin jails if only distinct services/daemons shall be jailed and no parallel instances of the same service/daemon is needed. +* Shared Resources: Service jails share all resources such as libraries and binaries with the host system. This can potentially lead to more efficient disk caching and improved performance for applications within the jail. +* Process Isolation: Service jails isolate a particular service, it can not see processes which are not a child of the service jail, even if they run within the same user account. + +Disadvantages of Service Jails: + +* Reduced Isolation: The primary disadvantage of service jails is that they offer no filesystem isolation compared to thick or thin jails. +* Security Concerns: The reduced isolation in service jails could pose security risks, as a compromise in one jail might have a greater potential to affect everything on the host system. + +Most of the configuration of jails which is discussed below is not needed for service jails. +To understand how jails work, it is recommended to understand those configuration possibilities. The details about what is needed to configure a service jail is in crossref:jails[service-jails-config, Configuring service jails]. + [[vnet-jails]] === VNET Jails A FreeBSD VNET jail is a virtualized environment that allows for the isolation and control of network resources for processes running within it. It provides a high level of network segmentation and security by creating a separate network stack for processes within the jail, ensuring that network traffic within the jail is isolated from the host system and other jails. In essence, FreeBSD VNET jails add a network configuration mechanism. This means a VNET jail can be created as a Thick or Thin Jail. [[linux-jails]] === Linux Jails A FreeBSD Linux Jail is a feature in the FreeBSD operating system that enables the use of Linux binaries and applications within a FreeBSD jail. This functionality is achieved by incorporating a compatibility layer that allows certain Linux system calls and libraries to be translated and executed on the FreeBSD kernel. The purpose of a Linux Jail is to facilitate the execution of Linux software on a FreeBSD system without needing a separate Linux virtual machine or environment. [[host-configuration]] == Host Configuration Before creating any jail on the host system it is necessary to perform certain configuration and obtain some information from the host system. It will be necessary to configure the man:jail[8] utility, create the necessary directories to configure and install jails, obtain information from the host's network, and check whether the host uses OpenZFS or UFS as its file system. [TIP] ==== The FreeBSD version running in the jail can not be newer than the version running in the host. ==== [[host-configuration-jail-utility]] === Jail Utility The man:jail[8] utility manages jails. To start jails when the system boots, run the following commands: [source,shell] .... # sysrc jail_enable="YES" # sysrc jail_parallel_start="YES" .... [TIP] ==== With `jail_parallel_start`, all configured jails will be started in the background. ==== [[jails-networking]] === Networking Networking for FreeBSD jails can be configured several different ways: Host Networking Mode (IP Sharing):: In host networking mode, a jail shares the same networking stack as the host system. When a jail is created in host networking mode it uses the same network interface and IP address. This means that the jail doesn't have a separate IP address, and its network traffic is associated with the host's IP. Virtual Networks (VNET):: Virtual Networks are a feature of FreeBSD jails that offer more advanced and flexible networking solutions than a basic networking mode like host networking. VNET allows the creation of isolated network stacks for each jail, providing them with their own separate IP addresses, routing tables, and network interfaces. This offers a higher level of network isolation and allows jails to function as if they are running on separate virtual machines. The netgraph system:: man:netgraph[4] is a versatile kernel framework for creating custom network configurations. It can be used to define how network traffic flows between jails and the host system and between different jails. [[host-configuration-directories]] === Setting Up the Jail Directory Tree There is no specific place to put the files for the jails. Some administrators use [.filename]#/jail#, others [.filename]#/usr/jail#, and still others [.filename]#/usr/local/jails#. In this chapter [.filename]#/usr/local/jails# will be used. Apart from [.filename]#/usr/local/jails# other directories will be created: * [.filename]#media# will contain the compressed files of the downloaded userlands. * [.filename]#templates# will contain the templates when using Thin Jails. * [.filename]#containers# will contain the jails. When using OpenZFS, execute the following commands to create datasets for these directories: [source,shell] .... # zfs create -o mountpoint=/usr/local/jails zroot/jails # zfs create zroot/jails/media # zfs create zroot/jails/templates # zfs create zroot/jails/containers .... [TIP] ==== In this case, `zroot` was used for the parent dataset, but other datasets could have been used. ==== When using UFS, execute the following commands to create the directories: [source,shell] .... # mkdir /usr/local/jails/ # mkdir /usr/local/jails/media # mkdir /usr/local/jails/templates # mkdir /usr/local/jails/containers .... [[jail-configuration-files]] === Jail Configuration Files There are two ways to configure jails. The first one is to add an entry for each jail to the file [.filename]#/etc/jail.conf#. The other option is to create a file for each jail in the directory [.filename]#/etc/jail.conf.d/#. In case a host system has few jails, an entry for each jail can be added in the file [.filename]#/etc/jail.conf#. If the host system has many jails, it is a good idea to have one configuration file for each jail in the [.filename]#/etc/jail.conf.d/# directory. The files in [.filename]#/etc/jail.conf.d/# must have `.conf` as their extension and have to be included in [.filename]#/etc/jail.conf#: [.programlisting] .... .include "/etc/jail.conf.d/*.conf"; .... A typical jail entry would look like this: [.programlisting] .... jailname { <.> # STARTUP/LOGGING exec.start = "/bin/sh /etc/rc"; <.> exec.stop = "/bin/sh /etc/rc.shutdown"; <.> exec.consolelog = "/var/log/jail_console_${name}.log"; <.> # PERMISSIONS allow.raw_sockets; <.> exec.clean; <.> mount.devfs; <.> # HOSTNAME/PATH host.hostname = "${name}"; <.> path = "/usr/local/jails/containers/${name}"; <.> # NETWORK ip4.addr = 192.168.1.151; <.> ip6.addr = ::ffff:c0a8:197 <.> interface = em0; <.> } .... <.> `jailname` - Name of the jail. <.> `exec.start` - Command(s) to run in the jail environment when a jail is created. A typical command to run is "/bin/sh /etc/rc". <.> `exec.stop` - Command(s) to run in the jail environment before a jail is removed. A typical command to run is "/bin/sh /etc/rc.shutdown". <.> `exec.consolelog` - A file to direct command output (stdout and stderr) to. <.> `allow.raw_sockets` - Allow creating raw sockets inside the jail. Setting this parameter allows utilities like man:ping[8] and man:traceroute[8] to operate inside the jail. <.> `exec.clean` - Run commands in a clean environment. <.> `mount.devfs` - Mount a man:devfs[5] filesystem on the chrooted [.filename]#/dev# directory, and apply the ruleset in the devfs_ruleset parameter to restrict the devices visible inside the jail. <.> `host.hostname` - The hostname of the jail. <.> `path` - The directory which is to be the root of the jail. Any commands that are run inside the jail, either by jail or from man:jexec[8], are run from this directory. <.> `ip4.addr` - IPv4 address. There are two configuration possibilities for IPv4. The first is to establish an IP or a list of IPs as has been done in the example. The other is to use `ip4` instead and set the `inherit` value to inherit the host's IP address. <.> `ip6.addr` - IPv6 address. There are two configuration possibilities for IPv6. The first is to establish an IP or a list of IPs as has been done in the example. The other is to use `ip6` instead and set the `inherit` value to inherit the host's IP address. <.> `interface` - A network interface to add the jail's IP addresses. Usually the host interface. More information about configuration variables can be found in man:jail[8] and man:jail.conf[5]. [[classic-jail]] == Classic Jail (Thick Jail) These jails resemble a real FreeBSD system. They can be managed more or less like a normal host system and updated independently. [[creating-classic-jail]] === Creating a Classic Jail In principle, a jail only needs a hostname, a root directory, an IP address, and a userland. The userland for the jail can be obtained from the official FreeBSD download servers. Execute the following command to download the userland: [source,shell] .... # fetch https://download.freebsd.org/ftp/releases/amd64/amd64/13.2-RELEASE/base.txz -o /usr/local/jails/media/13.2-RELEASE-base.txz .... Once the download is complete, it will be necessary to extract the contents into the jail directory. Execute the following commands to extract the userland into the jail's directory: [source,shell] .... # mkdir -p /usr/local/jails/containers/classic # tar -xf /usr/local/jails/media/13.2-RELEASE-base.txz -C /usr/local/jails/containers/classic --unlink .... With the userland extracted in the jail directory, it will be necessary to copy the timezone and DNS server files: [source,shell] .... # cp /etc/resolv.conf /usr/local/jails/containers/classic/etc/resolv.conf # cp /etc/localtime /usr/local/jails/containers/classic/etc/localtime .... With the files copied, the next thing to do is update to the latest patch level by executing the following command: [source,shell] .... # freebsd-update -b /usr/local/jails/containers/classic/ fetch install .... The last step is to configure the jail. It will be necessary to add an entry to the configuration file [.filename]#/etc/jail.conf# or in [.filename]#jail.conf.d# with the parameters of the jail. An example would be the following: [.programlisting] .... classic { # STARTUP/LOGGING exec.start = "/bin/sh /etc/rc"; exec.stop = "/bin/sh /etc/rc.shutdown"; exec.consolelog = "/var/log/jail_console_${name}.log"; # PERMISSIONS allow.raw_sockets; exec.clean; mount.devfs; # HOSTNAME/PATH host.hostname = "${name}"; path = "/usr/local/jails/containers/${name}"; # NETWORK ip4.addr = 192.168.1.151; interface = em0; } .... Execute the following command to start the jail: [source,shell] .... # service jail start classic .... More information on how to manage jails can be found in the section <>. [[thin-jail]] == Thin Jails Although Thin Jails use the same technology as Thick Jails, the creation procedure is different. Thin jails can be created using OpenZFS snapshots or using templates and NullFS. The use of OpenZFS snapshots and templates using NullFS have certain advantages over classic jails, such as being able to create them faster from snapshots or being able to update multiple jails using NullFS. [[creating-thin-jail-openzfs-snapshots]] === Creating a Thin Jail Using OpenZFS Snapshots Due to the good integration between FreeBSD and OpenZFS it is very easy to create new Thin Jails using OpenZFS Snapshots. To create a Thin Jail using OpenZFS Snapshots the first step is to create a template. Templates will only be used to create new jails. For this reason they are created in "read-only" mode so that jails are created with an immutable base. To create the dataset for the template, execute the following command: [source,shell] .... # zfs create -p zroot/jails/templates/13.2-RELEASE .... Then execute the following command to download the userland: [source,shell] .... # fetch https://download.freebsd.org/ftp/releases/amd64/amd64/13.2-RELEASE/base.txz -o /usr/local/jails/media/13.2-RELEASE-base.txz .... Once the download is complete, it will be necessary to extract the contents in the template directory by executing the following command: [source,shell] .... # tar -xf /usr/local/jails/media/13.2-RELEASE-base.txz -C /usr/local/jails/templates/13.2-RELEASE --unlink .... With the userland extracted in the templates directory, it will be necessary to copy the timezone and DNS server files to the template directory by executing the following command: [source,shell] .... # cp /etc/resolv.conf /usr/local/jails/templates/13.2-RELEASE/etc/resolv.conf # cp /etc/localtime /usr/local/jails/templates/13.2-RELEASE/etc/localtime .... The next thing to do is update to the latest patch level by executing the following command: [source,shell] .... # freebsd-update -b /usr/local/jails/templates/13.2-RELEASE/ fetch install .... Once the update is finished, the template is ready. To create an OpenZFS Snapshot from the template, execute the following command: [source,shell] .... # zfs snapshot zroot/jails/templates/13.2-RELEASE@base .... Once the OpenZFS Snapshot has been created, infinite jails can be created using the OpenZFS clone function. To create a Thin Jail named `thinjail`, execute the following command: [source,shell] .... # zfs clone zroot/jails/templates/13.2-RELEASE@base zroot/jails/containers/thinjail .... The last step is to configure the jail. It will be necessary to add an entry to the configuration file [.filename]#/etc/jail.conf# or in [.filename]#jail.conf.d# with the parameters of the jail. An example would be the following: [.programlisting] .... thinjail { # STARTUP/LOGGING exec.start = "/bin/sh /etc/rc"; exec.stop = "/bin/sh /etc/rc.shutdown"; exec.consolelog = "/var/log/jail_console_${name}.log"; # PERMISSIONS allow.raw_sockets; exec.clean; mount.devfs; # HOSTNAME/PATH host.hostname = "${name}"; path = "/usr/local/jails/containers/${name}"; # NETWORK ip4 = inherit; interface = em0; } .... Execute the following command to start the jail: [source,shell] .... # service jail start thinjail .... More information on how to manage jails can be found in the section <>. [[creating-thin-jail-nullfs]] === Creating a Thin Jail Using NullFS A jail can be created with reduced duplication of system files by using the Thin Jail technique and using NullFS to selectively share specific directories from the host system into the jail. The first step is to create the dataset to save the template, execute the following command if using OpenZFS: [source,shell] .... # zfs create -p zroot/jails/templates/13.2-RELEASE-base .... Or this one if using UFS: [source,shell] .... # mkdir /usr/local/jails/templates/13.2-RELEASE-base .... Then execute the following command to download the userland: [source,shell] .... # fetch https://download.freebsd.org/ftp/releases/amd64/amd64/13.2-RELEASE/base.txz -o /usr/local/jails/media/13.2-RELEASE-base.txz .... Once the download is complete, it will be necessary to extract the contents in the template directory by executing the following command: [source,shell] .... # tar -xf /usr/local/jails/media/13.2-RELEASE-base.txz -C /usr/local/jails/templates/13.2-RELEASE-base --unlink .... Once the userland is extracted in the templates directory, it will be necessary to copy the timezone and DNS server files to the template directory by executing the following command: [source,shell] .... # cp /etc/resolv.conf /usr/local/jails/templates/13.2-RELEASE-base/etc/resolv.conf # cp /etc/localtime /usr/local/jails/templates/13.2-RELEASE-base/etc/localtime .... With the files moved to the template, the next thing to do is update to the latest patch level by executing the following command: [source,shell] .... # freebsd-update -b /usr/local/jails/templates/13.2-RELEASE-base/ fetch install .... In addition to the base template, it is also necessary to create a directory where the `skeleton` will be located. Some directories will be copied from the template to the `skeleton`. Execute the following command to create the dataset for the `skeleton` in case of using OpenZFS: [source,shell] .... # zfs create -p zroot/jails/templates/13.2-RELEASE-skeleton .... Or this one in case of using UFS: [source,shell] .... # mkdir /usr/local/jails/templates/13.2-RELEASE-skeleton .... Then create the `skeleton` directories. The `skeleton` directories will hold the local directories of the jails. Execute the following commands to create the directories: [source,shell] .... # mkdir -p /usr/local/jails/templates/13.2-RELEASE-skeleton/home # mkdir -p /usr/local/jails/templates/13.2-RELEASE-skeleton/usr # mv /usr/local/jails/templates/13.2-RELEASE-base/etc /usr/local/jails/templates/13.2-RELEASE-skeleton/etc # mv /usr/local/jails/templates/13.2-RELEASE-base/usr/local /usr/local/jails/templates/13.2-RELEASE-skeleton/usr/local # mv /usr/local/jails/templates/13.2-RELEASE-base/tmp /usr/local/jails/templates/13.2-RELEASE-skeleton/tmp # mv /usr/local/jails/templates/13.2-RELEASE-base/var /usr/local/jails/templates/13.2-RELEASE-skeleton/var # mv /usr/local/jails/templates/13.2-RELEASE-base/root /usr/local/jails/templates/13.2-RELEASE-skeleton/root .... The next step is to create the symlinks to the `skeleton` by executing the following commands: [source,shell] .... # cd /usr/local/jails/templates/13.2-RELEASE-base/ # mkdir skeleton # ln -s skeleton/etc etc # ln -s skeleton/home home # ln -s skeleton/root root # ln -s ../skeleton/usr/local usr/local # ln -s skeleton/tmp tmp # ln -s skeleton/var var .... With the `skeleton` ready, it will be necessary to copy the data to the jail directory. In case of using OpenZFS, OpenZFS snapshots can be used to easily create as many jails as necessary by executing the following commands: [source,shell] .... # zfs snapshot zroot/jails/templates/13.2-RELEASE-skeleton@base # zfs clone zroot/jails/templates/13.2-RELEASE-skeleton@base zroot/jails/containers/thinjail .... In case of using UFS the man:cp[1] program can be used by executing the following commands: [source,shell] .... # mkdir /usr/local/jails/containers/thinjail # cp -R /usr/local/jails/templates/13.2-RELEASE-skeleton /usr/local/jails/containers/thinjail .... Then create the directory in which the base template and the skeleton will be mounted: [source,shell] .... # mkdir -p /usr/local/jails/thinjail-nullfs-base .... Add a jail entry in [.filename]#/etc/jail.conf# or a file in [.filename]#jail.conf.d# as follows: [.programlisting] .... thinjail { # STARTUP/LOGGING exec.start = "/bin/sh /etc/rc"; exec.stop = "/bin/sh /etc/rc.shutdown"; exec.consolelog = "/var/log/jail_console_${name}.log"; # PERMISSIONS allow.raw_sockets; exec.clean; mount.devfs; # HOSTNAME/PATH host.hostname = "${name}"; path = "/usr/local/jails/${name}-nullfs-base"; # NETWORK ip4.addr = 192.168.1.153; interface = em0; # MOUNT mount.fstab = "/usr/local/jails/${name}-nullfs-base.fstab"; } .... Then the create the [.filename]#/usr/local/jails/thinjail-nullfs-base.fstab# file as follows: [.programlisting] .... /usr/local/jails/templates/13.2-RELEASE-base /usr/local/jails/thinjail-nullfs-base/ nullfs ro 0 0 /usr/local/jails/containers/thinjail /usr/local/jails/thinjail-nullfs-base/skeleton nullfs rw 0 0 .... Execute the following command to start the jail: [source,shell] .... # service jail start thinjail .... [[creating-vnet-jail]] === Creating a VNET Jail FreeBSD VNET Jails have their own distinct networking stack, including interfaces, IP addresses, routing tables, and firewall rules. The first step to create a VNET jail is to create the man:bridge[4] by executing the following command: [source,shell] .... # ifconfig bridge create .... The output should be similar to the following: [.programlisting] .... bridge0 .... With the `bridge` created, it will be necessary to attach it to the `em0` interface by executing the following command: [source,shell] .... # ifconfig bridge0 addm em0 .... To make this setting persist across reboots, add the following lines to [.filename]#/etc/rc.conf#: [.programlisting] .... defaultrouter="192.168.1.1" cloned_interfaces="bridge0" ifconfig_bridge0="inet 192.168.1.150/24 addm em0 up" .... The next step is to create the jail as indicated above. Either the <> procedure and the <> procedure can be used. The only thing that will change is the configuration in the [.filename]#/etc/jail.conf# file. The path [.filename]#/usr/local/jails/containers/vnet# will be used as an example for the created jail. The following is an example configuration for a VNET jail: [.programlisting] .... vnet { # STARTUP/LOGGING exec.start = "/bin/sh /etc/rc"; exec.stop = "/bin/sh /etc/rc.shutdown"; exec.consolelog = "/var/log/jail_console_${name}.log"; # PERMISSIONS allow.raw_sockets; exec.clean; mount.devfs; devfs_ruleset = 5; # PATH/HOSTNAME path = "/usr/local/jails/containers/${name}"; host.hostname = "${name}"; # VNET/VIMAGE vnet; vnet.interface = "${epair}b"; # NETWORKS/INTERFACES $id = "154"; <.> $ip = "192.168.1.${id}/24"; $gateway = "192.168.1.1"; $bridge = "bridge0"; <.> $epair = "epair${id}"; # ADD TO bridge INTERFACE exec.prestart += "ifconfig ${epair} create up"; exec.prestart += "ifconfig ${epair}a up descr jail:${name}"; exec.prestart += "ifconfig ${bridge} addm ${epair}a up"; exec.start += "ifconfig ${epair}b ${ip} up"; exec.start += "route add default ${gateway}"; exec.poststop = "ifconfig ${bridge} deletem ${epair}a"; exec.poststop += "ifconfig ${epair}a destroy"; } .... <.> Represents the IP of the Jail, it must be *unique*. <.> Refers to the bridge created previously. [[creating-linux-jail]] === Creating a Linux Jail FreeBSD can run Linux inside a jail using crossref:linuxemu[linuxemu,Linux Binary Compatibility] and man:debootstrap[8]. Jails do not have a kernel. They run on the host's kernel. Therefore it is necessary to enable Linux Binary Compatibility in the host system. To enable the Linux ABI at boot time, execute the following command: [source,shell] .... # sysrc linux_enable="YES" .... Once enabled, it can be started without rebooting by executing the following command: [source,shell] .... # service linux start .... The next step will be to create a jail as indicated above, for example in <>, but *without* performing the configuration. FreeBSD Linux jails require a specific configuration that will be detailed below. Once the jail has been created as explained above, execute the following command to perform required configuration for the jail and start it: [source,shell] .... # jail -cm \ name=ubuntu \ host.hostname="ubuntu.example.com" \ path="/usr/local/jails/ubuntu" \ interface="em0" \ ip4.addr="192.168.1.150" \ exec.start="/bin/sh /etc/rc" \ exec.stop="/bin/sh /etc/rc.shutdown" \ mount.devfs \ devfs_ruleset=4 \ allow.mount \ allow.mount.devfs \ allow.mount.fdescfs \ allow.mount.procfs \ allow.mount.linprocfs \ allow.mount.linsysfs \ allow.mount.tmpfs \ enforce_statfs=1 .... To access the jail, it will be necessary to install package:sysutils/debootstrap[]. Execute the following command to access the FreeBSD Linux jail: [source,shell] .... # jexec -u root ubuntu .... Inside the jail, execute the following commands to install package:sysutils/debootstrap[] and prepare the Ubuntu environment: [source,shell] .... # pkg install debootstrap # debootstrap jammy /compat/ubuntu .... When the process has finished and the message `Base system installed successfully` is displayed on the console, it will be necessary to stop the jail from the host system by executing the following command: [source,shell] .... # service jail onestop ubuntu .... Then add an entry in [.filename]#/etc/jail.conf# for the Linux jail: [.programlisting] .... ubuntu { # STARTUP/LOGGING exec.start = "/bin/sh /etc/rc"; exec.stop = "/bin/sh /etc/rc.shutdown"; exec.consolelog = "/var/log/jail_console_${name}.log"; # PERMISSIONS allow.raw_sockets; exec.clean; mount.devfs; devfs_ruleset=4; # HOSTNAME/PATH host.hostname = "${name}"; path = "/usr/local/jails/containers/${name}"; # NETWORK ip4.addr = 192.168.1.155; interface = em0; # MOUNT mount += "devfs $path/compat/ubuntu/dev devfs rw 0 0"; mount += "tmpfs $path/compat/ubuntu/dev/shm tmpfs rw,size=1g,mode=1777 0 0"; mount += "fdescfs $path/compat/ubuntu/dev/fd fdescfs rw,linrdlnk 0 0"; mount += "linprocfs $path/compat/ubuntu/proc linprocfs rw 0 0"; mount += "linsysfs $path/compat/ubuntu/sys linsysfs rw 0 0"; mount += "/tmp $path/compat/ubuntu/tmp nullfs rw 0 0"; mount += "/home $path/compat/ubuntu/home nullfs rw 0 0"; } .... Then the jail can be started as usual with the following command: [source,shell] .... # service jail start ubuntu .... The Ubuntu environment can be accessed using the following command: [source,shell] .... # jexec ubuntu chroot /compat/ubuntu /bin/bash .... More information can be found in the chapter crossref:linuxemu[linuxemu,Linux Binary Compatibility]. +[[service-jails-config]] +=== Configuring Service Jails + +A service jail is configured completely via [.filename]#/etc/rc.conf# or man:sysrc[8]. +The base system services are service jails ready. +They contain a config line which enables networking or lift other restrictions of jails. +Base system services which do not make sense to run inside jails are configured to not be started as a service jail, even if enabled in [.filename]#/etc/rc.conf#. +Some examples of such a service are services which want to mount or unmount something in the start of stop method, or only configure something like a route, or firewall, or the like. + +Third party services may or may not be service jails ready. To check if a service is service jail ready, the following command can be used: + +[source,shell] +.... +# grep _svcj_options /path/to/rc.d/servicename +.... + +If there is no output, the service is not service jail ready, or does not need any additional privileges like e.g. network access. + +If the service is not service jail ready, and needs network access, it can be made ready by adding the necessary config to [.filename]#/etc/rc.conf#: + +[source,shell] +.... +# sysrc servicename_svcj_options=net_basic +.... + +For all possible `_svcj_options` see the man:rc.conf[5] man-page. + +To enable a service jail for a given service, the service needs to be stopped and the `servicename_svcj` variable needs to be set to YES. +To put man:syslogd[8] into a service jail, use the following sequence of commands: + +[source,shell] +.... +# service syslogd stop +# sysrc syslogd_svcj=YES +# service syslogd start +.... + +If the `servicename_svcj` variable is changed, the service needs to be stopped before it is changed. +If it is not stopped, the rc framework will not detect the correct state of the service and will not be able to do what is requested. + +Service jails are managed only via man:rc.conf[5]/man:sysrc[8] and the man:service[8] command. +The jail utilities, like man:jls[8] as described in crossref:jails[jail-management,Jail Management] can be used to investigate the operation, but the man:jail[8] command is not supposed to be used to manage them. + [[jail-management]] == Jail Management Once the jail is created, there are a number of operations that can be performed, like starting, rebooting or deleting the jail, installing software in it, etc. In this section the different actions that can be done with jails from the host will be described. [[list-running-jails]] === List Running Jails To list the jails that are running on the host system, the command man:jls[8] can be used: [source,shell] .... # jls .... The output should be similar to the following: .... JID IP Address Hostname Path 1 192.168.250.70 classic /usr/local/jails/containers/classic .... man:jls[8] supports the `--libxo` argument, which through the man:libxo[3] library allows other types of formats to be displayed, such as `JSON`, `HTML`, etc. For example, execute the following command to get the `JSON` output: [source,shell] .... # jls --libxo=json .... The output should be similar to the following: .... {"__version": "2", "jail-information": {"jail": [{"jid":1,"ipv4":"192.168.250.70","hostname":"classic","path":"/usr/local/jails/containers/classic"}]}} .... [[start-jail]] === Start, Restart, and Stop a Jail man:service[8] is used to start, reboot, or stop a jail on the host. For example, to start a jail, run the following command: [source,shell] .... # service jail start jailname .... Change the `start` argument to `restart` or `stop` to perform other actions on the jail. [[destroy-jail]] === Destroy a Jail Destroying a jail is not as simple as stopping the jail using man:service[8] and removing the jail directory and [.filename]#/etc/jail.conf# entry. FreeBSD takes system security very seriously. For this reason there are certain files that not even the root user can delete. This functionality is known as File Flags. The first step is to stop the desired jail executing the following command: [source,shell] .... # service jail stop jailname .... The second step is to remove these flags with man:chflags[1] by executing the following command, in which `classic` is the name of the jail to remove: [source,shell] .... # chflags -R 0 /usr/local/jails/classic .... The third step is to delete the directory where the jail was: [source,shell] .... # rm -rf /usr/local/jails/classic .... Finally, it will be necessary to remove the jail entry in [.filename]#/etc/jail.conf# or in [.filename]#jail.conf.d#. [[handle-packages-jail]] === Handle Packages in a Jail The man:pkg[8] tool supports the `-j` argument in order to handle packages installed inside the jail. For example, to install package:nginx-lite[] in the jail, the next command can be executed *from the host*: [source,shell] .... # pkg -j classic install nginx-lite .... For more information on working with packages in FreeBSD, see crossref:ports[ports,"Installing Applications: Packages and Ports"]. [[access-jail]] === Access a Jail While it has been stated above that it is best to manage jails from the host system, a jail can be entered with man:jexec[8]. The jail can be entered by running man:jexec[8] from the host: [source,shell] .... # jexec -u root jailname .... When gaining access to the jail, the message configured in man:motd[5] will be displayed. [[execute-commands-jail]] === Execute Commands in a Jail To execute a command from the host system in a jail the man:jexec[8] can be used. For example, to stop a service that is running inside a jail, the command will be executed: [source,shell] .... # jexec -l jailname service nginx stop .... [[jail-upgrading]] == Jail Upgrading Upgrading FreeBSD Jails ensures that the isolated environments remain secure, up-to-date, and in line with the latest features and improvements available in the FreeBSD ecosystem. [[jails-updating]] === Upgrading a Classic Jail or a Thin Jail using OpenZFS Snapshots Jails *must be updated from the host* operating system. The default behavior in FreeBSD is to disallow the use of man:chflags[1] in a jail. This will prevent the update of some files so updating from within the jail will fail. To update the jail to the latest patch release of the version of FreeBSD it is running, execute the following commands on the host: [source,shell] .... # freebsd-update -j classic fetch install # service jail restart classic .... To upgrade the jail to a new major or minor version, first upgrade the host system as described in crossref:cutting-edge[freebsdupdate-upgrade,"Performing Major and Minor Version Upgrades"]. Once the host has been upgraded and rebooted, the jail can then be upgraded. [TIP] ==== In case of upgrade from one version to another, it is easier to create a new jail than to upgrade completely. ==== For example to upgrade from 13.1-RELEASE to 13.2-RELEASE, execute the following commands on the host: [source,shell] .... # freebsd-update -j classic -r 13.2-RELEASE upgrade # freebsd-update -j classic install # service jail restart classic # freebsd-update -j classic install # service jail restart classic .... [NOTE] ==== It is necessary to execute the `install` step two times. The first one upgrades the kernel, and the second one upgrades the rest of the components. ==== Then, if it was a major version upgrade, reinstall all installed packages and restart the jail again. This is required because the ABI version changes when upgrading between major versions of FreeBSD. From the host: [source,shell] .... # pkg -j jailname upgrade -f # service jail restart jailname .... [[upgrading-thin-jail]] === Upgrading a Thin Jail Using NullFS Since Thin Jails that use NullFS share the majority of system directories, they are very easy to update. It is enough to update the template. This allows updating multiple jails at the same time. To update the template to the latest patch release of the version of FreeBSD it is running, execute the following commands on the host: [source,shell] .... # freebsd-update -b /usr/local/jails/templates/13.1-RELEASE-base/ fetch install # service jail restart .... To upgrade the template to a new major or minor version, first upgrade the host system as described in crossref:cutting-edge[freebsdupdate-upgrade,"Performing Major and Minor Version Upgrades"]. Once the host has been upgraded and rebooted, the template can then be upgraded. For example, to upgrade from 13.1-RELEASE to 13.2-RELEASE, execute the following commands on the host: [source,shell] .... # freebsd-update -b /usr/local/jails/templates/13.1-RELEASE-base/ -r 13.2-RELEASE upgrade # freebsd-update -b /usr/local/jails/templates/13.1-RELEASE-base/ install # service jail restart # freebsd-update -b /usr/local/jails/templates/13.1-RELEASE-base/ install # service jail restart .... [[jail-resource-limits]] == Jail Resource Limits Controlling the resources that a jail uses from the host system is a task to be taken into account by the system administrator. man:rctl[8] allows you to manage the resources that a jail can use from the host system. [TIP] ==== The `kern.racct.enable` tunable must be enabled at [.filename]#/boot/loader.conf#. ==== The syntax to limit the resources of a jail is as follows: [.programlisting] .... rctl -a jail::resource:action=amount/percentage .... For example, to limit the maximum RAM that a jail can access, run the following command: [source,shell] .... # rctl -a jail:classic:memoryuse:deny=2G .... To make the limitation persistent across reboots of the host system, it will be necessary to add the rule to the [.filename]#/etc/rctl.conf# file as follows: [.programlisting] .... jail:classic:memoryuse:deny=2G/jail .... More information on resource limits can be found in the security chapter in the crossref:security[security-resourcelimits,"Resource Limits section"]. [[jail-managers-and-containers]] == Jail Managers and Containers As previously explained, each type of FreeBSD Jail can be created and configured manually, but FreeBSD also has third-party utilities to make configuration and administration easier. Below is an incomplete list of the different FreeBSD Jail managers: .Jail Managers [options="header", cols="1,1,1,1"] |=== | Name | License | Package | Documentation | BastilleBSD | BSD-3 | package:sysutils/bastille[] | link:https://bastille.readthedocs.io/en/latest/[Documentation] | pot | BSD-3 | package:sysutils/pot[] | link:https://pot.pizzamig.dev/[Documentation] | cbsd | BSD-2 | package:sysutils/cbsd[] | link:https://www.bsdstore.ru/en/docs.html[Documentation] | AppJail | BSD-3 | package:sysutils/appjail[], for devel package:sysutils/appjail-devel[] | link:https://github.com/DtxdF/AppJail#getting-started[Documentation] | iocage | BSD-2 | package:sysutils/iocage[] | link:https://iocage.readthedocs.io/en/latest/[Documentation] | ezjail | link:https://erdgeist.org/beerware.html[Beer Ware] | package:sysutils/ezjail[] | link:https://erdgeist.org/arts/software/ezjail/[Documentation] |===