O serviço Systemd é executado sem sair

30

Criei meu próprio serviço para o jekyll e, quando inicio o serviço, parece que ele não é executado como um processo em segundo plano porque sou forçado a ctrl+ csair dele. Apenas permanece em primeiro plano por causa do --watch. Não sei ao certo como contorná-lo e fazê-lo funcionar em segundo plano. Alguma ideia?

# /etc/systemd/system/jekyll-blog.service

[Unit]
Description=Start blog jekyll

[Service]
Type=forking
WorkingDirectory=/home/blog
ExecStart=/usr/local/bin/jekyll build --watch --incremental -s /home/blog -d /var/www/html/blog &
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
User=root
Group=root

[Install]
WantedBy=multi-user.target
madmanali93
fonte
O systemd iniciará seu processo e espera que ele bifurque outro processo, se você estiver usando Type=forking. Além disso, ele não será executado execStartcomo uma expansão do shell, de modo que &no final nunca será entendido como um sinalizador de segundo plano.
grochmal
meu ruim era o & eu estava testando. Então, o tipo deve ser simples?
precisa saber é o seguinte
2
Se não me engano, jekyll é um tipo de trilhos, ou seja, um pequeno servidor web em ruby. Então, sim, Type=simpleseria apropriado. Além disso, esse não é o tipo de aplicativo que eu executaria como root, pelo menos não em uma máquina com acesso à Internet (que pode não ser o seu caso).
grochmal 7/09/16
Obrigado sim simples trabalhou. Além disso, o comando this gera o html estático para o Apache, para que o jekyll não seja exibido no servidor. Eu acho que deve ficar bem se for executado como root. Não tenho certeza que estava debatendo.
precisa saber é o seguinte
Oh OK, então é isso que --incrementalfaz :). Sim, não vejo nenhum problema de segurança na regeneração de arquivos como raiz. Obviamente, considerando que esses arquivos não são fornecidos pelo usuário.
grochmal

Respostas:

54

O Systemd pode lidar com vários tipos de serviço diferentes, especificamente um dos seguintes

  • simple - Um processo de execução demorada que não se destaca e permanece anexado ao shell.
  • forking - Um daemon típico que se bifurca desanexando-o do processo que o executou, efetivamente se background.
  • oneshot - Um processo de curta duração que deve sair.
  • dbus - Simples, mas a notificação dos processos de inicialização é enviada pelo dbus.
  • notify - Simples, mas a notificação do acabamento da inicialização dos processos é enviada por inotify.
  • idle - Como simples, mas o binário é iniciado após o envio do trabalho.

No seu caso, você escolheu o Type=forkingque significa que o systemd está aguardando o processo de bifurcar-se e o processo pai terminar, o que é uma indicação de que o processo foi iniciado com êxito. No entanto, seu processo não está fazendo isso - ele permanece em primeiro plano e, portanto, systemctl startficará travado indefinidamente ou até que os processos travem.

Em vez disso, você deseja Type=simple, que é o padrão, para que você possa remover completamente a linha para obter o mesmo efeito. Nesse modo, o systemd não espera a conclusão dos processos (como não há como saber quando isso aconteceu) e, portanto, continua executando os serviços dependentes imediatamente. No seu caso, não há nenhum, então isso não importa.

Uma pequena nota sobre segurança:

Você está executando o serviço como root; isso é desencorajado, pois é menos seguro do que executá-lo como um usuário não privilegiado. A razão para isso é que, se houver uma vulnerabilidade no jekyll que, de alguma forma, permita a execução de comandos (possivelmente por meio do código que está analisando), o invasor não precisará fazer mais nada para possuir o seu sistema completamente. Se, por outro lado, ele for executado como um usuário não privilegiado, o invasor poderá causar tanto dano quanto esse usuário e deve agora tentar obter privilégios de root para possuir completamente o seu sistema. Ele simplesmente adiciona uma camada extra que os atacantes devem seguir.

Você pode simplesmente executá-lo como o mesmo usuário que está executando seu servidor da Web, mas isso deixa você aberto a outro possível ataque. Se houver uma vulnerabilidade no servidor da web que permita ao usuário manipular arquivos no sistema, eles poderão modificar os arquivos html gerados ou, pior ainda, os arquivos de origem e fazer com que o servidor atenda o que quiser. No entanto, se os arquivos gerados e os arquivos de origem forem legíveis apenas pelo servidor da web e graváveis ​​como outro usuário não privilegiado, eles não poderão modificá-los com facilidade, atacando o servidor da web.

No entanto, se você estiver simplesmente servindo arquivos estáticos desse servidor e manter o servidor atualizado, esses ataques serão muito improváveis ​​- mas ainda possíveis. É de sua responsabilidade avaliar os riscos versus a sobrecarga de configurá-lo com base no quão crítico é o seu sistema, mas essas duas dicas são muito simples de configurar e quase sem sobrecarga de manutenção.

Michael Daffin
fonte
0

Além da solução de @ Michael Daffin , você também pode usar a ferramenta daemonize para obter o uso forkingconforme mostrado no exemplo a seguir.

Dado um pequeno script de shell que eu quero daemonize e que eu quero controlar sobre o systemd, salvei-o como /home/pi/testscript.sh:

#!/bin/bash

while true;
do
    sleep 1
    echo -n "."
done

Se você ainda não o tiver, instale o daemonize, desta forma:

sudo apt install daemonize

Agora crie o arquivo de definição de serviço de arquivo:

sudo vi /etc/systemd/system/testomat.service
# It is not recommended to modify this file in-place, because it will
# be overwritten during package upgrades. If you want to add further
# options or overwrite existing ones then use
# $ systemctl edit testomat.service
# See "man systemd.service" for details.

# copied from https://github.com/bitcoin/bitcoin/blob/master/contrib/init/bitcoind.service and modified by Michael 

[Unit]
Description=Test service
After=network.target

[Service]
ExecStart=daemonize -p /run/testomat/testomat.pid -o /home/pi/testscript.log /home/pi/testscript.sh
TimeoutSec=1200

# Make sure the config directory is readable by the service user
PermissionsStartOnly=true

# Process management
####################
Type=forking
PIDFile=/run/testomat/testomat.pid
Restart=on-failure
GuessMainPID = true

# Directory creation and permissions
####################################

# Run as pi:pi
User=pi
Group=pi

# /run/testomat
RuntimeDirectory=testomat
RuntimeDirectoryMode=0710

# /var/lib/testomat
StateDirectory=testomat
StateDirectoryMode=0710

# Hardening measures
####################

# Provide a private /tmp and /var/tmp.
PrivateTmp=true

# Mount /usr, /boot/ and /etc read-only for the process.
ProtectSystem=full

# Allow access to /home, /root and /run/user
# Chosing "false" is actually no hardening, this is just to demonstrate the usage of a service. Well, I could have omitted it. True. :)
ProtectHome=false

# Disallow the process and all of its children to gain
# new privileges through execve().
NoNewPrivileges=true

# Use a new /dev namespace only populated with API pseudo devices
# such as /dev/null, /dev/zero and /dev/random.
PrivateDevices=true

# Deny the creation of writable and executable memory mappings.
MemoryDenyWriteExecute=true

[Install]
WantedBy=multi-user.target

O serviço recém-criado deve ser anunciado para systemd:

systemctl daemon-reload

Agora você pode iniciar o serviço e os garfos de script. Como esperado, o início do serviço volta ao shell imediatamente. O resultado é óbvio:

$ tail -f testscript.log 
.....................

Michael
fonte
Qual é a vantagem de usar daemonize+ em Type=forkingvez de Type=simpledeixar o systemd cuidar de iniciar o serviço? Type=forkingé um tipo de configuração de compatibilidade no systemd para oferecer suporte a programas herdados gravados no fork.
Johan Myréen
Eu acho que é uma solução equivalente; Eu só queria fornecer ao OP uma solução alternativa e torná-lo ciente dessa ferramenta que eu já usei no /etc/init.d vezes, pois a pergunta também é um pouco sobre como daemonizar um processo.
Michael