บริการ Systemd ทำงานโดยไม่ออกจากเครื่อง


30

ฉันสร้างบริการของตัวเองสำหรับ jekyll และเมื่อฉันเริ่มบริการดูเหมือนว่ามันจะไม่ทำงานเป็นกระบวนการพื้นหลังเพราะฉันถูกบังคับให้ctrl+ cออกจากมัน มันอยู่ในเบื้องหน้าเพราะ - ดู ฉันไม่แน่ใจว่าจะไปรอบ ๆ และทำให้มันทำงานในพื้นหลังได้อย่างไร ความคิดใด ๆ

# /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 Type=forkingจะเริ่มต้นกระบวนการของคุณและคาดว่าจะแยกกระบวนการอื่นถ้าคุณกำลังใช้ นอกจากนี้มันจะไม่ทำงานexecStartเป็นการขยายตัวของเชลล์ดังนั้น&ในตอนท้ายจะไม่ถูกเข้าใจว่าเป็นแฟล็กพื้นหลัง
grochmal

ของฉันไม่ดี & คือฉันกำลังทดสอบมัน ดังนั้นควรพิมพ์ง่าย ๆ ?
madmanali93

2
ถ้าฉันไม่ได้ผิดพลาด jekyll เป็นประเภทรถไฟสิ่งเช่นเว็บเซิร์ฟเวอร์เล็ก ๆ ในทับทิม ดังนั้นใช่Type=simpleจะมีความเหมาะสม นอกจากนี้นี่ไม่ใช่แอปพลิเคชันที่ฉันจะเรียกใช้ในฐานะรูทอย่างน้อยก็ไม่ได้อยู่ในเครื่องที่ใช้อินเทอร์เน็ต (ซึ่งอาจไม่ใช่กรณีของคุณ)
grochmal

ขอบคุณใช่ง่ายทำงาน นอกจากนี้คำสั่งนี้จะสร้าง html แบบคงที่สำหรับ Apache เพื่อให้ jekyll ไม่ได้ให้บริการบนเซิร์ฟเวอร์ ฉันคิดว่ามันควรจะดีถ้ามันทำงานเป็นราก ไม่แน่ใจว่ากำลังถกเถียงกัน
madmanali93

โอ้ตกลงนั่นคือสิ่งที่--incrementalทำ :) ใช่ฉันเห็นว่าไม่มีปัญหาด้านความปลอดภัยในการสร้างไฟล์ใหม่เป็น root แน่นอนว่าไฟล์เหล่านั้นไม่ได้จัดทำโดยผู้ใช้
grochmal

คำตอบ:


54

Systemd สามารถจัดการประเภทบริการที่แตกต่างหลากหลายโดยเฉพาะอย่างใดอย่างหนึ่งต่อไปนี้

  • simple - กระบวนการที่ใช้เวลายาวนานซึ่งไม่ได้ทำให้พื้นหลังตัวมันเองและยังคงติดอยู่กับเปลือก
  • forking - ภูตทั่วไปที่แยกตัวมันเองออกจากกระบวนการที่รันมัน
  • oneshot - กระบวนการมีอายุสั้นที่คาดว่าจะออก
  • dbus - เหมือนง่าย แต่การแจ้งเตือนกระบวนการเสร็จสิ้นการเริ่มต้นจะถูกส่งผ่าน dbus
  • notify - เหมือนง่าย แต่การแจ้งเตือนกระบวนการเสร็จสิ้นการเริ่มต้นจะถูกส่งไปโดยไม่แจ้งข้อมูล
  • idle - เรียบง่าย แต่ไบนารีเริ่มต้นหลังจากงานถูกส่งไป

ในกรณีของคุณคุณเลือกType=forkingซึ่งหมายความว่า systemd กำลังรอให้กระบวนการแยกตัวเองและทำให้กระบวนการหลักสิ้นสุดซึ่งใช้เป็นตัวบ่งชี้ว่ากระบวนการเริ่มต้นได้สำเร็จ อย่างไรก็ตามกระบวนการของคุณไม่ได้ทำสิ่งนี้ - ยังคงอยู่ในเบื้องหน้าและsystemctl startจะหยุดอย่างไม่มีกำหนดหรือจนกว่ากระบวนการขัดข้อง

แต่คุณต้องการType=simpleซึ่งเป็นค่าเริ่มต้นเพื่อให้คุณสามารถลบบรรทัดทั้งหมดเพื่อให้ได้ผลเหมือนกัน ในโหมดนี้ systemd ไม่รอให้กระบวนการเสร็จสิ้นการเริ่มต้น (เนื่องจากไม่มีวิธีทราบเมื่อเกิดเหตุการณ์นี้) และดำเนินการต่อและให้บริการต่อเนื่องทันที ในกรณีของคุณไม่มีดังนั้นสิ่งนี้ไม่สำคัญ

หมายเหตุเล็ก ๆ เกี่ยวกับความปลอดภัย:

คุณกำลังเรียกใช้บริการในฐานะผู้ใช้รูทซึ่งมีความปลอดภัยน้อยกว่าการใช้งานในฐานะผู้ใช้ที่ไม่มีสิทธิ์ เหตุผลนี้คือถ้ามีช่องโหว่ใน jekyll ที่อย่างใดอนุญาตให้ดำเนินการคำสั่ง (อาจผ่านรหัสมันคือการแยกวิเคราะห์) จากนั้นผู้โจมตีต้องทำอะไรอย่างอื่นเพื่อเป็นเจ้าของระบบของคุณอย่างสมบูรณ์ หากในทางตรงกันข้ามมันถูกเรียกใช้ในฐานะผู้ใช้ที่ไม่มีสิทธิพิเศษผู้โจมตีจะสามารถสร้างความเสียหายได้มากเท่ากับผู้ใช้รายนั้นและต้องพยายามที่จะได้รับสิทธิ์พิเศษในการเป็นเจ้าของระบบของคุณโดยสมบูรณ์ เพียงเพิ่มผู้โจมตีเลเยอร์พิเศษต้องดำเนินการต่อไป

คุณสามารถเรียกใช้ในฐานะผู้ใช้เดียวกับที่ใช้งานเว็บเซิร์ฟเวอร์ของคุณ แต่นี่จะทำให้คุณเปิดการโจมตีอื่นได้ หากมีช่องโหว่ในเว็บเซิร์ฟเวอร์ของคุณที่อนุญาตให้ผู้ใช้จัดการไฟล์ในระบบของคุณพวกเขาสามารถแก้ไขไฟล์ html ที่สร้างขึ้นหรือแย่ที่สุดไฟล์ต้นฉบับและทำให้เซิร์ฟเวอร์ของคุณให้บริการสิ่งที่พวกเขาต้องการ อย่างไรก็ตามหากไฟล์ที่สร้างขึ้นและไฟล์ต้นฉบับสามารถอ่านได้โดยเว็บเซิร์ฟเวอร์เท่านั้นและสามารถเขียนได้เป็นผู้ใช้ที่ไม่มีสิทธิพิเศษพวกเขาจะไม่สามารถแก้ไขได้อย่างง่ายดายโดยการโจมตีเว็บเซิร์ฟเวอร์

อย่างไรก็ตามหากคุณเพียงแค่ให้บริการไฟล์คงที่จากเซิร์ฟเวอร์นี้และทำให้เซิร์ฟเวอร์ทันสมัยอยู่เสมอการโจมตีเหล่านี้ไม่น่าเป็นไปได้มาก - แต่ยังคงเป็นไปได้ เป็นความรับผิดชอบของคุณในการชั่งน้ำหนักความเสี่ยงเทียบกับค่าใช้จ่ายในการตั้งค่าตามความสำคัญของระบบของคุณ แต่เคล็ดลับทั้งสองนี้ง่ายมากในการตั้งค่าและถัดจากไม่มีค่าใช้จ่ายในการบำรุงรักษา


0

นอกเหนือจากโซลูชันของ@ Michael Daffinคุณยังสามารถใช้เครื่องมือdaemonizeเพื่อทำให้การใช้งานforkingเป็นไปตามที่แสดงในตัวอย่างต่อไปนี้

รับเชลล์สคริปต์เล็กน้อยที่ฉันต้องการ daemonize และที่ฉันต้องการควบคุม systemd ฉันบันทึกเป็น/home/pi/testscript.sh:

#!/bin/bash

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

หากคุณยังไม่มีให้ติดตั้ง daemonize ดังนี้:

sudo apt install daemonize

ตอนนี้สร้างไฟล์นิยามบริการไฟล์:

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

บริการที่เพิ่งสร้างใหม่จะต้องประกาศให้ systemd:

systemctl daemon-reload

ตอนนี้คุณสามารถเริ่มบริการและสคริปต์ส้อม ตามที่คาดไว้การเริ่มต้นบริการจะกลับมาที่เชลล์ทันที ผลลัพธ์ชัดเจน:

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


อะไรคือข้อดีของการใช้daemonize+ Type=forkingแทนType=simpleและให้ systemd ดูแลการเริ่มบริการ Type=forkingเป็นประเภทของการตั้งค่าความเข้ากันได้ใน systemd เพื่อสนับสนุนโปรแกรมดั้งเดิมที่เขียนลงใน fork
Johan Myréen

ฉันคิดว่ามันเป็นทางออกที่เท่าเทียมกัน; ฉันแค่ต้องการให้ทางเลือกแก่ OP เป็นวิธีแก้ปัญหาและทำให้เขาได้ตระหนักถึงเครื่องมือนี้ซึ่งฉันได้ใช้ไปแล้วใน /etc/init.d ครั้งเนื่องจากคำถามนั้นเล็กน้อยเกี่ยวกับวิธีที่จะทำให้กระบวนการทำงานดีขึ้น
ไมเคิล
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.