Table des matières

Configurer les automatisations de Puppet

CréationNicolas THOREZ 2022/11/20 17:37

Architecture et composants

L'ensemble des règles de Puppet est contenu dans le dossier /etc/puppetlabs/code/environments. A ce premier niveau on trouvera les différents environnements. production est créé par défaut mais il est possible d'en ajouter d'autres, preprod, test, etc…

Au sein des dossiers d'environnement, on trouvera notamment :

Lorsqu'un agent se connecte, le serveur puppet compile l'ensemble des fichiers *.pp de l'environnement sélectionné et informe l'agent des règles qui le concerne et par conséquent, des actions à réaliser. Parmis ces fichiers, le plus important est le fichier site.pp qui correspond au fichier général pour l'ensemble des clients.

Le langage Puppet

Le langage utilisé par puppet permet de décrire l'état dans lequel au souhaite mettre le système. On parlera alors de :

La syntaxe utilisée est généralement du type :

ressource { 'Titre':
  paramètre1 => 'valeur',
  paramètre2 => 'valeur',
}

Au delà de ces déclarations de ressources, puppet inclue des éléments de codage afin de dynamiser les manifestes. Parmi ces éléments, on a droit à :

Déclaration de variables

$variable = 'valeur'

La variable est nommée et utilisé par le biais du symbole $.

Condition à test unitaire (if)

if $trusted['hostname'] == 'serv1' {
  include class1
} elsif $trusted['hostname'] == 'serv2' {
  include class2
} else {
  include class3
}

Le test est introduit par if. Si le résultat du test est correct alors la ressource suivante est envoyée. Sinon, une nouvelle condition peut être introduite par elsif. Si aucune condition n'est valide, le terme else permet d'envoyer une ressource spécifique.

Condition à test multiple (case)

case $trusted['hostname'] {
  'serv1': {
    include class1
  }
  'serv2': {
    include class2
  }
  default: {
    include class3
  }
}

case introduit la condition à vérifier. Cette dernière est alors comparée à chacune des propositions disponibles et, à chaque correspondance, exécute la ressource qui y est attachée. Le terme default permet de définir une réponse par défaut.

Condition négative (unless)

unless $trusted['hostname'] == 'serv1' {
  include class4
}

unless permet d'appliquer un résultat à chaque fois que la condition est fausse. Ainsi, dans cet exemple, la classe n°4 sera inclue sur chaque serveur à l'exception de serv1.

Sélecteur (?)

 $servfunction = $trusted['hostname'] ? {
  'serv1' => 'dns',
  'serv2' => 'apache2',
  default => 'none',
}

Le sélecteur fonctionne de la même manière que case à la différence qu'au lieu de fournir des ressources à exécuter, il définie une valeur. il est introduit par le symbole ? suivant la condition à évaluer.

Hiérarchie

Il est possible de définir une hiérarchie aux niveaux des ressources afin de s'assurer la mise en place de dépendances. On aura donc le choix parmi :

Type de ressources les plus fréquents

exec

Permet l'exécution d'une commande spécifique.

exec { 'HelloWorld':                                    # Nom de la ressource
  command => 'echo "Hello World!"',                     # Commande à exécuter
  cwd     => '/tmp',                                    # Dossier à partir duquel exécuter la commande
  path    => ['/bin/', '/usr/bin', '/usr/sbin',],       # Dossiers dans lesquels chercher la commande à exécuter
}

file

Pour la création, modification suppression d'un fichier.

file { '/home/nekan/test.txt':                                             # Chemin du fichier
  content => template('file/test.txt'),                                    # Emplacement de la source
  ensure  => 'present',                                                    # 'present' pour la création et 'absent' pour la suppression
  mode    => '0755',                                                       # Droits du fichier (chmod)
  owner   => 'nekan',                                                      # Propriétaire du fichier (chown)
  group   => 'adm',                                                        # Groupe du fichier (chgrp)
}

file_line

Pour l'ajout, la modification ou la suppression d'une ligne dans un fichier.

file_line { 'puppet_test':                                                 # Nom de la ligne (arbitraire)
  line    => 'ligne = ligne de test',                                      # contenu de la ligne tel qu'elle doit être dans le fichier
  match   => '^ligne',                                                     # 'match' permet de vérifier la ligne à modifier par le biais d'une regex 
  after   => '^# Logs',                                                    # recherche la ligne à modifier après une ligne spécifié par la regex
  path    => '/etc/nagios/nrpe.d/check_debian.cfg',                        # Chemin du fichier à modifier
  notify  => Service['nagios-nrpe-server'],                                # Service à notifier (redémarrer, recharger) après modification
}

package

Pour gérer les paquets (installation, suppression, …)

package { 'openssl':               # Paquet à gérer
  ensure => 'present',             # A installer si absent
  mark   => 'hold',                # Verrouiller la version (empêche les mises à jour)
}

service

Permet de gérer un service (démarrage, arrêt, redémarrage, rechargement, etc.)

service { 'apache2':                     # Service à gérer
  ensure => 'running',                   # Le service doit être en cours d'exécution
  enable => 'true',                      # Le service doit être en démarrage automatique
}

ssh_authorized_key

Permet de gérer les fichiers authorized_keys (contenant les clés SSH publiques ainsi que leurs options)

ssh_authorized_key { '/home/nekan/.ssh/authorized_keys':       # Gestion du fichier ~/.ssh/authorized_keys
  ensure  => 'present',                                        # present : création si absent | absent : suppression si présent
  user    => 'nekan',                                          # Définition du l'utilisateur lié à la clé
  type    => 'ssh-rsa',                                        # Type de la clé à ajouter
  key     => 'AAAAB3NzaC[...]Z6q17zF4sW1WvU=',                 # Clé à ajouter
}

user

Permet de gérer les utilisateurs (création, suppression, etc)

user { 'nekan':                                      # Création de l'utilisateur
  ensure     => present,                             # present : création si absent | absent : suppression si présent        
  shell      => "/bin/bash",                         # Le shell qu'utilisera l'utilisateur
  password   => '$6$/odkSIo4.[...]YW/lpsg1',         # Le mot de passe de l'utilisateur en version sécurisée
  groups     => ['sudo'],                            # La lise de groupes que doit intégrer l'utilisateur 
  managehome => yes,                                 # Création du dossier home
}

Les classes

Les classes sont des déclarations de ressources introduites par le terme class. Une fois déclarée, la classe peut être ajoutée à l'exécution par les termes :

A l'intérieur de la classe, on trouvera une succession de déclarations d'autres ressources, le but étant de gérer toutes ces ressources en une seule fois.

class apache {
  package {'apache2':
    ensure => present
    before => File['/etc/apache2/apache2.conf'],
  }
  file {'/etc/apache2/apache2.conf':
    ensure  => file,
    owner   => 'www-data',
    content => file('apache/apache2.conf'),
  }
  service {'apache2':
    ensure    => running,
    enable    => true,
    subscribe => File['/etc/apache2/apache2.conf'],
  }
}

Les nœuds

Les nœuds ne sont rien d'autres que les clients du serveur puppet. Ils sont définis par le mot-clé node, suivi du nom du client et de l'ensemble des modules et classes qui doivent s'appliquer à ce dernier. Exemple :

node 'test1' { include class1, class2, class3 }
node 'test2' {
  include class1
  include class4
  include class5
}

Les templates

Les templates sont des fichiers particuliers dont l'extension est *.epp (pour les versions puppet pures) ou *.erb (pour les versions ruby). La différence principale avec les fichiers de ressources est que les templates embarquent du code dans leurs contenus permettant une personnalisation du résultat final, là où les fichiers sont statiques. Les templates sont enregistrés dans le sous-dossier du même nom dans les dossiers des modules auxquels ils appartiennent. En résumé :

Balises

Le code intégré dans un template est basiquement le même que dans les fichiers de ressources. Cependant, afin de spécifier ce qui est interprétable de ce qui ne l'est pas, chaque élément de code est balisé :

Balise Explication Exemple Retour
Expression interprétable
<%= Ouvre une expression interprétable. Le contenu sera donc exécuté et son résultat renvoyé comme valeur définitive.
<%= $facts[hostname] %>
serv1
%> Ferme l'expression interprétable.
-%> Ferme l'expression interprétable en supprimant les espaces ou retour charriot présents.
Expression non interprétable
<% Ouvre une expression non interprétable. Le contenu ne sera donc exécuté et renvoyé tel quel comme valeur définitive.
<% if $facts[hostname] == 'serv1' { -%>
if $facts[hostname] == 'serv1' {
<%- Ouvre une expression non interprétable et supprime l'indentation.
%> Ferme l'expression non interprétable.
-%> Ferme l'expression non interprétable en supprimant les espaces ou retour charriot présents.
Paramètres
<% | Ouvre la déclaration des paramètres. Ces derniers sont sous la forme type $nom = 'valeur par défaut'.
<% | String $service_state = 'running',
     Boolean $service_enable = true
| %>
<%- | Ouvre la déclaration des paramètres en supprimant l'indentation.
| %> Ferme la déclaration des paramètres.
| -%> Ferme la déclaration des paramètres en supprimant les espaces ou retour charriot présents.
Commentaires
<%# Ouvre un commentaire.
<%# Commentaire %>
# Commentaire
%> Ferme le commentaire.
-%> Ferme le commentaire en supprimant les espaces ou retour charriot présents.

Exemple de template :

# File Managed by Puppet
# Module apache2, Template virtualhost.epp

<%- | String $Domain = undef,
      String $Website = undef,
      String $DocumentRoot = undef,
      Optional[String] $Alias = undef
| -%>

<VirtualHost *:80>
    ServerAdmin webmaster@<%= $Domain %>
    ServerName <%= $Website %>
    
    RewriteEngine On
    RewriteCond %{SERVER_NAME} =<%= $Domain %>
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<VirtualHost *:443>
    ServerAdmin webmaster@<%= $Domain %>
    DocumentRoot <%= $DocumentRoot %>
    ServerName <%= $Website %>
<% if $Alias != "" -%>
    ServerAlias <%= $Alias %>
<% end -%>
    ErrorLog  <%= scope.lookupvar('apache::log_dir')%>/<%= $Website %>-error_log
    CustomLog <%= scope.lookupvar('apache::log_dir')%>/<%= $Website %>-access_log combined
    
    SSLCertificateFile /etc/letsencrypt/live/<%= $Website %>/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/<%= $Website %>/privkey.pem
    Include /etc/letsencrypt/options-ssl-apache.conf
    
    Header always set Strict-Transport-Security "max-age=63072000"
    Header always set X-Content-Type-Options nosniff
    
    Header always set Strict-Transport-Security "max-age=15552000; includeSubDomains"
</VirtualHost>

Une fois créé, le template peut être appelé via le terme epp :

file { '/etc/apache2/sites-available/www.shyrkasystem.com.conf':
  ensure => file,
  content => epp('apache2/virtualhost.epp', {
    'Domain'       => 'shyrkasystem.com',
    'Website'      => 'www.shyrkasystem.com',
    'Alias'        => 'web.shyrkasystem.com',
    'DocumentRoot' => '/var/www/html'
    }
  )
}