Dịch vụ Systemd chạy mà không thoát


30

Tôi đã tạo dịch vụ của riêng mình cho jekyll và khi tôi bắt đầu dịch vụ, có vẻ như nó không chạy như một quá trình nền vì tôi buộc phải ctrl+ cra khỏi nó. Nó chỉ ở phía trước vì --watch. Tôi không chắc chắn làm thế nào để đi xung quanh nó và làm cho nó để nó chạy trong nền. Có suy nghĩ gì không?

# /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

systemd sẽ bắt đầu quá trình của bạn và hy vọng nó sẽ rẽ nhánh một quá trình khác nếu bạn đang sử dụng Type=forking. Hơn nữa, nó sẽ không chạy execStartdưới dạng mở rộng shell, do đó, &ở cuối sẽ không bao giờ được hiểu là cờ nền.
hóa học

xấu của tôi và tôi đã thử nghiệm nó. Vậy nên gõ đơn giản?
madmanali93

2
Nếu tôi không nhầm thì jekyll là một loại rails điều, tức là một máy chủ web nhỏ trong ruby. Vì vậy, yeah, Type=simplesẽ thích hợp. Ngoài ra, đây không phải là loại ứng dụng tôi sẽ chạy bằng root, ít nhất là không phải trên máy phải đối mặt với internet (có thể không phải là trường hợp của bạn).
grochmal

Cảm ơn bạn đã làm việc đơn giản. Ngoài ra, lệnh này tạo ra html tĩnh cho Apache để jekyll không phục vụ trên máy chủ. Tôi nghĩ nó sẽ ổn nếu nó chạy như root. Không chắc chắn mặc dù đã tranh luận về nó.
madmanali93

Oh OK, đó là những gì --incrementalkhông :). Vâng, tôi thấy không có vấn đề bảo mật trong việc tạo lại các tập tin như root. Tất nhiên, cho rằng những tập tin đó không phải là người dùng cung cấp.
grochmal

Câu trả lời:


54

Systemd có thể xử lý nhiều loại dịch vụ khác nhau, cụ thể là một trong những điều sau đây

  • simple - Một quá trình dài không tự nền và vẫn gắn liền với vỏ.
  • forking - Một daemon điển hình tự tách nó ra khỏi quá trình chạy nó, làm nền chính nó một cách hiệu quả.
  • oneshot - Một quá trình ngắn ngủi dự kiến ​​sẽ thoát.
  • dbus - Giống như đơn giản, nhưng thông báo về quá trình hoàn thiện khởi động được gửi qua dbus.
  • notify - Giống như đơn giản, nhưng thông báo về quá trình hoàn thiện khởi động được gửi qua inotify.
  • idle - Giống như đơn giản, nhưng nhị phân được bắt đầu sau khi công việc đã được gửi đi.

Trong trường hợp của bạn, bạn đã chọn Type=forkingđiều đó có nghĩa là systemd đang chờ quá trình tự rẽ nhánh và quá trình cha mẹ kết thúc, điều này được coi là một dấu hiệu cho thấy quá trình đã bắt đầu thành công. Tuy nhiên, quy trình của bạn không thực hiện điều này - nó vẫn ở phía trước và do đó systemctl startsẽ treo vô thời hạn hoặc cho đến khi các quy trình gặp sự cố.

Thay vào đó, bạn muốn Type=simple, đó là mặc định để bạn có thể loại bỏ hoàn toàn dòng để có được hiệu ứng tương tự. Trong chế độ này, systemd không đợi các quá trình kết thúc khởi động (vì nó không có cách nào để biết khi nào điều này xảy ra) và vì vậy tiếp tục thực hiện và thực hiện các dịch vụ phụ thuộc ngay lập tức. Trong trường hợp của bạn không có vì vậy điều này không quan trọng.

Một lưu ý nhỏ về bảo mật:

Bạn đang chạy dịch vụ với quyền root, điều này không được khuyến khích vì nó kém an toàn hơn so với việc chạy nó với tư cách là người dùng không có quyền. Lý do cho điều này là nếu có một lỗ hổng trong jekyll bằng cách nào đó cho phép thực thi các lệnh (có thể thông qua mã mà nó đang phân tích cú pháp) thì kẻ tấn công không cần phải làm gì khác để sở hữu hoàn toàn hệ thống của bạn. Mặt khác, nếu nó được chạy như một người dùng không có đặc quyền, kẻ tấn công chỉ có thể gây ra nhiều thiệt hại như người dùng đó và giờ phải cố gắng giành quyền root để sở hữu hoàn toàn hệ thống của bạn. Nó chỉ đơn giản là thêm một lớp tấn công phải đi mặc dù.

Bạn có thể chỉ cần chạy nó với cùng một người dùng đang chạy máy chủ web của bạn, nhưng điều này khiến bạn mở ra một cuộc tấn công tiềm năng khác. Nếu có một lỗ hổng trong máy chủ web của bạn cho phép người dùng thao tác các tệp trên hệ thống của bạn, họ có thể sửa đổi các tệp html được tạo hoặc tệ nhất là các tệp nguồn và khiến máy chủ của bạn phục vụ mọi thứ họ muốn. Tuy nhiên, nếu các tệp được tạo và các tệp nguồn chỉ có thể đọc được bởi máy chủ web và có thể ghi là một người dùng không có đặc quyền khác, họ sẽ không thể sửa đổi chúng bằng cách tấn công máy chủ web một cách dễ dàng.

Tuy nhiên, nếu bạn chỉ đơn giản phục vụ các tệp tĩnh từ máy chủ này và cập nhật máy chủ thì những cuộc tấn công này rất khó xảy ra - nhưng vẫn có thể xảy ra. Bạn có trách nhiệm cân nhắc rủi ro so với chi phí thiết lập dựa trên mức độ quan trọng của hệ thống của bạn nhưng cả hai mẹo này đều rất đơn giản để thiết lập và bên cạnh không có chi phí bảo trì.


0

Ngoài giải pháp của @ Michael Daffin , bạn cũng có thể sử dụng công cụ daemonize để đạt được việc sử dụng forkingnhư trong ví dụ sau.

Đưa ra một kịch bản shell nhỏ mà tôi muốn trình bày và tôi muốn kiểm soát systemd, tôi đã lưu nó dưới dạng /home/pi/testscript.sh:

#!/bin/bash

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

Nếu bạn chưa có nó, hãy cài đặt daemonize, như thế này:

sudo apt install daemonize

Bây giờ tạo tệp định nghĩa dịch vụ tệp:

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

Dịch vụ mới được tạo phải được thông báo tới systemd:

systemctl daemon-reload

Bây giờ bạn có thể bắt đầu dịch vụ và các tập lệnh forks. Như mong đợi, dịch vụ bắt đầu trở lại vỏ ngay lập tức. Kết quả rất rõ ràng:

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


Lợi thế của việc sử dụng daemonize+ Type=forkingthay vì Type=simplevà để systemd đảm nhiệm việc bắt đầu dịch vụ là gì? Type=forkinglà một loại cài đặt tương thích trong systemd để hỗ trợ các chương trình cũ được ghi vào fork.
Johan Myréen

Tôi nghĩ đó là một giải pháp tương đương; Tôi chỉ muốn cung cấp cho OP một giải pháp thay thế và để anh ấy biết về công cụ này mà tôi đã sử dụng trong /etc/init.d lần vì câu hỏi cũng là một chút về cách trình bày quy trình.
Michael
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.