HOWTO: Bootstrap the Puppet Master Server

This HOWTO installs Puppet version 3.7.1 (at the time of this writing) on to a single vanilla Enterprise Linux 6 system.

Note

The packages referred to in this document are available in the Puppet add-on channel to Kolab Enterprise 14.

If you do not have any Kolab Enterprise 14 configuration on the system yet, please see the guide for installation-guide-rhel-6-enterprise-14 for the steps involved.

Important

Bootstrapping the Puppet master server is the hardest thing to do in relation to running Puppet for your environments.

This HOWTO lists a large number of steps that need to be executed manually, but only need to be executed once.

Configure the System FQDN and Service DNS Entry

Make sure that the system’s FQDN is a sustainable value. We recommend using puppet$x.$domain, for example puppet01.example.org.

The digits are in there, so that node manifests can match the system’s common name against a node manifest for puppet\d+\.example\.org.

The FQDN of the system has to resolve back to the actual system’s primary external interface’s IP address.

To double-check whether this is the case, execute the following commands:

# python -c 'import socket; print socket.getfqdn();'
puppet01.example.org
# dig +short puppet01.example.org
13.14.15.16
# host 13.14.15.16
16.15.14.13.in-addr.arpa domain name pointer puppet01.example.org.

The service DNS entry is a default puppet.example.org. Tinkering with this is obviously allowed, but not recommended.

If you may end up with multiple Puppet master servers, make sure that the puppet.example.org DNS entry is a (collection of) IN A resource record(s), possibly pointing to service IP addresses rather than system IP addresses.

Configure the Puppet Add-on YUM Repository

At the time of this writing, there is not a so-called -release package for the add-on repository for Puppet.

Put the following YUM repository configuration in /etc/yum.repos.d/kolab-14-extras-puppet.repo:

# cat > /etc/yum.repos.d/kolab-14-extras-puppet.repo << EOF
[kolab-14-extras-puppet]
name = Enterprise Linux 6 Puppet Packages for Kolab Enterprise 14
baseurl = https://mirror.kolabsys.com/redhat/kolab-14/el6/extras-puppet/\$basearch
enabled = 1
priority = 60
gpgcheck = 1
gpgkey = https://mirror.kolabsys.com/santiago.asc
sslcacert = /etc/pki/tls/certs/mirror.kolabsys.com.ca.cert
sslverify = True
sslclientcert = /etc/pki/tls/private/mirror.kolabsys.com.client.pem

[kolab-14-extras-puppet-debuginfo]
name = Enterprise Linux 6 Puppet Packages for Kolab Enterprise 14 - Debug
baseurl = https://mirror.kolabsys.com/redhat/kolab-14/el6/extras-puppet/\$basearch/debug
enabled = 0
priority = 60
gpgcheck = 1
gpgkey = https://mirror.kolabsys.com/santiago.asc
sslcacert = /etc/pki/tls/certs/mirror.kolabsys.com.ca.cert
sslverify = True
sslclientcert = /etc/pki/tls/private/mirror.kolabsys.com.client.pem

[kolab-14-extras-puppet-source]
name = Enterprise Linux 6 Puppet Packages for Kolab Enterprise 14 - Sources
baseurl = https://mirror.kolabsys.com/redhat/kolab-14/el6/extras-puppet/SRPMS
enabled = 0
priority = 60
gpgcheck = 1
gpgkey = https://mirror.kolabsys.com/santiago.asc
sslcacert = /etc/pki/tls/certs/mirror.kolabsys.com.ca.cert
sslverify = True
sslclientcert = /etc/pki/tls/private/mirror.kolabsys.com.client.pem
EOF

Install and Configure the Puppet Master Server Software

Execute the following command to install the necessary software:

# yum -y install git puppet-server redhat-lsb \
    mod_passenger mod_ssl puppetdb puppet-dashboard mysql-server

In case you have started from a base Enterprise Linux 6 installation, which is not a bad idea at all, ensure the following packages are up-to-date:

# yum -y update apr apr-util mod_nss openssl

Next, adjust /etc/puppet/puppet.conf to include configuration for the master, adding the following section:

# cat >> /etc/puppet/puppet.conf << EOF
[master]
    ca = true
    certname = puppet.$(hostname -d)
    dns_alt_names = puppet
    ssl_client_header = SSL_CLIENT_S_DN
    ssl_client_verify_header = SSL_CLIENT_VERIFY
    autosign = false
    reports = store
    environmentpath = \$vardir/environments/
    basemodulepath = /etc/puppet/modules:/usr/share/puppet/modules:/var/lib/puppet/modules
    parser = future
EOF

Run the following command once to generate the certificates necessary to run the Puppet master server under the Apache HTTP server with Phusion Passenger:

# timeout 10s puppet master --verbose --no-daemonize

At this point, executing httpd -t should succeed without errors or warnings.

The fileserver configuration needs adjusting, too:

# cat > /etc/puppet/fileserver.conf << EOF
[private]
    path /var/lib/puppet/private/%d/
    allow *

[files]
    path /var/lib/puppet/files/%d/
    allow *
EOF

Next, create or adjust /etc/httpd/conf.d/puppet-server.conf to contain the following:

# cat > /etc/httpd/conf.d/puppet-server.conf << EOF
Listen *:8140

<VirtualHost *:8140>
    ServerAdmin sysadmin-main@$(hostname -d)
    ServerName puppet.$(hostname -d)

    ErrorLog "|/usr/sbin/rotatelogs -L /var/log/httpd/puppet.$(hostname -d)-error_log -f -l /var/log/httpd/puppet.$(hostname -d)-error_log.%Y-%m-%d 86400"
    CustomLog "|/usr/sbin/rotatelogs -L /var/log/httpd/puppet.$(hostname -d)-access_log -f -l /var/log/httpd/puppet.$(hostname -d)-access_log.%Y-%m-%d 86400" combined
    CustomLog "|/usr/sbin/rotatelogs -L /var/log/httpd/puppet.$(hostname -d)-ssl_log -f -l /var/log/httpd/puppet.$(hostname -d)-ssl_log.%Y-%m-%d 86400" "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

    DocumentRoot /usr/share/puppet/rack/puppetmasterd/public/

    SSLEngine on
    SSLCipherSuite SSLv2:-LOW:-EXPORT:RC4+RSA
    SSLCertificateFile      /var/lib/puppet/ssl/certs/puppet.$(hostname -d).pem
    SSLCertificateKeyFile   /var/lib/puppet/ssl/private_keys/puppet.$(hostname -d).pem
    SSLCertificateChainFile /var/lib/puppet/ssl/ca/ca_crt.pem
    SSLCACertificateFile    /var/lib/puppet/ssl/ca/ca_crt.pem
    SSLCARevocationFile     /var/lib/puppet/ssl/ca/ca_crl.pem
    SSLCARevocationCheck    chain

    SSLVerifyClient optional
    SSLVerifyDepth  1
    SSLOptions +StdEnvVars

    # The following client headers allow the same configuration to work with Pound.
    RequestHeader set X-SSL-Subject %{SSL_CLIENT_S_DN}e
    RequestHeader set X-Client-DN %{SSL_CLIENT_S_DN}e
    RequestHeader set X-Client-Verify %{SSL_CLIENT_VERIFY}e

    <Directory /usr/share/puppet/rack/puppetmasterd/>
        Options None
        AllowOverride None
        <ifModule mod_authz_core.c>
            Require all granted
        </ifModule>
        <ifModule !mod_authz_core.c>
            Order Allow,Deny
            Allow from All
        </ifModule>
    </Directory>

</VirtualHost>
EOF

And restart the Apache HTTP server:

# service httpd restart

Create the aforementioned DocumentRoot:

# mkdir -p /usr/share/puppet/rack/puppetmasterd/{public,tmp}
# cp /usr/share/puppet/ext/rack/config.ru \
    /usr/share/puppet/rack/puppetmasterd/
# chown puppet:puppet /usr/share/puppet/rack/puppetmasterd/config.ru

Initialize the Management Repositories

Initialize the central GIT repositories for the domain name space to manage, on the Puppet master server:

# mkdir -p /git/
# mkdir -p /git/$(hostname -d)
# cd /git/$(hostname -d)
# git --bare init --shared
# sed -i -e 's/master/development/g' HEAD

# mkdir -p /git/$(hostname -d)-sensitive
# cd /git/$(hostname -d)-sensitive
# git --bare init --shared

To distinguish between permissions to each repository, optionally add the necessary groups:

# groupadd sysadmin-puppet
# groupadd sysadmin-main
# chown -R root:sysadmin-puppet /git/$(hostname -d)
# find /git/$(hostname -d) -type f -exec chmod g+w,o-r {} ;
# find /git/$(hostname -d) -type d -exec chmod g+ws,o-rx {} ;

# chown -R root:sysadmin-main /git/$(hostname -d)-sensitive
# find /git/$(hostname -d)-sensitive -type f -exec chmod g+w,o-r {} ;
# find /git/$(hostname -d)-sensitive -type d -exec chmod g+ws,o-rx {} ;

And add users to said groups:

# useradd john
# gpasswd -a john sysadmin-main
# gpasswd -a john sysadmin-puppet

# useradd jane
# gpasswd -a jane sysadmin-puppet

See also

  • delegation-for-domain-manifests
  • hook-the-puppet-master-server-up-to-ldap

Provide Puppet with Something To Do

From a workstation (that is presumably in the same domain name space), clone the (still empty) repositories:

$ git clone ssh://puppet.$(hostname -d)/git/$(hostname -d)
$ git clone ssh://puppet.$(hostname -d)/git/$(hostname -d)-sensitive

And provide the initial populus:

$ cd $(hostname -d)
$ mkdir -p puppet/manifests/{classes,nodes,utils}
$ hostname=PUPPET_SERVER_HOSTNAME
$ cat > puppet/site.pp << EOF
# Get facts and give them a good, good name
\$os = \$operatingsystem
\$server = "puppet.$(hostname -d)"

case \$os {
    "Fedora", "CentOS", "RedHat": {
        \$osver = \$lsbdistrelease
        \$osmajorver = \$lsbmajdistrelease
    }
    "Debian", "Ubuntu": {
        \$osver = \$lsbdistrelease
        \$osmajorver = \$lsbmajdistrelease
    }
    "SuSE": {
    }
    "openSuSE": {
    }
    "Darwin": {
        \$osver = \$operatingsystemrelease
    }
}

case \$environment {
    nil: {
        \$environment = "development"
    }
}

# Always include the puppet::client class
include puppet::client

node default {
}
EOF
$ cat > puppet/puppetdb.conf << EOF
[main]
server = $hostname
port = 8081
EOF
$ cat > puppet/manifests/classes/$(echo $(hostname -d) | sed -e 's/./_/g')_common.pp << EOF
class $(echo $(hostname -d) | sed -e 's/./_/g')_common {
    include yum::standard

    yum::repository { [
            "kolab-14-release",
            "kolab-14-updates",
            "kolab-14-extras-puppet"
        ]:
        enable => true,
        gpgkey => true
    }

    yum::repository { [
            "kolab-14-updates-testing"
        ]:
        enable => \$environment ? {
                "production" => false,
                default => true
            },
        gpgkey => true
    }

    yum::repository { [
            "kolab-14-development"
        ]:
        enable => \$environment ? {
                "development" => true,
                default => false
            },
        gpgkey => true
    }
}
EOF
$ cat > puppet/manifests/nodes/$(hostname -f).pp << EOF
node '$(hostname -f)' {
    include $(echo $(hostname -d) | sed -e 's/./_/g')_common
    include puppet::server

    cron { "puppet-dashboard_reports:prune":
        command => "cd /usr/share/puppet-dashboard; rake RAILS_ENV=production reports:prune upto=28 unit=day >/dev/null 2>&1",
        hour => "*/1",
        minute => "15"
    }

    # From git::server
    file { "/usr/local/bin/git_init_script":
        owner => "root",
        group => "root",
        mode => 750,
        source => [
            "puppet://\$server/private/\$environment/git/git_init_script",
            "puppet://\$server/modules/files/git/git_init_script",
            "puppet://\$server/modules/git/git_init_script"
        ]
    }

    git::clone { "$(hostname -d)-sensitive":
        localtree => "/var/lib/puppet/files/",
        source => "/git/$(hostname -d)-sensitive/",
        real_name => "$(hostname -d)"
    }

    git::repository { [
            "$(hostname -d)",
            "$(hostname -d)-sensitive"
        ]:
        localtree => "/git/",
        symbolic_link => false,
        shared => true,
        public => false,
        owner => "root",
        group => "root",
        description => "Kolab Systems Puppet"
    }

    puppet::server::domain::development { [
            "$(hostname -d)"
        ]:
        base_url => "/git/"
    }

    puppet::server::domain::testing { [
            "$(hostname -d)"
        ]:
        base_url => "/git/"
    }

    puppet::server::domain::production { [
            "$(hostname -d)"
        ]:
        base_url => "/git/"
    }

    puppet::server::module::development { [
            "git",
            "puppet",
            "webserver",
            "yum"
        ]:
        base_url => "git://git.kolab.org/~vanmeeuwen/puppet/"
    }

    puppet::server::module::testing { [
            "git",
            "puppet",
            "webserver",
            "yum"
        ]:
        base_url => "git://git.kolab.org/~vanmeeuwen/puppet/"
    }

    puppet::server::module::production { [
            "git",
            "puppet",
            "webserver",
            "yum"
        ]:
        base_url => "git://git.kolab.org/~vanmeeuwen/puppet/"
    }
}
EOF
$ cat > puppet/manifests/utils/exec.pp << EOF
# Exec

Exec {
    logoutput => on_failure,
    loglevel => info,
    path => [
        "/bin",
        "/usr/bin",
        "/usr/local/bin",
        "/sbin",
        "/usr/sbin",
        "/usr/local/sbin"
    ]
}
EOF
$ cat > puppet/manifests/utils/file.pp << EOF
File {
    links => follow
}
EOF
$ mkdir -p webserver/includes.d/
$ cat > webserver/includes.d/listen.conf.$(hostname -s) << EOF
Listen 80
Listen 443
Listen 3000
Listen 8140
EOF

Add the files to track, commit the changes and push it back out:

$ git add .
$ git commit . -m "Initial commit"
$ git checkout -b development
$ git branch -D master
$ git push origin development
$ git push origin development:testing
$ git push origin development:production

Next, to setup PuppetDB, adjust /etc/puppet/puppetdb.conf:

# cat > /etc/puppet/puppetdb.conf << EOF
[main]
server = puppet.$(hostname -d)
port = 8081
EOF

Provide it the proper SSL certificates and CA from the Puppet installation:

# puppetdb ssl-setup
# service puppetdb start

Next, create the initial clones of the domain name space specific management repositories:

# mkdir -p /var/lib/puppet/private/$(hostname -d)/
# for environment in development testing production; do
    cd /var/lib/puppet/private/$(hostname -d)/
    git clone -b $environment /git/$(hostname -d)/ $environment
done

# for environment in development testing production; do
    mkdir -p /var/lib/puppet/environments/$environment/manifests
    cd /var/lib/puppet/environments/$environment/
    echo "modulepath = /var/lib/puppet/environments/$environment/modules:\$basemodulepath" > environment.conf
    cp -fa /var/lib/puppet/private/$(hostname -d)/$environment/puppet/site.pp .
    cp -fa /var/lib/puppet/private/$(hostname -d)/$environment/puppet/manifests .

    mkdir -p /var/lib/puppet/environments/$environment/modules/
    cd /var/lib/puppet/environments/$environment/modules/
    for module in git kolab munin nagios puppet webserver yum; do
        git clone -b $environment https://github.com/kolab-groupware/puppet-module-$module $module
    done
done

# mkdir -p /var/lib/puppet/files/
# cd /var/lib/puppet/files/
# git clone /git/$(hostname -d)-sensitive $(hostname -d)

Additionally, install the following two required modules from the Puppet Forge:

# puppet module install puppetlabs-stdlib
# puppet module install dalen-puppetdbquery
service mysqld start
mysql_secure_installation
cat > ~/.my.cnf << EOF
[client]
password=$PASSWORD
EOF
mysql -e "create database dashboard;"
mysql -e "grant all privileges on dashboard.* to 'dashboard'@'localhost' identified by 'asdasd';"
mysql -e "flush privileges;"
cat > /usr/share/puppet-dashboard/config/database.yml << EOF
production:
    database: dashboard
    username: dashboard
    password: asdasd
    encoding: utf8
    adapter: mysql

development:
    database: dashboard
    username: dashboard
    password: asdasd
    encoding: utf8
    adapter: mysql

test:
    database: dashboard
    username: dashboard
    password: asdasd
    encoding: utf8
    adapter: mysql
EOF
cd /usr/share/puppet-dashboard
rake db:migrate
rake gems:refresh_specs

Important

The dashboard report prune job does not eliminate records in other tables, and it is therefore necessary to add to the database schema:

ALTER TABLE resource_events ADD FOREIGN KEY (resource_status_id) REFERENCES resource_statuses(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE resource_statuses ADD FOREIGN KEY (report_id) REFERENCES reports(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE report_logs ADD FOREIGN KEY (report_id) REFERENCES reports(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE metrics ADD FOREIGN KEY (report_id) REFERENCES reports(id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE reports ADD FOREIGN KEY (node_id) REFERENCES nodes(id) ON UPDATE CASCADE ON DELETE CASCADE;

The server can now be made its own client, and start managing Puppet by Puppet:

# puppet agent --verbose --onetime --no-daemonize --server puppet.$(hostname -d) --environment development
# puppet ca sign $(hostname -f)
# puppet agent --verbose --onetime --no-daemonize --server puppet.$(hostname -d) --environment development

For yet undetermined reasons, the Puppet master server may hold a certificate signing request, yet be unable to sign the request (with a message “Could not find certificate request for”). Please see Re-initializing the CA Certificate and Peer Certificates for more information.

Enabling the Inventory Service in the Puppet Dashboard

# cat >> /etc/puppet/auth.conf << EOF
path /facts
auth yes
method find, search
allow dashboard
EOF
# sed -r -i \
    -e "s/^ca_server:.*$/ca_server: 'puppet.$(hostname -d)'/g" \
    -e "s/^enable_inventory_service:.*$/enable_inventory_service: true/g" \
    -e "s/^inventory_server:.*$/inventory_server: 'puppet.$(hostname -d)'/g" \
    -e "s/^use_file_bucket_diffs:.*$/use_file_bucket_diffs: true/g" \
    -e "s/^file_bucket_server:.*$/file_bucket_server: 'puppet.$(hostname -d)'/g" \
    /usr/share/puppet-dashboard/config/settings.yml
# cd /usr/share/puppet-dashboard/
# rake cert:create_key_pair
# rake cert:request
# puppet ca sign dashboard
# rake cert:retrieve

Re-initializing the CA Certificate and Peer Certificates

Warning

This process resets the CA, Puppet master server certificate, and all Puppet agent certificates.

On the Puppet master server, execute the following:

# service httpd stop
# rm -rf /var/lib/puppet/ssl/
# timeout 10s puppet master --verbose --no-daemonize
# puppetdb ssl-setup -f
# service puppetdb restart
# while [ -z "$(netstat -tlnp | grep :8081)" ]; do sleep 1; done
# service httpd start
# puppet agent --verbose --onetime --no-daemonize --server puppet.$(hostname -d) --environment development
# puppet ca sign $(hostname -f)
# puppet agent --verbose --onetime --no-daemonize --server puppet.$(hostname -d) --environment development