Systemd mata serviço imediatamente após o início

14

Estou escrevendo um arquivo de unidade systemd para o OSSEC HIDS. O problema é que, quando o systemd inicia o serviço, ele imediatamente os interrompe.

Quando eu uso essa diretiva ExecStart, tudo funciona bem.

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

Porém, quando faço pequenas melhorias, fico bem nos logs do OSSEC, que recebem o SIG 15 após o início.

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

Se eu fizer outro pequeno serviço de troca, receberá SIG 15 após 20 segundos.

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

Então, eu acho que esse systemd mata / bin / sh após o início do serviço e bin / sh mata o OSSEC.

Como posso resolver este problema?

Daniil Svetlov
fonte
1
Qual é o tipo de serviço?
Wieland
@ Wieland, eu estava tentando simples e bifurcação, mas o resultado ainda é o mesmo.
Daniil Svetlov

Respostas:

36

incompatibilidade de protocolo de prontidão

Como Wieland sugeriu, Typeo serviço é importante. Essa configuração indica qual protocolo de prontidão o systemd espera que o serviço fale. simplePresume-se que um serviço esteja imediatamente pronto. Um forkingserviço é levado para ficar pronto depois que seu processo inicial bifurca uma criança e sai. Um dbusserviço é considerado pronto quando um servidor aparece no Desktop Bus. E assim por diante.

Se você não conseguir que o protocolo de prontidão declarado na unidade de serviço corresponda ao que o serviço faz, as coisas darão errado. As incompatibilidades do protocolo de prontidão fazem com que os serviços não iniciem corretamente ou (geralmente) sejam diagnosticados incorretamente pelo systemd como falhas. Quando um serviço é visto como falhando ao iniciar o systemd, garante que todo processo adicional órfão do serviço que pode ter sido deixado em execução como parte da falha (do ponto de vista dele) seja eliminado, a fim de trazer o serviço corretamente de volta ao inativo Estado.

Você está fazendo exatamente isso.

Primeiro de tudo, as coisas simples: sh -cnão correspondem Type=simpleou Type=forking.

No simpleprotocolo, o processo inicial é levado para ser o processo de serviço. Mas, de fato, um sh -cwrapper executa o programa de serviço real como um processo filho . Então MAINPIDdá errado e ExecReloadpara de funcionar, para começar. Ao usar Type=simple, é preciso usar sh -c 'exec …'ou não usar sh -c em primeiro lugar. Este último é mais frequentemente o curso correto do que algumas pessoas pensam.

sh -ctambém não corresponde Type=forking. O protocolo de prontidão para um forkingserviço é bastante específico. O processo inicial precisa bifurcar um filho e sair. O systemd aplica um tempo limite a este protocolo. Se o processo inicial não bifurcar dentro do tempo alocado, é uma falha para ficar pronto. Se o processo inicial não sair dentro do tempo alocado, isso também será uma falha.

o horror desnecessário que é ossec-control

O que nos leva a coisas complexas: esse ossec-controlscript.

Acontece que é um rcscript do System 5 que executa entre 4 e 10 processos, que por sua vez bifurcam e saem também. É um daqueles rcscripts do Sistema 5 que tenta gerenciar todo um conjunto de processos do servidor em um único script, com forloops, condições de corrida, sleeps arbitrários para tentar evitá-los, modos de falha que podem sufocar o sistema em um estado semi-iniciado, e todos os outros horrores que levaram as pessoas a inventar coisas como o AIX System Resource Controller e daemontools duas décadas atrás. E não vamos esquecer o script de shell oculto em um diretório binário que ele reescreve em tempo real, para implementar idiossincráticos enablee disableverbos.

Então, quando você o /bin/sh -c '/var/ossec/bin/ossec-control start'que acontece é o seguinte:

  1. O systemd bifurca o que espera ser o processo de serviço.
  2. Essa é a concha, que bifurca ossec-control.
  3. Isso, por sua vez, bifurca-se entre 4 e 10 netos.
  4. Todos os netos bifurcam e saem por sua vez.
  5. Todos os bisnetos bifurcam e saem em paralelo.
  6. ossec-control saídas.
  7. O primeiro shell sai.
  8. Os processos de serviços foram os grandes-grande- netos, mas porque esta maneira de partidas de trabalho nem o forking nem o simpleprotocolo de prontidão, systemd considera o serviço como um todo para falharam e fecha-lo de volta para baixo.

Nada desse horror é realmente necessário no systemd. Nada disso.

uma unidade de serviço de modelo systemd

Em vez disso, escreve-se uma unidade de modelo muito simples :

[Unidade]
Descrição = O servidor OSSEC HIDS% i
Depois = network.target 

[Serviço]
Tipo = simples
ExecStartPre = / usr / bin / env / var / ossec / bin /% p-% i-t
ExecStart = / usr / bin / env / var / ossec / bin /% p-% i -f

[Instalar]
WantedBy = multi-user.target

Salve isso como /etc/systemd/system/[email protected].

Os vários serviços reais são instanciações deste modelo, denominadas:

A função habilitar e desabilitar vem diretamente do sistema de gerenciamento de serviços (com o bug 752774 do RedHat corrigido), sem a necessidade de scripts shell ocultos.

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

Além disso, o systemd conhece e rastreia cada serviço real diretamente. Ele pode filtrar seus logs com journalctl -u. Ele pode saber quando um serviço individual falhou. Ele sabe quais serviços devem estar habilitados e em execução.

A propósito: Type=simplee a -fopção é tão aqui quanto em muitos outros casos. Muito poucos serviços na natureza realmente sinalizam sua prontidão pela força do exit, e esses aqui também não são esses casos. Mas é isso que o forkingtipo significa. Serviços na natureza, principalmente, apenas bifurcam e saem por causa de alguma noção errônea de sabedoria recebida de que é isso que os daemons devem fazer. De fato, não é. Não é desde a década de 1990. É hora de recuperar o atraso.

Leitura adicional

JdeBP
fonte
2
Resposta muito detalhada! Eu também sugeriria criar um destino de "agrupamento", por exemplo, ossec.target, com Requires=todas as instâncias necessárias e, em seguida, definido PartOf=ossec.targetem ossec @ .service. Isso permitirá iniciar e parar o ossec iniciando e parando o ossec.target.
Intelfx #
@JdeBP, uau! Muito obrigado por esse tipo de resposta detalhada. Espero que eu faça essa unidade e escreva aqui sobre os resultados. Eu pensava que seria mais fácil. Mas você está certo, o controle ossec é um inferno para iniciantes.
Daniil Svetlov
1
Qual o motivo do uso de / usr / bin / env como wrapper?
Marius Gedminas 30/03/19
1

Mantenha Type = bifurcação e forneça um local para o arquivo pid se o serviço / aplicativo de inicialização estiver mantendo algum pid.

[Unidade]
Descrição = "Executar aplicativo na inicialização"
Depois = network.target syslog.target auditd.service

[Serviço]
Tipo = bifurcar
PIDFile = / var / run / apache2 / apache2 / apache2.pid
ExecStart = / etc / init.d / apache2 inicie
ExecStop = / etc / init.d / apache2 stop
StandardOutput = syslog
StandardError = syslog
Reinicie = em falha
SyslogIdentifier = webappslog

[Instalação]
WantedBy = multiusuário.target
Alias ​​= webapps

Raushan
fonte
0

De certa forma, eu tinha um serviço systemd que parecia que o systemd o "mataria" depois dos 30 anos.

systemctl status service-nameapareceria main process exited, code=exited, status=1/FAILUREdepois dos 30 anos.

Ele funcionaria bem "isoladamente" (como manualmente no terminal com o mesmo ambiente ).

Acontece que era

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

dentro my_script_to_spawn_process.shdele estava fazendo

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

que funciona, mas estava descartando as informações do log de saída (normalmente ele vai para um arquivo ou, se não for esse, possivelmente journalctl).

Alterando-o para logar em outro lugar como /bin/something > /tmp/my_file

depois, seguindo a /tmp/my_filerevelada causa real. Que era (tangencialmente) que você não pode usar a sintaxe Environment=ABC="true"como no bash, não deve haver aspas ou o valor-chave dentro de aspas como o Environment="ABC=true"que estava fazendo com que meu processo fosse encerrado "em sua fase de configuração" após cerca de 30 anos.

rogerdpack
fonte
-4

Observe que o modelo de daemon do systemd é simplista e incompatível com muitos daemons existentes que realizam múltiplas operações de forquilha, execução e configuração. Os mais comuns são os daemons que iniciam como raiz para configurar as coisas e depois mudam para um UID menos privilegiado para operação de rotina. Por exemplo, a inicialização do arquivo Pid é uma coisa que falha no systemd devido a problemas de privilégio. Existem soluções alternativas (não correções), mas estão mal documentadas.

A explicação do JdeBP é bem-vinda, mas incompleta, e sua afirmação de que tudo é culpa do ossec-control simplesmente não é verdadeira. Mesmo coisas bastante triviais são problemáticas, por exemplo, obter linhas de log não truncadas para depurar problemas ou mensagens de erro significativas do próprio systemd quando ele mata processos.

John
fonte
1
Para que servem os arquivos PID, afinal? Se existe um para um determinado serviço, pode ou não haver um processo real com esse PID e, quando existe um processo com o PID correto, ele pode ou não ser o serviço esperado.
JoostM 11/11