El servicio Systemd se ejecuta sin salir

30

He creado mi propio servicio de Jekyll y cuando inicio el servicio que parece que no se ejecuta como un proceso en segundo plano, porque estoy obligado a ctrl+ cfuera de él. Simplemente permanece en primer plano debido a --watch. No estoy seguro de cómo evitarlo y hacerlo de modo que se ejecute en segundo plano. ¿Alguna idea?

# /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
fuente
systemd iniciará su proceso y esperará que bifurque otro proceso si lo está utilizando Type=forking. Además, no se ejecutará execStartcomo una expansión de shell, por lo que &al final nunca se entenderá como una bandera de fondo.
grochmal
mi mal y yo estaba probándolo. Entonces, ¿el tipo debe ser simple?
madmanali93
2
Si no me equivoco, jekyll es un tipo de rieles, es decir, un pequeño servidor web en ruby. Entonces sí, Type=simplesería apropiado. Además, este no es el tipo de aplicación que ejecutaría como root, al menos no en una máquina con conexión a Internet (que puede no ser su caso).
grochmal
Gracias sí simple trabajado. Además, el comando this genera el html estático para Apache para que jekyll no sirva en el servidor. Creo que debería estar bien si se ejecuta como root. Aunque no estoy seguro si lo estaba debatiendo.
madmanali93
Oh OK, eso es lo que --incrementalhace :). Sí, no veo ningún problema de seguridad en la regeneración de archivos como root. Por supuesto, dado que esos archivos no son proporcionados por el usuario.
grochmal

Respuestas:

54

Systemd puede manejar varios tipos de servicios diferentes, específicamente uno de los siguientes

  • simple - Un proceso de larga duración que no funciona en segundo plano y permanece unido al shell.
  • forking - Un demonio típico que se bifurca separándolo del proceso que lo ejecutó, de manera efectiva en segundo plano.
  • oneshot - Un proceso de corta duración que se espera que salga.
  • dbus - Como simple, pero la notificación de los procesos de inicio de finalización se envía a través de dbus.
  • notify - Como simple, pero la notificación de los procesos de inicio de finalización se envía a través de inotify.
  • idle - Como simple, pero el binario se inicia después de que se ha enviado el trabajo.

En su caso, ha elegido lo Type=forkingque significa que systemd está esperando que el proceso se bifurque y que el proceso principal finalice, lo que toma como una indicación de que el proceso ha comenzado con éxito. Sin embargo, su proceso no está haciendo esto: permanece en primer plano y, por systemctl startlo tanto, se bloqueará indefinidamente o hasta que el proceso se bloquee.

En su lugar, desea Type=simple, que es el valor predeterminado para que pueda eliminar la línea por completo para obtener el mismo efecto. En este modo, systemd no espera a que los procesos terminen de iniciarse (ya que no tiene forma de saber cuándo sucedió esto) y, por lo tanto, continúa ejecutando servicios dependientes de inmediato. En su caso no hay ninguno, así que esto no importa.

Una pequeña nota sobre seguridad:

Está ejecutando el servicio como root, se desaconseja, ya que es menos seguro que ejecutarlo como un usuario sin privilegios. La razón de esto es que si hay una vulnerabilidad en jekyll que de alguna manera permite la ejecución de comandos (posiblemente a través del código que está analizando), entonces el atacante no tiene que hacer nada más para poseer completamente su sistema. Si, por otro lado, se ejecuta como un usuario sin privilegios, el atacante solo puede hacer tanto daño como ese usuario y ahora debe intentar obtener privilegios de root para poseer completamente su sistema. Sin embargo, simplemente agrega una capa extra que deben atacar.

Simplemente puede ejecutarlo como el mismo usuario que está ejecutando su servidor web, pero esto lo deja abierto a otro posible ataque. Si hay una vulnerabilidad en su servidor web que le permite al usuario manipular archivos en su sistema, puede modificar los archivos html generados, o peor aún, los archivos fuente y hacer que su servidor sirva lo que quiera. Sin embargo, si el servidor web solo puede leer los archivos generados y los archivos fuente y se puede escribir en otro usuario sin privilegios, no podrán modificarlos tan fácilmente atacando el servidor web.

Sin embargo, si simplemente está sirviendo archivos estáticos desde este servidor y mantiene el servidor actualizado, estos ataques son muy poco probables, pero aún posibles. Es su responsabilidad sopesar los riesgos frente a la sobrecarga de configurarlo en función de la importancia de su sistema, pero estos dos consejos son muy simples de configurar y prácticamente no requieren gastos generales de mantenimiento.

Michael Daffin
fuente
0

Además de @ Michael Daffin solución 's, también se puede utilizar el daemonize herramienta para el uso de achive forkingcomo se muestra en el siguiente ejemplo.

Dado un pequeño script de shell que quiero demonizar y que quiero controlar sobre systemd, lo guardé como /home/pi/testscript.sh:

#!/bin/bash

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

Si aún no lo tiene, instale daemonize, así:

sudo apt install daemonize

Ahora cree el archivo de definición del servicio de archivos:

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

El servicio recién creado debe anunciarse a systemd:

systemctl daemon-reload

Ahora puede iniciar el servicio y la secuencia de comandos se bifurca. Como se esperaba, el inicio del servicio vuelve al shell inmediatamente. El resultado es obvio:

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

Miguel
fuente
¿Cuál es la ventaja de usar daemonize+ en Type=forkinglugar de Type=simpledejar que systemd se encargue de iniciar el servicio? Type=forkinges una especie de configuración de compatibilidad en systemd para admitir programas heredados escritos en fork.
Johan Myréen
Creo que es una solución equivalente; Solo quería proporcionarle al OP una solución alternativa y hacerle conocer esta herramienta que ya he usado en /etc/init.d veces, ya que la pregunta también es un poco sobre cómo demonizar un proceso.
Michael