Módulo 6 · 6.3
Serviços próprios e temporizadores
Módulo 6: Serviços e registos · Tempo de leitura: 11 minutos
Objetivo de aprendizagem: Após esta lição, saberá executar um programa próprio como serviço systemd com um utilizador próprio e algumas opções de segurança, e agendar tarefas com um temporizador systemd ou com cron.
Conceitos-chave: ficheiro de unidade, [Unit], [Service], [Install], ExecStart, User=, WorkingDirectory, EnvironmentFile, Restart=, WantedBy, NoNewPrivileges, ProtectSystem, ProtectHome, PrivateTmp, systemd-analyze security, temporizador (timer), OnCalendar, Persistent, crontab
Porque não simplesmente "nohup" ou "screen"?
Quem instala um programa próprio num servidor (uma pequena aplicação web, um bot, um script que faz algo todas as noites) começa muitas vezes por iniciá-lo à mão: node server.js &, ou numa sessão screen. Isso funciona até terminar a sessão, reiniciar o servidor ou o programa falhar. Nessa altura, o programa desaparece e ninguém dá por isso.
Um programa que tem de estar sempre em execução deve ser um serviço systemd. Assim:
- arranca automaticamente no arranque do sistema;
- o systemd volta a iniciá-lo se falhar;
- a saída fica no journal (lição 6.2);
- é executado com um utilizador à sua escolha, com as permissões que o senhor autorizar.
Um ficheiro de unidade próprio
Suponha que tem uma pequena aplicação Node.js em /opt/factuurapp, que escuta em 127.0.0.1:3000. Mais tarde, o nginx ou o Caddy vão disponibilizá-la para o exterior (módulo 7).
Passo 1: um utilizador próprio sem shell (lição 3.2). A aplicação não precisa de ser root nem de iniciar sessão:
anna@web1:~$ sudo adduser --system --group --home /opt/factuurapp --no-create-home factuurapp
anna@web1:~$ sudo chown -R factuurapp:factuurapp /opt/factuurapp/dataO código propriamente dito pode continuar a pertencer ao root (a aplicação não precisa de se alterar a si própria); só a pasta onde escreve pertence ao utilizador do serviço.
Passo 2: o ficheiro de unidade. As unidades próprias ficam em /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.targetOs três blocos:
- [Unit]: descrição e ordem de arranque.
After=network-online.targetcomWants=garante que o serviço só arranca quando a rede estiver disponível. - [Service]: como o programa é executado.
ExecStartcom o caminho completo (o systemd não usa um caminho de pesquisa como a sua shell).User=eGroup=determinam com que utilizador.EnvironmentFilelê definições, como uma palavra-passe de base de dados, a partir de um ficheiro separado, para que não fiquem na unidade. - [Install]: o que
enablefaz.WantedBy=multi-user.targetsignifica: arrancar com um arranque normal do sistema.
Passo 3: segredos à parte, com permissões restritas.
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/envConteúdo, por exemplo, DATABASE_URL=postgres://factuur:...@127.0.0.1/factuur. O systemd lê o ficheiro como root antes de mudar para o utilizador do serviço, por isso 600 com dono root é suficiente.
Passo 4: carregar, iniciar, verificar.
anna@web1:~$ sudo systemctl daemon-reload
anna@web1:~$ sudo systemctl enable --now factuurapp
anna@web1:~$ systemctl status factuurapp
anna@web1:~$ journalctl -u factuurapp -fOpções de segurança: pouco esforço, grande diferença
As quatro linhas abaixo de RestartSec no exemplo limitam o que o serviço pode fazer, mesmo que alguém encontre uma falha na sua aplicação:
| Opção | Efeito |
|---|---|
NoNewPrivileges=true | o processo e os seus processos filhos nunca podem obter mais permissões (nem através de sudo ou de programas setuid) |
ProtectSystem=strict | todo o sistema de ficheiros fica só de leitura para o serviço, exceto o que autorizar com ReadWritePaths= |
ProtectHome=true | /home, /root e /run/user ficam invisíveis |
PrivateTmp=true | um /tmp próprio e vazio, que os outros programas não veem |
O systemd pode avaliar a sua unidade:
anna@web1:~$ systemd-analyze security factuurapp
...
→ Overall exposure level for factuurapp.service: 4.9 OK 🙂Quanto mais baixo o número, melhor. Um serviço sem opções obtém cerca de 9 ("UNSAFE"). Não precisa de chegar a zero: cada opção que acrescenta pode avariar algo de que a aplicação precisa. Acrescente-as uma a uma e teste após cada passo. Se algo deixar de funcionar, a causa está normalmente no journal ("Read-only file system", ou seja, sistema de ficheiros só de leitura; "Permission denied", ou seja, permissão recusada).
Agendar tarefas: temporizadores
Para algo que tem de acontecer a horas fixas (uma cópia de segurança, um script de limpeza, um relatório) precisa de um temporizador (timer). Um temporizador systemd é composto por duas unidades com o mesmo nome: um .service que diz o que deve acontecer e um .timer que diz quando.
# /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 significa: o programa é executado e depois termina. OnCalendar é o horário: todos os dias às 03:15. Também pode escrever daily, weekly ou Mon *-*-* 02:00; systemd-analyze calendar "Mon *-*-* 02:00" mostra quando será a próxima execução. Persistent=true é importante: se o servidor estava desligado às 03:15, a tarefa é executada logo após o arranque. RandomizedDelaySec distribui um pouco o momento de início.
A ativação faz-se com o temporizador, não com o serviço:
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 # testar agora uma vez manualmente
anna@web1:~$ journalctl -u opruimen.serviceE o cron?
O cron é a ferramenta mais antiga e mais simples, e continua a funcionar bem. Uma linha na crontab de um utilizador (crontab -e):
15 3 * * * /usr/local/bin/opruimen.shOs cinco campos são minuto, hora, dia do mês, mês e dia da semana. Vantagens e desvantagens lado a lado:
| cron | temporizador systemd | |
|---|---|---|
| Configuração | uma linha | dois ficheiros |
| Execução perdida (servidor desligado) | é ignorada | com Persistent=true, é recuperada |
| Saída e erros | por e-mail, se o servidor conseguir enviar e-mail; caso contrário, perdem-se | no journal |
| Opções de segurança, utilizador | através do próprio script | tudo o que o systemd oferece (ver acima) |
| Ver quando foi executado | difícil | systemctl list-timers e o journal |
Para um servidor que gere a sério, os temporizadores são a melhor escolha, sobretudo para cópias de segurança (lição 8.2): quer ter a certeza de que foram executadas e do que reportaram. Uma linha cron que falha em silêncio só é notada quando precisar da cópia de segurança.
No AlmaLinux e no Rocky Linux
As unidades e os temporizadores funcionam exatamente da mesma forma. Uma diferença: o SELinux pode bloquear um serviço próprio que leia ou escreva ficheiros num local que o SELinux não espera, ou que escute numa porta invulgar. A mensagem fica em /var/log/audit/audit.log. Não desative o SELinux para resolver o problema; procure a etiqueta ou regra correta (por exemplo, com semanage).
Resumo
- Um programa que tem de estar sempre em execução deve ser um serviço systemd: arranque automático, reinício após uma falha, registos no journal.
- Unidades próprias em
/etc/systemd/system/, comUser=um utilizador próprio sem shell, segredos numEnvironmentFilecom permissões 600. NoNewPrivileges,ProtectSystem=strictcomReadWritePaths,ProtectHomeePrivateTmplimitam os danos em caso de falha de segurança;systemd-analyze securityavalia a unidade.- Um temporizador é um
.servicemais um.timercomOnCalendar;Persistent=truerecupera execuções perdidas. Ative o temporizador, não o serviço. - O cron continua a funcionar, mas os temporizadores dão melhores registos e não perdem execuções.
Questionário
Exercício
Exercício O6.3: Um serviço próprio com temporizador. Crie no seu servidor um pequeno serviço próprio com um temporizador, por exemplo um script que todas as noites escreve o espaço livre em disco e systemctl --failed num ficheiro ou no journal. Utilize um utilizador próprio, Type=oneshot, pelo menos duas opções de segurança e Persistent=true. Teste com systemctl start, veja a saída no journal e anote a pontuação de systemd-analyze security. Cole o seu .service e o seu .timer e responda às perguntas da lista de verificação.
Exercício O6.3: Um serviço próprio com temporizador
Este exercício faz você mesmo; ninguém o verifica. Assinale o que já fez e marque o exercício como concluído.
Com Persistent=true, a tarefa é executada na mesma após o arranque. Uma pontuação abaixo de 6 é boa para um primeiro serviço; permissões de escrita apenas através de ReadWritePaths.
As suas marcações e notas ficam apenas neste navegador; não as enviamos para lado nenhum. Se tiver sessão iniciada, guardamos em Os meus cursos apenas que concluiu o exercício.
Para saber mais
- Manual DirectAdmin 15.1: Tarefas Cron Jobs: executar tarefas automaticamente
- Manual
systemd.exec(5)(User=, ProtectSystem=, NoNewPrivileges=): https://manpages.debian.org/trixie/systemd/systemd.exec.5.en.html - Manual
systemd.timer(5)(OnCalendar=, Persistent=): https://manpages.debian.org/trixie/systemd/systemd.timer.5.en.html - Manual
systemd.time(7)(horários): https://manpages.debian.org/trixie/systemd/systemd.time.7.en.html
