Monday, 24 October 2016

Quickstart: Installing and configuring puppet on CentOS 7 / RHEL

For the puppet master we will need a VM with at least 8GB of RAM, 80GB of disk and 2 vCPU.

The topology will comprise of two nodes - MASTERNODE (The puppet server) and the CLIENTNODE (the puppet client).

Firstly we should ensure that NTP is configured on both the client and server.

We'll now install the official Puppet repository:

sudo rpm -Uvh https://yum.puppetlabs.com/puppet5/puppet5-release-el-7.noarch.rpm
yum install puppetserver puppetdb puppetdb-termini
sudo systemctl enable puppet
sudo systemctl start puppetserver

We should then set our DNS name etc. for the puppet server - append / change the following in vi /etc/puppetlabs/puppet/puppet.conf:

[main]
certname = puppetmaster01.example.com
server = puppetmaster01.example.com
environment = production
runinterval = 1h
strict_variables = true

[master]
dns_alt_names = puppetmaster01,puppetdb,puppet,puppet.example.com
reports = puppetdb
storeconfigs_backend = puppetdb
storeconfigs = true
environment_timeout = unlimited

We will also need to ensure the PuppetDB service is started - although we'll firstly need to install / setup PostgreSQL before we proceed - follow the guidance here  - however stop just before the 'user creation' and instead see below:

sudo -u postgres sh
createuser -DRSP puppetdb
createdb -E UTF8 -O puppetdb puppetdb
exit

and ensure the pg_trgm extension is installed:

sudo -u postgres sh
psql puppetdb -c 'create extension pg_trgm'
exit

Restart postgres and ensure you can login:

sudo service postgresql restart
psql -h localhost puppetdb puppetdb
\q

And define the database connection details here:

vi /etc/puppetlabs/puppetdb/conf.d/database.ini

Replacing / adding the following directives:

[database]
classname = org.postgresql.Driver
subprotocol = postgresql
subname = //127.0.0.1:5432/puppetdb
username = puppetdb
password = <yourpassword>

Note: Also ensure that you are using PostgreSQL version >=9.6 otherwise the puppetdb service will fail to start. (as the epel release is at current only on 9.2) Uninstall the existing postgres install and install the newer version with: yum install postgresql-96 postgresql-server-96 postgresql-contrib-96

Important: By default the puppet master will attempt to connect ot PuppetDB via the hostname 'puppetdb' - however we can change this behaviour by defining the following on the puppet master:

vi /etc/puppetlabs/puppet/puppetdb.conf

and adding:

[main]
server_urls = https://puppetmaster01.example.com:8081

sudo service puppetdb start

Configure ssl support with:

sudo puppetdb ssl-setup

Now either use puppet to start and ensure that the db service runs on boot with:

sudo puppet resource service puppetdb ensure=running enable=true

or

sudo systemctl enable puppetdb
sudo systemctl start puppetdb

We will proceed by generating the server certificates:

export PATH=/opt/puppetlabs/bin:$PATH
sudo puppet master --verbose --no-daemonize

Once you see 'Notice: Starting Puppet master version 5.2.0' pres Ctrl + C to escape.

We can review certificates that have been created by issuing:

sudo puppet cert list -all

and start the puppet master:

sudo service puppet start

We'll also need to add an exception in for TCP/8140 and TCP/8081 (PuppetDB) (for clients to communicate with the puppet master):

sudo iptables -I INPUT 3 -i eth0 -p tcp -m state  --state NEW,ESTABLISHED -m tcp --dport 8140 -j ACCEPT
sudo iptables -I INPUT 3 -i eth0 -p tcp -m state  --state NEW,ESTABLISHED -m tcp --dport 8081 -j ACCEPT

sudo iptables-save > /etc/sysconfig/iptables

Puppet Client Installation


we should then install our client:

sudo rpm -Uvh https://yum.puppetlabs.com/puppet5/puppet5-release-el-7.noarch.rpm
sudo yum install puppet
sudo systemctl enable puppet

edit puppet.conf:

[main]
certname = agent01.example.com
server = puppetmaster01.example.com
environment = production
runinterval = 1h

and restart the puppet client:

systemctl restart puppet

Set path details:

export PATH=/opt/puppetlabs/bin:$PATH

The puppet server (master) utilizes PKI to ensure authenticity between itself and the client - so we must firstly generate a certificate signing request from the client:

puppet agent --enable
puppet agent -t

At this point I got an an error:

Error: Could not request certificate: Error 400 on SERVER: The environment must be purely alphanumeric, not 'puppet-ca'
Exiting; failed to retrieve certificate and waitforcert is disabled

This turned out due to a version mismatch between the puppet client and server.

Note: The Puppet server version must always be >= than that of the puppet client - I actually ended up removing the official puppet repo from the client and using the EPEL repo instead.

and then attempt to enable puppet and generate our certificate:

puppet agent --enable
puppet agent -t

At this point I got the following error:

Exiting; no certificate found and waitforcert is disabled.

This is because the generated certificate has not yet been approved by the puppet master!

In order to approve the certificate - on the puppet master issue:

puppet cert list

and then sign it by issuing:

puppet cert sign hostname.domain.com

We can then view the signed certificate with:

puppet cert list -all

Now head back to the client and attempt to initialise the puppet agent again:

puppet agent -t

However again - I got the following message:

Could not retrieve catalog from remote server: Error 500 on SERVER

Note: Using the following command allows you to run the puppet server in the foreground and provided a lot of help when debugging the above errors:

puppet master --no-daemonize --debug

We should (if everything goes to plan) see something like:

Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Caching catalog for puppetmaster.yourdomain.com
Info: Applying configuration version '1234567890'
Info: Creating state file /var/lib/puppet/state/state.yaml
Notice: Finished catalog run in 0.02 seconds

We want to install few modules firstly:

puppet module install ghoneycutt/ssh

We will extend our modules:

vi /etc/puppet/modules/firewall/manifests/ssh.pp

ssh::permit_root_login
  permit_root_login => 'no',

Now lets create our manifest:

vi /etc/puppetlabs/code/environments/production/manifests/site.pp

import "/opt/puppetlabs/puppet/modules/firewall/manifests/*.pp"

node default {

  package { tcpdump: ensure => installed; }
  package { nano: ensure => installed; }
  package { wget: ensure => installed; }
  package { firewalld: ensure => absent; }

  service { 'firewalld':
    ensure     => stopped,
    enable     => false,
    hasstatus  => true,
  }

  service { 'iptables':
    ensure     => started,
    enable     => true,
    hasstatus  => true,
  }

  resources { "firewall":
    purge   => true
  }

  Firewall {
    before  => Class['fw::post'],
    require => Class['fw::pre'],
  }

  class { ['fw::pre', 'fw::post']: }

  include common
  include ssh

}

We have two sets of rules - one of which will be applied first:

mkdir -p /opt/puppetlabs/puppet/modules/firewall/manifests

vi /opt/puppetlabs/puppet/modules/firewall/manifests/pre.pp

class fw::pre {

  Firewall {
    require => undef,
  }

  # basic in/out
  firewall { "000 accept all icmp":
    chain    => 'INPUT',
    proto    => 'icmp',
    action   => 'accept',
  }

  firewall { '001 accept all to lo interface':
    chain    => 'INPUT',
    proto    => 'all',
    iniface  => 'lo',
    action   => 'accept',
  }

  firewall { '006 Allow inbound SSH':
    dport     => 22,
    proto    => tcp,
    action   => accept,
  }

  firewall { '003 accept related established rules':
    chain    => 'INPUT',
    proto    => 'all',
    state    => ['RELATED', 'ESTABLISHED'],
    action   => 'accept',
  }

  firewall { '004 accept related established rules':
    chain    => 'OUTPUT',
    proto    => 'all',
    state    => ['RELATED', 'ESTABLISHED'],
    action   => 'accept',
  }

  firewall { '005 allow all outgoing traffic':
    chain    => 'OUTPUT',
    state    => ['NEW','RELATED','ESTABLISHED'],
    proto    => 'all',
    action   => 'accept',
  }

}

and afterwards:

vi /opt/puppetlabs/puppet/modules/firewall/manifests/post.pp

class fw::post {

  firewall { '900 log dropped input chain':
    chain      => 'INPUT',
    jump       => 'LOG',
    log_level  => '6',
    log_prefix => '[IPTABLES INPUT] dropped ',
    proto      => 'all',
    before     => undef,
  }

  firewall { '900 log dropped forward chain':
    chain      => 'FORWARD',
    jump       => 'LOG',
    log_level  => '6',
    log_prefix => '[IPTABLES FORWARD] dropped ',
    proto      => 'all',
    before     => undef,
  }

  firewall { '900 log dropped output chain':
    chain      => 'OUTPUT',
    jump       => 'LOG',
    log_level  => '6',
    log_prefix => '[IPTABLES OUTPUT] dropped ',
    proto      => 'all',
    before     => undef,
  }

  firewall { "910 deny all other input requests":
    chain      => 'INPUT',
    action     => 'drop',
    proto      => 'all',
    before     => undef,
  }

  firewall { "910 deny all other forward requests":
    chain      => 'FORWARD',
    action     => 'drop',
    proto      => 'all',
    before     => undef,
  }

  firewall { "910 deny all other output requests":
    chain      => 'OUTPUT',
    action     => 'drop',
    proto      => 'all',
    before     => undef,
  }
}


We should also validate the file as follows:

sudo puppet parser validate site.pp

The puppet client (by default) will poll every 30 minutes - we can change this by defining:

runinterval=900

Where 900 is == number of seconds. (This should be appended to the 'main' section in puppet.conf

We can also test the config by issuing:

puppet agent --test

We should see something like:

Info: Retrieving pluginfacts
Info: Retrieving plugin
...
Notice: /Stage[main]/Main/Node[default]/Package[tcpdump]/ensure: created
Notice: /Stage[main]/Main/Node[default]/Package[nano]/ensure: created
Notice: Finished catalog run in 6.88 seconds

Now we want to define some host-specific configuration - lets say our host runs a bespoke application running on tcp/8000:

node 'webserver.yourdomain.com' {
  firewall { '006 Allow inbound SSH':
  dport     => 22,
  proto    => tcp,
  action   => accept,
}

Finally run a test again to ensure the firewall rule has been executed:

puppet agent --test

iptables -L

0 comments:

Post a Comment