Moduuli 6 · 6.3
Omat palvelut ja ajastimet
Moduuli 6: Palvelut ja lokit · Lukuaika: 11 minuuttia
Tavoite: Tämän oppitunnin jälkeen osaat ajaa oman ohjelman systemd-palveluna omalla käyttäjällään muutamalla suojausasetuksella sekä ajastaa tehtäviä systemd-ajastimella tai cronilla.
Keskeiset käsitteet: unit-tiedosto, [Unit], [Service], [Install], ExecStart, User=, WorkingDirectory, EnvironmentFile, Restart=, WantedBy, NoNewPrivileges, ProtectSystem, ProtectHome, PrivateTmp, systemd-analyze security, ajastin (timer), OnCalendar, Persistent, crontab
Miksei vain "nohup" tai "screen"?
Kun asennat palvelimelle oman ohjelman (pienen verkkosovelluksen, botin tai skriptin, joka tekee jotain joka yö), käynnistät sen ensimmäisellä kerralla usein käsin: node server.js & tai screen-istunnossa. Se toimii, kunnes kirjaudut ulos, palvelin käynnistyy uudelleen tai ohjelma kaatuu. Silloin se on poissa, eikä kukaan huomaa sitä.
Ohjelman, jonka pitää olla aina käynnissä, kuuluu olla systemd-palvelu. Silloin:
- se käynnistyy itsestään, kun palvelin käynnistyy;
- systemd käynnistää sen uudelleen, jos se kaatuu;
- sen tuloste menee journaliin (oppitunti 6.2);
- se toimii valitsemasi käyttäjän alla ja vain sallimillasi oikeuksilla.
Oma unit-tiedosto
Oletetaan, että sinulla on pieni Node.js-sovellus hakemistossa /opt/factuurapp ja se kuuntelee osoitteessa 127.0.0.1:3000. nginx tai Caddy julkaisee sen myöhemmin ulospäin (moduuli 7).
Vaihe 1: oma käyttäjä ilman kuorta (oppitunti 3.2). Sovelluksen ei tarvitse olla root eikä kirjautua sisään:
anna@web1:~$ sudo adduser --system --group --home /opt/factuurapp --no-create-home factuurapp
anna@web1:~$ sudo chown -R factuurapp:factuurapp /opt/factuurapp/dataItse koodi voi jäädä rootin omistukseen (sovelluksen ei tarvitse muuttaa itseään); vain hakemisto, johon se kirjoittaa, kuuluu palvelukäyttäjälle.
Vaihe 2: unit-tiedosto. Omat unitit sijoitetaan hakemistoon /etc/systemd/system/:
# /etc/systemd/system/factuurapp.service
[Unit]
Description=Factuurapp (Node.js)
After=network-online.target
Wants=network-online.target
[Service]
User=factuurapp
Group=factuurapp
WorkingDirectory=/opt/factuurapp
EnvironmentFile=/etc/factuurapp/env
ExecStart=/usr/bin/node /opt/factuurapp/server.js
Restart=on-failure
RestartSec=5s
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/factuurapp/data
[Install]
WantedBy=multi-user.targetKolme osiota:
- [Unit]: kuvaus ja järjestys.
After=network-online.targetyhdessäWants=:n kanssa varmistaa, että palvelu käynnistyy vasta, kun verkko on käytettävissä. - [Service]: miten ohjelma ajetaan.
ExecStartkoko polun kanssa (systemd ei käytä hakupolkua kuten kuorisi).User=jaGroup=määräävät, kenen alla se toimii.EnvironmentFilelukee asetukset, kuten tietokannan salasanan, erillisestä tiedostosta, jotta ne eivät ole unitissa. - [Install]: mitä
enabletekee.WantedBy=multi-user.targettarkoittaa: käynnisty normaalin käynnistyksen yhteydessä.
Vaihe 3: salaisuudet erikseen, tiukoin oikeuksin.
anna@web1:~$ sudo mkdir /etc/factuurapp
anna@web1:~$ sudo install -m 600 -o root -g root /dev/null /etc/factuurapp/env
anna@web1:~$ sudoedit /etc/factuurapp/envSisältö voi olla esimerkiksi DATABASE_URL=postgres://factuur:...@127.0.0.1/factuur. systemd lukee tiedoston rootina ennen kuin se vaihtaa palvelukäyttäjään, joten rootin omistama 600 riittää.
Vaihe 4: lataa, käynnistä, tarkista.
anna@web1:~$ sudo systemctl daemon-reload
anna@web1:~$ sudo systemctl enable --now factuurapp
anna@web1:~$ systemctl status factuurapp
anna@web1:~$ journalctl -u factuurapp -fSuojausasetukset: pieni vaiva, suuri ero
Esimerkin neljä riviä kohdan RestartSec alla rajoittavat, mitä palvelu voi tehdä, myös silloin kun joku löytää sovelluksestasi haavoittuvuuden:
| Asetus | Vaikutus |
|---|---|
NoNewPrivileges=true | prosessi ja sen lapsiprosessit eivät voi koskaan saada lisää oikeuksia (eivät myöskään sudo:n tai setuid-ohjelmien kautta) |
ProtectSystem=strict | koko tiedostojärjestelmä on palvelulle vain luku -tilassa, paitsi se, minkä sallit ReadWritePaths=:llä |
ProtectHome=true | /home, /root ja /run/user ovat näkymättömiä |
PrivateTmp=true | oma, tyhjä /tmp, jota muut ohjelmat eivät näe |
systemd voi arvioida unitisi:
anna@web1:~$ systemd-analyze security factuurapp
...
→ Overall exposure level for factuurapp.service: 4.9 OK 🙂Mitä pienempi luku, sitä parempi. Palvelu ilman asetuksia saa noin 9 ("UNSAFE"). Nollaan ei tarvitse pyrkiä: jokainen lisäämäsi asetus voi rikkoa jotain, mitä sovellus tarvitsee. Lisää ne yksi kerrallaan ja testaa jokaisen vaiheen jälkeen. Jos jokin lakkaa toimimasta, syy näkyy yleensä journalissa ("Read-only file system", "Permission denied").
Tehtävien ajastaminen: ajastimet
Kun jonkin pitää tapahtua säännöllisesti (varmuuskopio, siivousskripti, raportti), tarvitset ajastimen (timer). systemd-ajastin koostuu kahdesta samannimisestä unitista: .service kertoo, mitä tehdään, ja .timer kertoo, milloin.
# /etc/systemd/system/opruimen.service
[Unit]
Description=Oude exportbestanden opruimen
[Service]
Type=oneshot
User=factuurapp
ExecStart=/usr/bin/find /opt/factuurapp/data/export -type f -mtime +30 -delete# /etc/systemd/system/opruimen.timer
[Unit]
Description=Dagelijks oude exportbestanden opruimen
[Timer]
OnCalendar=*-*-* 03:15:00
RandomizedDelaySec=10m
Persistent=true
[Install]
WantedBy=timers.targetType=oneshot tarkoittaa: ohjelma ajetaan ja se päättyy sen jälkeen. OnCalendar on aikataulu: joka päivä klo 03:15. Voit kirjoittaa myös daily, weekly tai Mon *-*-* 02:00; systemd-analyze calendar "Mon *-*-* 02:00" näyttää, milloin seuraava ajo on. Persistent=true on tärkeä: jos palvelin oli sammuksissa klo 03:15, tehtävä ajetaan heti käynnistyksen jälkeen. RandomizedDelaySec hajauttaa käynnistyshetkeä hieman.
Käyttöön otetaan ajastin, ei palvelua:
anna@web1:~$ sudo systemctl daemon-reload
anna@web1:~$ sudo systemctl enable --now opruimen.timer
anna@web1:~$ systemctl list-timers opruimen
NEXT LEFT LAST PASSED UNIT ACTIVATES
Tue 2026-11-10 03:19:42 CET 15h left - - opruimen.timer opruimen.service
anna@web1:~$ sudo systemctl start opruimen.service # testaa nyt kerran käsin
anna@web1:~$ journalctl -u opruimen.serviceEntä cron?
cron on vanhempi ja yksinkertaisempi työkalu, ja se toimii edelleen hyvin. Rivi käyttäjän crontabissa (crontab -e):
15 3 * * * /usr/local/bin/opruimen.shViisi kenttää ovat minuutti, tunti, kuukauden päivä, kuukausi ja viikonpäivä. Hyvät ja huonot puolet rinnakkain:
| cron | systemd-ajastin | |
|---|---|---|
| Käyttöönotto | yksi rivi | kaksi tiedostoa |
| Väliin jäänyt ajo (palvelin sammuksissa) | ohitetaan | Persistent=true:n kanssa ajetaan jälkikäteen |
| Tuloste ja virheet | sähköpostilla, jos palvelin voi lähettää postia; muuten katoavat | journalissa |
| Suojausasetukset, käyttäjä | skriptin kautta | kaikki systemd:n asetukset (katso yllä) |
| Milloin se ajettiin | hankala selvittää | systemctl list-timers ja journal |
Vakavasti ylläpidetyllä palvelimella ajastimet ovat parempi valinta, etenkin varmuuskopioille (oppitunti 8.2): haluat varmasti tietää, että ne ajettiin ja mitä ne tulostivat. Hiljaa epäonnistuvan cron-rivin huomaat vasta, kun tarvitset varmuuskopiota.
AlmaLinuxissa ja Rocky Linuxissa
Unitit ja ajastimet toimivat täsmälleen samalla tavalla. Yksi ero: SELinux voi estää oman palvelun, joka lukee tai kirjoittaa tiedostoja paikassa, jota SELinux ei odota, tai joka kuuntelee epätavallisessa portissa. Ilmoitus löytyy kohteesta /var/log/audit/audit.log. Älä ratkaise ongelmaa poistamalla SELinuxia käytöstä; etsi oikea nimiö tai sääntö (esimerkiksi semanage:llä).
Yhteenveto
- Ohjelman, jonka pitää olla aina käynnissä, kuuluu olla systemd-palvelu: se käynnistyy itsestään, käynnistyy uudelleen kaatuessaan ja kirjoittaa lokit journaliin.
- Omat unitit hakemistoon
/etc/systemd/system/,User=:llä oma käyttäjä ilman kuorta, salaisuudetEnvironmentFile:iin oikeuksilla 600. NoNewPrivileges,ProtectSystem=strictyhdessäReadWritePaths:n kanssa,ProtectHomejaPrivateTmprajoittavat vahinkoa haavoittuvuuden sattuessa;systemd-analyze securityarvioi unitin.- Ajastin on
.serviceja.timer, jossa onOnCalendar;Persistent=trueajaa väliin jääneet ajot jälkikäteen. Ota käyttöön ajastin, ei palvelua. - cron toimii edelleen, mutta ajastimet antavat paremmat lokit eivätkä jätä ajoja väliin.
Tietovisa (quiz)
Tehtävä
Tehtävä O6.3: Oma palvelu ajastimella. Luo palvelimellesi pieni oma palvelu ajastimella, esimerkiksi skripti, joka kirjoittaa joka yö vapaan levytilan ja systemctl --failed:n tiedostoon tai journaliin. Käytä omaa käyttäjää, Type=oneshot:a, vähintään kahta suojausasetusta ja Persistent=true:a. Testaa komennolla systemctl start, katso tuloste journalista ja kirjaa ylös systemd-analyze security:n pistemäärä. Liitä .service ja .timer ja vastaa tarkistuslistan kysymyksiin.
Tehtävä O6.3: Oma palvelu ajastimella
Tämän tehtävän teet itse, kukaan ei tarkista sitä. Merkitse tekemäsi kohdat ja merkitse tehtävä tehdyksi.
Kun Persistent=true on käytössä, tehtävä ajetaan käynnistyksen jälkeen, jos se jäi väliin. Alle 6:n pistemäärä on hyvä ensimmäiselle palvelulle; kirjoitusoikeudet vain ReadWritePaths-asetuksella.
Merkintäsi ja muistiinpanosi pysyvät vain tässä selaimessa, emme lähetä niitä minnekään. Jos olet kirjautunut sisään, tallennamme kohtaan Omat kurssit vain sen, että olet tehnyt tehtävän.
Lue lisää
- DirectAdmin-ohje 15.1: Cron Jobs: tehtävien automaattinen ajaminen
- Käyttöohje
systemd.exec(5)(User=, ProtectSystem=, NoNewPrivileges=): https://manpages.debian.org/trixie/systemd/systemd.exec.5.en.html - Käyttöohje
systemd.timer(5)(OnCalendar=, Persistent=): https://manpages.debian.org/trixie/systemd/systemd.timer.5.en.html - Käyttöohje
systemd.time(7)(aikataulut): https://manpages.debian.org/trixie/systemd/systemd.time.7.en.html
