Systemd tue le service immédiatement après le démarrage

14

J'écris le fichier d'unité systemd pour OSSEC HIDS. Le problème est que lorsque systemd démarre le service, il les arrête immédiatement.

Lorsque j'utilise cette directive ExecStart, tout fonctionne bien.

ExecStart=/var/ossec/bin/ossec-control start

Mais quand je fais une petite amélioration, je suis bien dans les journaux OSSEC, qu'il reçoive SIG 15 après le démarrage.

ExecStart=/bin/sh -c '${DIRECTORY}/bin/ossec-control start'

Si je fais un autre petit changement, le service recevra SIG 15 après 20 secondes.

ExecStart=/bin/sh -c '${DIRECTORY}/bin/ossec-control start && sleep 20'

Donc, je suppose, que systemd tue / bin / sh après le démarrage du service, et bin / sh tue alors OSSEC.

Comment puis-je résoudre ce problème?

Daniil Svetlov
la source
1
Quel est le type de service?
Wieland
@Wieland, j'ai été simple et bifurqué, mais le résultat est toujours le même.
Daniil Svetlov

Réponses:

36

incompatibilité du protocole de préparation

Comme Wieland l'a laissé entendre, Typele service est important. Ce paramètre indique quel protocole de préparation systemd attend du service qu'il parle. Un simpleservice est supposé être immédiatement prêt. Un forkingservice est censé être prêt après que son processus initial a forcé un enfant puis se termine. Un dbusservice est considéré comme prêt lorsqu'un serveur apparaît sur le bus de bureau. Et ainsi de suite.

Si vous n'obtenez pas le protocole de disponibilité déclarée dans l'unité de service pour correspondre à ce que fait le service, alors les choses tournent mal. L'inadéquation des protocoles de disponibilité empêche les services de démarrer correctement ou (plus généralement) d'être (mal) diagnostiqués par systemd comme défaillants. Lorsqu'un service est considéré comme ne démarrant pas systemd, il garantit que tous les processus supplémentaires orphelins du service qui pourraient avoir été laissés en cours d'exécution dans le cadre de l'échec (de son point de vue) sont tués afin de ramener correctement le service à l'inactif. Etat.

Tu fais exactement ça.

Tout d'abord, le truc simple: sh -cne correspond pas Type=simpleou Type=forking.

Dans le simpleprotocole, le processus initial est considéré comme le processus de service. Mais en fait, un sh -cwrapper exécute le programme de service réel en tant que processus enfant . Va donc MAINPIDmal et ExecReloadcesse de fonctionner, pour commencer. Lors de l'utilisation Type=simple, il faut utiliser sh -c 'exec …'ou ne pas utiliser sh -c en premier lieu. Ce dernier est plus souvent le bon choix que certains ne le pensent.

sh -cne correspond pas non Type=forkingplus. Le protocole de préparation d'un forkingservice est assez spécifique. Le processus initial doit bifurquer un enfant, puis quitter. systemd applique un délai d'attente à ce protocole. Si le processus initial ne se déroule pas dans le temps imparti, c'est un échec à devenir prêt. Si le processus initial ne se termine pas dans le temps imparti, c'est aussi un échec.

l'horreur inutile qui est ossec-control

Ce qui nous amène au truc complexe: ce ossec-controlscript.

Il s'avère que c'est un rcscript System 5 qui bifurque entre 4 et 10 processus, qui eux-mêmes à leur tour bifurquent et sortent également. C'est l'un de ces rcscripts System 5 qui tente de gérer tout un ensemble de processus serveur dans un seul script, avec des forboucles, des conditions de concurrence, des arbitraires sleeppour essayer de les éviter, des modes de défaillance qui peuvent étouffer le système dans un état semi-démarré, et toutes les autres horreurs qui ont poussé les gens à inventer des choses comme AIX System Resource Controller et daemontools il y a deux décennies. Et n'oublions pas le script shell caché dans un répertoire binaire qu'il réécrit à la volée, pour implémenter idiosyncrasiques enableet disableverbes.

Donc, quand vous /bin/sh -c '/var/ossec/bin/ossec-control start'ce qui se passe, c'est que:

  1. systemd indique ce qu'il attend du processus de service.
  2. C'est la coquille, qui bifurque ossec-control.
  3. Cela à son tour fourche entre 4 et 10 petits-enfants.
  4. Les petits-enfants bifurquent tous et sortent tour à tour.
  5. Les arrière-petits-enfants bifurquent tous et sortent en parallèle.
  6. ossec-control sort.
  7. Le premier obus sort.
  8. Les processus de service étaient les arrière-arrière- petits - enfants, mais parce que cette façon de travailler ne correspond ni au protocole forking ni au simpleprotocole de préparation, systemd considère que le service dans son ensemble a échoué et le ferme.

Aucune de ces horreurs n'est réellement nécessaire sous systemd. Rien de cela.

une unité de service de modèle systemd

Au lieu de cela, on écrit une unité de modèle très simple :

[Unité]
Description = Le serveur OSSEC HIDS% i
Après = network.target 

[Un service]
Type = simple
ExecStartPre = / usr / bin / env / var / ossec / bin /% p-% i -t
ExecStart = / usr / bin / env / var / ossec / bin /% p-% i -f

[Installer]
WantedBy = multi-user.target

Enregistrez ceci sous /etc/systemd/system/[email protected].

Les différents services réels sont des instanciations de ce modèle, nommé:

Ensuite, la fonction d'activation et de désactivation vient directement du système de gestion des services (avec le bogue RedHat 752774 corrigé), sans avoir besoin de scripts shell cachés.

 systemctl enable ossec @ dbd ossec @ agentlessd ossec @ csyslogd ossec @ maild ossec @ execd ossec @ analysisd ossec @ logcollector ossec @ remec ossec @ syscheckd ossec @ monitord

De plus, systemd apprend à connaître et à suivre directement chaque service réel. Il peut filtrer leurs journaux avec journalctl -u. Il peut savoir lorsqu'un service individuel a échoué. Il sait quels services sont censés être activés et en cours d'exécution.

Soit dit en passant: Type=simpleet l' -foption est aussi juste ici que dans de nombreux autres cas. Très peu de services dans la nature signalent en fait leur disponibilité à force de cela exit, et ce ne sont pas de tels cas ici non plus. Mais c'est ce que le forkingtype signifie. Les services à l'état sauvage dans la fourchette principale et la sortie principale en raison d'une notion de sagesse reçue erronée selon laquelle c'est ce que les démons sont censés faire. En fait, ce n'est pas le cas. Cela ne s'est pas produit depuis les années 1990. Il est temps de se rattraper.

Lectures complémentaires

JdeBP
la source
2
Réponse très détaillée! Je suggère également de créer une cible de "regroupement", disons ossec.target, qui contient Requires=toutes les instances nécessaires, puis de définir PartOf=ossec.targetdans ossec @ .service. Cela permettra de démarrer et d'arrêter ossec en démarrant et en arrêtant ossec.target.
intelfx
@JdeBP, wow! Merci beaucoup pour ce genre de réponse détaillée. J'espère que je vais faire cette unité et écrire ici sur les résultats. J'étais cependant, que je serais plus facile. Mais vous avez raison, ossec-controll est un enfer d'init.
Daniil Svetlov
1
Quelle est la raison d'utiliser / usr / bin / env comme wrapper?
Marius Gedminas
1

Gardez Type = forking et donnez un emplacement de fichier pid si le service / l'application de démarrage maintient un pid.

[Unité]
Description = "Exécuter l'application au démarrage"
Après = network.target syslog.target auditd.service

[Service]
Type = forking
PIDFile = / var / run / apache2 / apache2.pid
ExecStart = / etc / init.d / apache2 start
ExecStop = / etc / init.d / apache2 stop
StandardOutput = syslog
StandardError = syslog
Restart = on-failure
SyslogIdentifier = webappslog

[Install]
WantedBy = multi-user.target
Alias ​​= webapps

Raushan
la source
0

Quelque peu lié, j'avais un service systemd qui semblait que systemd le "tuerait" après 30s.

systemctl status service-namemontrerait main process exited, code=exited, status=1/FAILUREaprès que 30s se soient écoulés.

Il fonctionnerait bien "de manière isolée" (comme manuellement dans le terminal avec le même environnement ).

Il s'avère que c'était

Type=forking
...
Environment=ABC="TRUE"
ExecStart=/path/to/my_script_to_spawn_process.sh

à l'intérieur, my_script_to_spawn_process.shil faisait

/bin/something > /dev/null 2>&1 &

qui fonctionne mais supprimait les informations du journal de sortie (normalement, il va dans un fichier, ou sinon, peut-être journalctl).

Le changer pour se connecter à un autre endroit comme /bin/something > /tmp/my_file

puis suivre la /tmp/my_filecause réelle révélée. Ce qui était (tangentiellement) que vous ne pouvez pas utiliser la syntaxe Environment=ABC="true"comme vous pouvez le faire dans bash, il ne doit pas y avoir de guillemets ou la valeur clé dans les guillemets comme Environment="ABC=true"ce qui faisait que mon processus se terminait "dans sa phase de configuration" après environ 30s.

rogerdpack
la source
-4

Notez que le modèle de démon de systemd est simpliste et incompatible avec de nombreux démons existants qui font de multiples forks, exec'ing et setuid'ing. Les plus courants sont les démons qui commencent en tant que root pour configurer les choses, puis basculent vers un UID moins privilégié pour les opérations de routine. Par exemple, l'initialisation du fichier Pid est une chose qui échoue sous systemd en raison de problèmes de privilèges. Il existe des solutions de contournement (pas de correctifs) mais elles sont mal documentées.

L'explication de JdeBP est la bienvenue mais incomplète et son affirmation selon laquelle tout est la faute d'Ossec-Control n'est tout simplement pas vraie. Même des choses assez banales sont problématiques, par exemple obtenir des lignes de journal non tronquées pour déboguer des problèmes ou des messages d'erreur significatifs de systemd lui-même quand il tue des processus.

John
la source
1
À quoi servent les fichiers PID? S'il en existe un pour un service donné, il peut y avoir ou non un processus réel avec ce PID, et lorsqu'un processus avec le bon PID existe, il peut ou non être réellement le service attendu.
JoostM