Systemd interrompe il servizio immediatamente dopo l'avvio


14

Sto scrivendo un file di unità di sistema per OSSEC HIDS. Il problema è che quando systemd avvia il servizio, li interrompe immediatamente.

Quando uso quella direttiva ExecStart tutto funziona bene.

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

Ma quando faccio piccoli miglioramenti, vado bene nei registri OSSEC, che ricevono SIG 15 dopo l'avvio.

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

Se faccio un altro piccolo servizio di cambio riceverò SIG 15 dopo 20 secondi.

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

Quindi, immagino, che systemd uccida il processo / bin / sh dopo l'avvio del servizio e bin / sh quindi uccide OSSEC.

Come posso risolvere questo problema?


1
Qual è il tipo di servizio?
Wieland,

@Wieland, ero semplice e biforcuta, ma il risultato è sempre lo stesso.
Daniil Svetlov,

Risposte:


36

disadattamento del protocollo di disponibilità

Come implica Wieland, Typeil servizio è importante. Tale impostazione indica quale protocollo di prontezza systemd prevede che il servizio parlerà. Si simplepresume che un servizio sia immediatamente pronto. Un forkingservizio viene considerato pronto dopo che il suo processo iniziale crea un figlio e poi esce. Un dbusservizio viene considerato pronto quando viene visualizzato un server sul bus desktop. E così via.

Se non si ottiene che il protocollo di disponibilità dichiarato nell'unità di servizio corrisponda a quello che fa il servizio, allora le cose vanno male. I disallineamenti del protocollo di disponibilità fanno sì che i servizi non vengano avviati correttamente o (più di solito) vengano (erroneamente) diagnosticati da systemd come non funzionanti. Quando un servizio viene visto come mancato avvio di systemd assicura che ogni processo aggiuntivo orfano del servizio che potrebbe essere stato lasciato in esecuzione come parte dell'errore (dal suo punto di vista) viene interrotto al fine di riportare il servizio correttamente inattivo stato.

Stai facendo esattamente questo.

Prima di tutto, le cose semplici: sh -cnon corrispondono Type=simpleo Type=forking.

Nel simpleprotocollo, il processo iniziale è presa per essere il processo di servizio. Ma in realtà un sh -cwrapper esegue l'effettivo programma di servizio come processo figlio . Quindi MAINPIDva storto e ExecReloadsmette di funzionare, per cominciare. Quando si usa Type=simple, si deve usare sh -c 'exec …'o non usare sh -c in primo luogo. Quest'ultimo è più spesso il corso corretto di quanto alcuni pensano.

sh -cnon corrisponde Type=forkingneanche. Il protocollo di prontezza per un forkingservizio è abbastanza specifico. Il processo iniziale deve biforcare un figlio e quindi uscire. systemd applica un timeout a questo protocollo. Se il processo iniziale non si interrompe entro il tempo assegnato, non è possibile essere pronti. Se il processo iniziale non termina entro il tempo assegnato, anche questo è un errore.

l'orrore inutile che è ossec-control

Il che ci porta a cose complesse: quella ossec-controlsceneggiatura.

Si scopre che si tratta di uno rcscript di System 5 che esegue il fork tra 4 e 10 processi, che a loro volta si spostano ed escono anch'essi. È uno di quegli rcscript di System 5 che tenta di gestire un intero set di processi server in un singolo script, con forloop, condizioni di gara, sleeps arbitrari per cercare di evitarli, modalità di errore che possono soffocare il sistema in uno stato semi-avviato, e tutti gli altri orrori che hanno portato la gente a inventare cose come AIX System Resource Controller e daemontools due decenni fa. E non dimentichiamo lo script di shell nascosto in una directory binaria che riscrive al volo, per implementare idiosincratici enablee disableverbi.

Quindi quando ti /bin/sh -c '/var/ossec/bin/ossec-control start'succede è che:

  1. systemd fork quello che si aspetta sia il processo di servizio.
  2. Questa è la shell, che forchette ossec-control.
  3. Che a sua volta biforcano tra 4 e 10 nipoti.
  4. I nipoti si biforcano tutti e escono a turno.
  5. I pronipoti si biforcano tutti e escono in parallelo.
  6. ossec-control uscite.
  7. Esce la prima shell.
  8. I processi di servizio sono stati i grandi-ottimo- nipoti, ma perché in questo modo di incontri di lavoro il forking il simpleprotocollo di prontezza, systemd considera il servizio nel suo complesso ad aver fallito e si chiude di nuovo verso il basso.

Niente di tutto questo orrore è effettivamente necessario sotto systemd. Nessuno di questi.

un'unità di servizio modello systemd

Invece, si scrive un'unità modello molto semplice :

[Unità]
Descrizione = Il server OSSEC HIDS% i
Dopo = network.target 

[Servizio]
Tipo = semplice
ExecStartPre = / usr / bin / env / var / ossec / bin /% p-% i -t
ExecStart = / usr / bin / env / var / ossec / bin /% p-% i -f

[Installare]
WantedBy = multi-user.target

Salva questo come /etc/systemd/system/ossec@.service.

I vari servizi effettivi sono istanze di questo modello, denominato:

  • ossec@dbd.service
  • ossec@agentlessd.service
  • ossec@csyslogd.service
  • ossec@execd.service
  • ossec@agentd.service
  • ossec@logcollector.service
  • ossec@syscheckd.service
  • ossec@maild.service
  • ossec@analysisd.service
  • ossec@remoted.service
  • ossec@monitord.service

Quindi abilitare e disabilitare la funzione proviene direttamente dal sistema di gestione del servizio (con bug RedHat 752774 corretto), senza la necessità di script di shell nascosti.

 systemctl abilita ossec @ dbd ossec @ agentlessd ossec @ csyslogd ossec @ maild ossec @ execd ossec @ analysisd ossec @ logcollector ossec @ remote ossec @ syscheckd ossec @ monitord

Inoltre, systemd è in grado di conoscere e tracciare direttamente ogni servizio effettivo. Può filtrare i loro log con journalctl -u. Può sapere quando un singolo servizio è fallito. Sa quali servizi dovrebbero essere abilitati e in esecuzione.

A proposito: Type=simplee l' -fopzione è proprio qui come in molti altri casi. Pochissimi servizi in natura segnalano effettivamente la loro prontezza a forza di exit, e questi non sono neanche questi casi. Ma questo è ciò che forkingsignifica il tipo. I servizi allo stato brado in genere si limitano a biforcarsi ed uscire a causa di un errore che ha ricevuto la saggezza che questo è ciò che i demoni dovrebbero fare. In realtà non lo è. Non lo è più dagli anni '90. È tempo di recuperare.

Ulteriori letture


2
Risposta molto dettagliata! Suggerirei anche di creare un target di "raggruppamento", ad esempio ossec.target, che includa Requires=tutte le istanze necessarie e quindi impostare PartOf=ossec.targetin ossec @ .service. Ciò consentirà di avviare e arrestare ossec avviando e arrestando ossec.target.
intelfx,

@JdeBP, wow! Grazie mille per questo tipo di risposta dettagliata. Spero di creare questa unità e scrivere qui sui risultati. Pensavo che sarò più facile. Ma hai ragione, ossec-controll è un vero inferno.
Daniil Svetlov,

1
Qual è la ragione per usare / usr / bin / env come wrapper?
Marius Gedminas,

1

Mantieni Type = biforcazione e indica il percorso del file pid se il servizio di avvio / app mantiene un pid.

[Unità]
Descrizione = "Esegui app all'avvio"
Dopo = network.target syslog.target auditd.service

[Servizio]
Tipo = forking
PIDFile = / var / run / apache2 / apache2.pid
ExecStart = / etc / init.d / apache2 avvia
ExecStop = / etc / init.d / apache2 stop
StandardOutput = syslog
StandardError = syslog
Riavvia = errore
SyslogIdentifier = webappslog

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


0

In qualche modo correlato, avevo un servizio systemd che sembrava che systemd lo avrebbe "ucciso" dopo 30 anni.

systemctl status service-namemostrerebbe main process exited, code=exited, status=1/FAILUREdopo che sono trascorsi 30 anni.

Funzionerebbe bene "in isolamento" (come manualmente nel terminale con lo stesso ambiente ).

Si è scoperto che lo era

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

al suo my_script_to_spawn_process.shinterno stava facendo

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

che funziona ma ha scartato le informazioni del registro di output (normalmente va a un file o, se non quello, possibilmente journalctl).

Modificandolo per accedere ad altrove come /bin/something > /tmp/my_file

quindi pedinare il /tmp/my_filerivelato la vera causa. Il che era (tangenzialmente) che non puoi usare la sintassi Environment=ABC="true"come puoi in bash, non deve esserci virgolette o il valore chiave racchiuso tra virgolette come quello Environment="ABC=true"che stava causando l'uscita del mio processo "nella sua fase di configurazione" dopo circa 30 secondi.


-4

Si noti che il modello daemon di systemd è semplicistico e incompatibile con molti demoni esistenti che eseguono più fork, eseguendo e impostando. I più comuni sono i demoni che iniziano come root per impostare le cose e quindi passare a un UID meno privilegiato per le operazioni di routine. ad es. l'inizializzazione del file Pid è una cosa che fallisce in systemd a causa di problemi di privilegio. Esistono soluzioni alternative (non correzioni) ma è documentato male.

La spiegazione di JdeBP è benvenuta ma incompleta e la sua affermazione che è tutta colpa dell'ossecontrollo non è semplicemente vera. Anche cose piuttosto banali sono problematiche, ad esempio ottenere linee di registro non troncate per eseguire il debug di problemi o messaggi di errore significativi dal sistema stesso quando uccide i processi.


1
A che cosa servono i file PID? Se ne esiste uno per un determinato servizio, potrebbe esserci o meno un processo effettivo con quel PID e quando esiste un processo con il PID giusto, potrebbe effettivamente essere il servizio previsto.
JoostM,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.