Usługa Systemd działa bez wychodzenia

30

Stworzyłem własną usługę dla jekyll i kiedy ją uruchamiam, wygląda na to, że nie działa jako proces w tle, ponieważ jestem zmuszony ctrl+ cz niej zrezygnować . Po prostu pozostaje na pierwszym planie z powodu --watch. Nie jestem pewien, jak go obejść i sprawić, aby działał w tle. jakieś pomysły?

# /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
źródło
systemd uruchomi proces i oczekuje, że rozwidli inny proces, jeśli go używasz Type=forking. Co więcej, nie będzie działać execStartjako rozszerzenie powłoki, więc &na końcu nigdy nie będzie rozumiane jako flaga tła.
grochmal
moje złe i to ja to testowałem. Czy więc pisanie powinno być proste?
madmanali93
2
Jeśli się nie mylę, jekyll jest rzeczą typu szynową, tj. Małym serwerem wey w rubinie. Tak, Type=simplebyłoby właściwe. Poza tym nie jest to aplikacja, którą uruchomiłbym jako root, przynajmniej na komputerze z dostępem do Internetu (co może nie być twoim przypadkiem).
grochmal
Dzięki tak proste działało. Również to polecenie generuje statyczny HTML dla Apache, dzięki czemu jekyll nie działa na serwerze. Myślę, że powinno być dobrze, jeśli działa jako root. Nie jestem jednak pewien, czy to debata.
madmanali93
Och OK, więc to właśnie --incrementaldziała :). Tak, nie widzę problemu z bezpieczeństwem przy regenerowaniu plików jako root. Oczywiście, biorąc pod uwagę, że te pliki nie są dostarczane przez użytkownika.
grochmal

Odpowiedzi:

54

Systemd jest w stanie obsługiwać różne typy usług, w szczególności jeden z poniższych

  • simple - Długotrwały proces, który nie tworzy tła i pozostaje przywiązany do powłoki.
  • forking - Typowy demon, który sam się odrywa od procesu, który go uruchomił, skutecznie tworząc tło.
  • oneshot - Krótkotrwały proces, który ma zakończyć się.
  • dbus - Jak proste, ale powiadomienie o zakończeniu uruchamiania procesów jest wysyłane przez dbus.
  • notify - Jak proste, ale powiadomienie o zakończeniu uruchamiania procesów jest wysyłane przez inotify.
  • idle - Jak proste, ale plik binarny jest uruchamiany po wysłaniu zadania.

W twoim przypadku wybrałeś, Type=forkingco oznacza, że ​​systemd czeka na sam proces rozwidlenia i na zakończenie procesu nadrzędnego, co przyjmuje jako wskazanie pomyślnego rozpoczęcia procesu. Jednak Twój proces tego nie robi - pozostaje na pierwszym planie, więc systemctl startzawiesi się na czas nieokreślony lub do czasu awarii procesów.

Zamiast tego chcesz Type=simple, co jest ustawieniem domyślnym, dzięki czemu możesz całkowicie usunąć linię, aby uzyskać ten sam efekt. W tym trybie systemd nie czeka na zakończenie procesu (ponieważ nie ma pojęcia, kiedy to się stało), więc od razu kontynuuje wykonywanie zależnych usług. W twoim przypadku nie ma ich, więc to nie ma znaczenia.

Mała uwaga na temat bezpieczeństwa:

Usługa jest uruchamiana jako root, jest to odradzane, ponieważ jest mniej bezpieczne niż uruchamianie jej jako nieuprzywilejowanego użytkownika. Powodem tego jest to, że jeśli w jekyll istnieje luka, która w jakiś sposób pozwala na wykonywanie poleceń (być może za pomocą parsowanego kodu), atakujący nie musi nic więcej robić, aby całkowicie przejąć kontrolę nad twoim systemem. Jeśli z drugiej strony jest uruchamiany jako użytkownik nieuprzywilejowany, atakujący może wyrządzić tylko tyle szkód, co ten użytkownik i musi teraz spróbować uzyskać uprawnienia roota, aby całkowicie przejąć kontrolę nad systemem. Po prostu dodaje dodatkową warstwę, którą muszą przejść napastnicy.

Możesz po prostu uruchomić go jako ten sam użytkownik, który uruchamia twój serwer WWW, ale to pozostawia cię otwartym na kolejny potencjalny atak. Jeśli na Twoim serwerze internetowym występuje luka, która pozwala użytkownikowi manipulować plikami w systemie, może modyfikować wygenerowane pliki HTML lub, co gorsza, pliki źródłowe i powodować, że Twój serwer obsłuży wszystko, co zechce. Jeśli jednak wygenerowane pliki i pliki źródłowe są czytelne tylko dla serwera WWW i mogą być zapisywane jako inny nieuprzywilejowany użytkownik, nie będą w stanie tak łatwo zmodyfikować ich poprzez atak na serwer WWW.

Jeśli jednak po prostu podajesz pliki statyczne z tego serwera i aktualizujesz serwer, ataki te są bardzo mało prawdopodobne - ale nadal możliwe. Twoim obowiązkiem jest zrównoważyć ryzyko i koszty związane z jego konfiguracją w zależności od tego, jak krytyczny jest twój system, ale obie te wskazówki są bardzo proste w konfiguracji i nie wymagają żadnych kosztów utrzymania.

Michael Daffin
źródło
0

Oprócz rozwiązania @ Michaela Daffina , możesz także użyć narzędzia demonizacji , aby uzyskać użycie, forkingjak pokazano w poniższym przykładzie.

Biorąc pod uwagę mały skrypt powłoki, który chcę demonizować i który chcę kontrolować nad systemd, zapisałem go jako /home/pi/testscript.sh:

#!/bin/bash

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

Jeśli jeszcze go nie masz, zainstaluj demonizuj, tak jak poniżej:

sudo apt install daemonize

Teraz utwórz plik definicji usługi plików:

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

Nowo utworzona usługa musi zostać ogłoszona systemd:

systemctl daemon-reload

Teraz możesz uruchomić usługę i rozwidlenia skryptów. Zgodnie z oczekiwaniami uruchomienie usługi natychmiast wraca do powłoki. Wynik jest oczywisty:

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

Michał
źródło
Jaka jest zaleta korzystania z daemonize+ Type=forkingzamiast Type=simplezezwalania systemd na zajęcie się uruchomieniem usługi? Type=forkingjest rodzajem ustawienia kompatybilności w systemd do obsługi starszych programów, które są napisane do fork.
Johan Myréen,
Myślę, że to równoważne rozwiązanie; Chciałem tylko przedstawić OP alternatywne rozwiązanie i uświadomić mu to narzędzie, z którego korzystałem już w czasach /etc/init.d, ponieważ pytanie dotyczy również sposobu demonizacji procesu.
Michael