The Puppet Labs Issue Tracker has Moved: https://tickets.puppetlabs.com

« Previous - Version 2/9 (diff) - Next » - Current version
Anonymous, 03/13/2010 08:01 pm


Running Puppet on FreeBSD

Password Management

Puppet uses ruby-shadow to manage passwords on Linux/Unix. FreeBSD (and also OpenBSD or NetBSD) does not support a full implementation of shadow passwords. As shadow.h on *BSD is missing some required features you cannot manage passwords for user resources on *BSD.

Using an alternative package site

If you use an internal package server and want to use the ‘freebsd’ provider (which literally runs “pkg_add -r <name>”), you will need to set the PACKAGESITE environment variable. The only way I found to do this without hacking the source or other installed files is by using the rc subsystem. puppetd’s rc script sources /etc/rc.subr, which at some point sources /etc/rc.conf![.local] to find the puppetd_* variables. You can use this to set your PACKAGESITE variable. Just put the following into /etc/rc.conf![.local] and restart puppetd (via the rc script):

export PACKAGESITE=“http://<server>/All/”

Beginning puppet.conf

On some earlier versions of FreeBSD, puppetd-devel 0.23.2 is shipped without a sample [puppetd] section in puppet.conf. Here’s a sample puppet.conf to get folk up and running quickly (this isn’t a FreeBSD specific config and is deliberately minimal to force you to explore and create your own site-specific config).

[puppetd]
  server = puppetmaster.local
  runinterval = 3000
  listen = true
  splay = false
  summarize = true

FreeBSD packages

If you have a local package server or don’t care about package customization, use the puppet package manager, otherwise avoid using it and see the tips/tricks in the next section.

### Basic way to invoke a port installation
class freebsd {
  package { portaudit: ensure => present, provider => freebsd }
}

portsnap

Here’s a complete class for using portsnap(1) on your system. If you have a different filesystem layout, you can set the following variables (only set these if they’re different):

  • $freebsd::portsdir – path to /usr/ports
  • $portsnap_bin – path to /usr/sbin/portsnap
  • $portsnap_conf – path to /etc/portsnap.conf
  • $portsnap_flags – Sets “-p $portsdir -d $portsnapdir”
  • $portsnapdir – path to /var/db/portsnap

    class freebsd::portsnap { $portsdir = $freebsd::portsdir $portsnap_conf = $portsnap_conf ? { ‘’ => ‘/etc/portsnap.conf’, default => $portsnap_conf } $portsnapdir = $portsnapdir ? { ‘’ => ‘/var/db/portsnap’, default => $portsnapdir } $portsnap_bin = $portsnap_bin ? { ‘’ => ‘/usr/sbin/portsnap’, default => $portsnap_bin } $portsnap_flags = $portsnap_flags ? { ‘’ => “-d \”$portsnapdir\“ -f \”$portsnap_conf\“ -p \”$portsdir\“”, default => $portsnap_flags }

    $portsnap = “$portsnap_bin $portsnap_flags”

    file {

    "$_portsnapdir":
      path => "$_portsnapdir",
      ensure => directory,
      owner => root,
      group => wheel,
      mode => 755;
    
    "$_portsnap_conf":
      path => "$_portsnap_conf",
      owner => root,
      group => wheel,
      mode => 444,
      source => "${puppet_url}/dist/$_portsnap_conf";
    

    }

    exec {

    "portsnap cron":
      command => "$__portsnap cron",
      require => [ File["$_portsnap_conf"], File["$_portsnapdir"] ],
      timeout => 7200,
      schedule => maint;
    
    "portsnap fetch":
      # Use 'cron' here and not 'fetch': same result, but this defeats the interactivity test
      # pkill(1) gets around the sleep call in cron
      command => "(/bin/sleep 15 && /bin/pkill -n sleep) & ($__portsnap cron; exit 0)",
      before => Exec['portsnap cron'],
      require => [ File["$_portsnap_conf"], File["$_portsnapdir"] ],
      timeout => 7200,
      onlyif => "/bin/test `/bin/ls -1 \"$_portsnapdir\" | /usr/bin/wc -l` -eq 0";
    
    "portsnap extract":
      command => "$__portsnap extract",
      require => [ File["$_portsdir"], Exec['portsnap fetch'] ],
      timeout => 3600,
      onlyif => "/bin/test `/bin/ls -1 \"$_portsdir\" | /usr/bin/wc -l` -eq 0";
    
    "portsnap update":
      command => "$__portsnap update",
      require => [ File["$_portsdir"], Exec['portsnap cron'] ],
      schedule => maint,
      timeout => 3600,
      onlyif => "/bin/sh -c '/bin/test `/usr/bin/find \"$_portsnapdir/files\" -mtime -1 -type f | /usr/bin/head -1 | /usr/bin/wc -l` -eq 1'";
    
    } }

    EXAMPLE USAGE

    #

    class freebsd {

    $_portsdir = $portsdir ? { ‘’ => ‘/usr/ports’, default => $portsdir }

    }

    #

    node ‘freebsd.local’ {

    include freebsd

    include freebsd::portsnap

    }

ports tips and tricks

  1. Set BATCH=yes and sync out /var/db/ports/[portname]/options to set basic port configurations.
  2. If you like per-host specific options, avoid using the built in packages mechanism.
  3. Install ports-mgmt/portupgrade and sync out $PREFIX/etc/pkgtools.conf
  4. Create macros/defines that handle your package management routines such as:

    schedule { maint:
      range => "4 - 6",
      period => daily,
      repeat => 1
    }
    
    define pkg_version() {
      exec { "pkg_version":
        command => "/usr/sbin/pkg_version -v -l'<' > /usr/ports/version.$fqdn",
        schedule => "maint"
      }
    }
    
  5. Be sure to check your logs and ensure that you’re not hammering FreeBSD project resources (ftp.freebsd.org) every five minutes!

Useful defines

line() macro with sed

FreeBSD doesn’t ship with perl(1) by default (and the crowd rejoices with much happiness!), however it does have sed(1) by default, which is much lighter weight and better suited for the task of adding/removing lines, especially when you prefer to run bare metal machines with minimal cruft and dependencies from ports. If you want to run with extended POSIX regexps, throw in a ‘-E’ in with BOTH the sed(1) and grep(1) args in both of the exec sections in the line define.

define line($file, $line, $ensure = 'present') {
  case $ensure {
    default: { err ( "unknown ensure value ${ensure}" ) }
    present: {
      exec {
        "/bin/echo '${line}' >> '${file}'":
         unless => "/usr/bin/grep -qFx '${line}' '${file}'"
      }
    }
    absent: {
      exec {
        "/usr/bin/sed -i '' -e '/^${line}\$/d' '${file}'":
          onlyif => "/usr/bin/grep -qFx '${line}' '${file}'"
      }
    }
  }
}

define shell_config()

FreeBSD can tweak most of its behavior through shell configuration scripts (eg: /etc/rc.conf*, /etc/sysctl.conf, and /etc/periodic.conf), which turns out to be very convenient. The below shell_config macro takes care of most of the heavy lifting for the bits below.

define shell_config($file, $key, $value, $ensure = 'present') {
  case $ensure {
    default: { err ( "unknown ensure value ${ensure}" ) }
    present: {
      exec {
        "shell_config_unique_$ensure '$file$key'":
          unless => "/bin/test `/usr/bin/grep -cE '^[ \t]*$key=' -- $file` -le 1",
          command => "/usr/bin/sed -i '' -e '/$key=\".*\"/d' $file";
        "shell_config_create_$ensure '$file$key'":
          unless => "/usr/bin/grep -qE '^[ \t]*$key=' -- $file",
          command => "/usr/bin/printf '%s=\"%s\"\n' '$key' '$value' >> '${file}'";
        "shell_config_update_$ensure '$file$key'":
          unless => "/usr/bin/grep -qE '^[ \t]*$key=\"$value\"' -- $file",
          command => "/usr/bin/sed -i '' -e 's/$key=\".*\"/$key=\"$value\"/' $file";
      }
    }
    absent: {
      exec {  "shell_config_delete_$ensure $file$key":
          onlyif => "/usr/bin/grep -qE '^[ \t]*$key=' -- $file",
          command => "/usr/bin/sed -i '' -e '/$key=\".*\"/d' $file";
      }
    }
  }
}

$name fun and rc_conf_local/periodic_conf

Instead of calling line() or shell_config() manually, let’s wrap shell_config() using the $name variable to create something substantially more elegant and clean:

define periodic_conf($value) {
  shell_config {  "periodic_conf_${name}":
    file => '/etc/periodic.conf',
    key => $name,
    value => $value
  }
}

define rc_conf_local($value) {
  shell_config { "rc_conf_local_${name}":
    file => "/etc/rc.conf.local",
    key => $name,
    value => $value;
  }
}

### EXAMPLE
#
#periodic_conf {
#  daily_show_badconfig: value => YES;
#  daily_clean_tmps_enable: value => YES;
#  weekly_noid_enable: value => YES;
#  weekly_status_pkg_enable: value => YES;
#}
#
#rc_conf_local {
#  inetd_flags: value => "-wW -a $ipaddress";
#}

ports_conf

Here’s a base template for showing the start to managing a ports/pkg installed version of puppetd on FreeBSD. Note the dependency on the directory /etc/rc.conf.d/ existing.

define ports_conf($key, $value) {
  shell_config {
    "port_${name}_rc_conf_${key}":
      file => "/etc/rc.conf.d/${name}",
      key => $key,
      value => $value;
  }
}

### EXAMPLE
#
#node 'freebsd.local' {
#  include freebsd-mtree
#  include ports-puppet
#}
#
## Only needed to create /etc/rc.conf.d:
#class freebsd-mtree {
#  file {
#    "/etc/rc.conf.d":
#      ensure => directory.
#      owner => root,
#      group => wheel,
#      mode => 755;
#  }
#}
#
#class ports-puppet {
#...
#  file { "/usr/local/etc/puppet/puppet.conf":
#    alias => "puppet.conf",
#    path => "/usr/local/etc/puppet/puppet.conf",
#    owner => root,
#    group => wheel,
#    mode => 444,
#    source => "...";
#  }
#
#  exec { "puppetd-restart":
#    command => "/usr/local/etc/rc.d/puppetd restart",
#    subscribe => File["puppetd.conf"],
#    refreshonly => true,
#  }
#
#  ports_conf {
#    puppetd:       key => puppetd_enable,       value => YES;
#    puppetmasterd: key => puppetmasterd_enable, value => YES;
#  }
#}