diff --git a/conf/etc/bash_completion.d/check_conf b/conf/etc/bash_completion.d/check_conf new file mode 100755 index 0000000..da8acf5 --- /dev/null +++ b/conf/etc/bash_completion.d/check_conf @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# !! GIT FILE !! +# https://framagit.org/zorval/scripts/check-nrpe + +_check_conf() { + local cur prev + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + if [[ "$cur" == -* ]]; then + if [[ "$prev" == --copy-repo-to-local ]] || [[ "$prev" == --copy-local-to-repo ]]; then + COMPREPLY=( $(compgen -W "--force" -- "$cur") ) + else + COMPREPLY=( $(compgen -W "--copy-repo-to-local --copy-local-to-repo" -- "$cur") ) + fi + return 0 + fi + + COMPREPLY=( $(compgen -W "--copy-repo-to-local --copy-local-to-repo --force" -- "$cur") ) + return 0 +} + +complete -F _check_conf check_conf diff --git a/conf/etc/nagios/nrpe.d/check_nrpe.cfg b/conf/etc/nagios/nrpe.d/check_nrpe.cfg index e2ceab4..f6b1b05 100644 --- a/conf/etc/nagios/nrpe.d/check_nrpe.cfg +++ b/conf/etc/nagios/nrpe.d/check_nrpe.cfg @@ -29,3 +29,4 @@ command[check_chrony] = /usr/local/bin/check_chrony command[check_reboot] = sudo /usr/sbin/needrestart -p -k command[check_restart] = sudo /usr/sbin/needrestart -p -l command[check_git] = sudo /usr/local/sbin/check_git +command[check_conf] = sudo /usr/local/sbin/check_conf diff --git a/conf/usr/local/sbin/check_conf b/conf/usr/local/sbin/check_conf new file mode 100755 index 0000000..781802a --- /dev/null +++ b/conf/usr/local/sbin/check_conf @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 + +################################################################################ +# BSD 3-Clause License +# +# Copyright © 2020-2021 Aurélien Grimal - aurelien.grimal@tech-tips.fr +# All rights reserved. +# +# Please see license file on root of this directory +################################################################################ + +# !! GIT FILE !! +# https://framagit.org/zorval/scripts/check-nrpe + +################################################################################ + +# Import des modules + +import sys + +try: + import hashlib, platform, os, shutil, argparse, subprocess as sp, re +except Exception as e: + print("[CRITICAL] Impossible d'importer les modules", e) + sys.exit(2) + +# Test si le script est bien lancé en root +if os.geteuid() != 0: + print("[CRITICAL] Il faut être root pour lancer cette commande") + sys.exit(2) + +# Variables + +config_dir = '/srv/git/conf' +common_config_dir = 'common' +wildcard = '__' +git_monitored_repos_file = '/etc/zorval/git-monitored-repos' + +try: + with open('/etc/hostname', 'r') as etc_hostname: + hostname = etc_hostname.read().rstrip() +except Exception: + print('[CRITICAL] Erreur lors de la lecture du fichier /etc/hostname') + +# Fonctions + +def hash_bytestr_iter(bytesiter, hasher, ashexstr=False): + for block in bytesiter: + hasher.update(block) + return hasher.hexdigest() if ashexstr else hasher.digest() + +def file_as_blockiter(afile, blocksize=65536): + with afile: + block = afile.read(blocksize) + while len(block) > 0: + yield block + block = afile.read(blocksize) + +def md5sum(file_path): + return hash_bytestr_iter(file_as_blockiter(open(file_path, 'rb')), hashlib.md5()) + +def walk_git_conf(directory, level=1): + directory = directory.rstrip(os.path.sep) + if not os.path.isdir(directory): + print("[CRITICAL] Le répertoire", directory, "n'existe pas") + sys.exit(2) + num_sep = directory.count(os.path.sep) + # Au moment du tri, on remplace '__' par '###' car dans l'ordre ASCII, le caractère _ est placé après les lettres + # Or pour l'application de la précédence, il doit se trouver avant + for root_abs, dirs, files in sorted(os.walk(directory), key=lambda d: d[0].replace("__", "###")): + root_abs = root_abs.replace("###", "__") + num_sep_this = root_abs.count(os.path.sep) + if num_sep_this >= num_sep + 2 and root_abs.split(os.path.sep)[num_sep + 2] == '.git': + continue + if num_sep_this == 6: + root_rel = os.path.basename(root_abs) + root_rel_ignore = re.sub('.ignore$', '', root_rel) + #print("Analyse de", root_abs, "->", root_rel) + if root_rel_ignore in [common_config_dir, hostname] or re.search('^' + root_rel_ignore.replace(wildcard, '.*') + '$', hostname): + #print(" -> retenu") + yield root_abs + else: + #print(" -> non retenu") + pass + if num_sep + level <= num_sep_this: + del dirs[:] + +# Arguments du script + +parser = argparse.ArgumentParser(description='Script permettant de surveiller / copier des fichiers entre un dépôt git et la machine locale') +exclusive_grp = parser.add_mutually_exclusive_group() +exclusive_grp.add_argument('--copy-repo-to-local', action='store_true', help='Copie les fichiers manquants / différents depuis le dépôt git ' + config_dir + ' vers la machine locale') +exclusive_grp.add_argument('--copy-local-to-repo', action='store_true', help='Copie les fichiers manquants / différents depuis la machine locale vers le dépôt git ' + config_dir) +parser.add_argument('--force', action='store_true', help='Force la copie, mode non-interactif - /!\ Dangereux /!\\') +args = parser.parse_args() + +# Liste des répertoires valides contenant les fichiers à analyser/comparer +# Les répertoires doivent suivre ce modèle : +# - {config_dir}/xxxxxxxx/conf/{common_config_dir} +# - {config_dir}/xxxxxxxx/conf/{hostname} +# - {config_dir}/xxxxxxxx/conf/{part_of_hostname_followed_by_wildcard_pattern} + +conf_dirs = [] +for directory in walk_git_conf(config_dir, 3): + conf_dirs.append(directory) + +# Stockage dans un dictionnaire de chaque fichier à comparer +# Clef : chemin absolu du fichier local +# Valeur : chemin absolu du fichier dans le dépôt git +# Les fichiers déjà trouvés sont remplacés par les éventuels nouveaux, dans l'ordre de parcours (alphanumérique) de 'conf_dirs' + +git_conf_file_dict = {} +for directory in conf_dirs: + len_dir = len(directory) + #print("\nRépertoire =", directory) + for item in os.walk(directory): + # Si le fichier contient des fichiers (donc est un répertoire non vide) + if len(item[2]) > 0: + for f in item[2]: + local_f_path = item[0][len_dir:] + '/' + f + git_f_path = item[0] + '/' + f + if directory.endswith('.ignore'): + #print(" -> on oublie", local_f_path) + git_conf_file_dict.pop(local_f_path, None) + else: + #print(" -> on retient", local_f_path) + git_conf_file_dict.update({ local_f_path : git_f_path }) + +# Génération des listes de fichiers manquants et différents + +different_files_dict = {} +missing_files_dict = {} + +for f_local, f_repo in git_conf_file_dict.items(): + try: + local_md5 = md5sum(f_local) + repo_md5 = md5sum(f_repo) + if local_md5 != repo_md5: + different_files_dict.update({ f_local : f_repo }) + except FileNotFoundError: + missing_files_dict.update({ f_local : f_repo }) + +# Récupération du statut des dépôts git + +monitored_git_repos = [] +try: + with open(git_monitored_repos_file, 'r') as monitored_repos: + for line in monitored_repos: + git_repo = line.rstrip() + if not os.path.exists(git_repo): + print('[CRITICAL]', git_repo, 'n\'existe pas, merci de vérifier le fichier', git_monitored_repos_file) + sys.exit(2) + if not os.path.isdir(git_repo): + print('[CRITICAL]', git_repo, 'n\'est pas un répertoire, merci de vérifier le fichier', git_monitored_repos_file) + sys.exit(2) + monitored_git_repos.append(git_repo) +except FileNotFoundError as e: + print('[CRITICAL]', e) + sys.exit(2) + +git_repos_locally_modified = [] +for git_repo in monitored_git_repos: + + p = sp.Popen(['git', '--work-tree=' + git_repo, '--git-dir=' + git_repo + '/.git', 'diff', '--name-only'], stdout=sp.PIPE, stderr=sp.PIPE) + stdout, stderr = p.communicate(2) + rc = p.returncode + stdout = stdout.decode('utf-8').replace('\n', ' ') + stderr = stderr.decode('utf-8').replace('\n', ' ') + if rc != 0: + print('[CRITICAL]', git_repo, stderr, 'rc=' + str(rc)) + sys.exit(2) + if stdout != '': + git_repos_locally_modified.append(git_repo) + +# Output + +len_diff = len(different_files_dict) +len_miss = len(missing_files_dict) + +if len_diff + len_miss > 0 or len(git_repos_locally_modified) > 0: + # If copy + if args.copy_repo_to_local or args.copy_local_to_repo: + # If there are missing files + if len_miss > 0: + for f_local, f_repo in missing_files_dict.items(): + if args.copy_local_to_repo: + print("[CRITICAL] Je ne peux pas copier un fichier qui n'existe pas (" + f_local + " vers " + f_repo + ")") + else: + if not args.force: + print("\nMerci de confirmer l'exécution de cette commande :") + print("cp", f_repo, f_local) + answer = input("Votre réponse ([oui|non]) : ") + if args.force or answer.lower() in ['y', 'yes', 'o', 'oui']: + try: + shutil.copy2(f_repo, f_local) + except FileNotFoundError: + os.makedirs(os.path.dirname(f_local), mode=0o755) + shutil.copy2(f_repo, f_local) + print("-", f_local, ": copie effectuée") + else: + print("-", f_local, ": on ne fait rien") + # If there are different files + if len_diff > 0: + for f_local, f_repo in different_files_dict.items(): + if not args.force: + print("\nMerci de confirmer l'exécution de cette commande :") + if args.copy_local_to_repo: + print("cp", f_local, f_repo) + else: + print("cp", f_repo, f_local) + answer = input("Votre réponse ([oui|non]) : ") + if args.force or answer.lower() in ['y', 'yes', 'o', 'oui']: + if args.copy_local_to_repo: + shutil.copy2(f_local, f_repo) + print("-", f_local, ": copie effectuée") + else: + shutil.copy2(f_repo, f_local) + print("-", f_local, ": copie effectuée") + else: + print("-", f_local, ": on ne fait rien") + # Else : just monitoring + else: + print('[WARNING] ', end='') + if len_miss > 0: + if len_miss == 1: + print('- 1 fichier manquant ', end='') + else: + print('-', len_miss, 'fichiers manquants ', end='') + if len_diff > 0: + if len_diff == 1: + print('- 1 fichier différent ', end='') + else: + print('-', len_diff, 'fichiers différents ', end='') + if len(git_repos_locally_modified) > 0: + if len(git_repos_locally_modified) == 1: + print('- 1 dépôt git modifié') + print("\nDépôt git modifié :") + else: + print('-', len(git_repos_locally_modified), 'dépôts git modifiés') + print("\nDépôts git modifiés :") + for git_repo in git_repos_locally_modified: + print('-', git_repo) + if len_miss > 0: + if len_miss == 1: + print("\nFichier manquant :") + else: + print("\nFichiers manquants :") + for f_local, f_repo in missing_files_dict.items(): + print("- dépôt :", f_repo) + print(" local :", f_local) + if len_diff > 0: + if len_diff == 1: + print("\nFichier différent :") + else: + print("\nFichiers différents :") + for f_local, f_repo in different_files_dict.items(): + print("- dépôt :", f_repo) + print(" local :", f_local) + sys.exit(1) +else: + print("[OK] Cette machine est conforme") + sys.exit(0)