Interface web para ver, criar, editar, excluir e executar entradas do crontab do usuário do sistema.
A aplicação executa comandos shell com os privilégios do usuário que roda o processo. Quem tem acesso a ela tem acesso ao shell do servidor. Não a exponha sem login, sem HTTPS e sem restringir os IPs de origem.
- Python 3.8 ou superior
- Node.js (apenas para reconstruir o CSS do Tailwind)
- Um sistema com o binário
crontab(Linux). Em Windows, useCRONTAB_BACKEND=filepara desenvolvimento.
python3 -m pip install -r requirements.txt
python3 manage.py migrate
python3 manage.py createsuperuserToda a configuração vem de variáveis de ambiente. O arquivo .env.example
documenta cada uma. Ele não é carregado automaticamente — aponte para ele com
EnvironmentFile= na unit do systemd, ou exporte as variáveis na sua sessão.
| Variável | Padrão | Descrição |
|---|---|---|
SECRET_KEY |
— | Obrigatória quando DEBUG=False. |
DEBUG |
False |
Nunca True em servidor exposto. |
ALLOWED_HOSTS |
vazio | Hosts aceitos, separados por vírgula. |
CSRF_TRUSTED_ORIGINS |
vazio | Origens aceitas, separadas por vírgula. |
ALLOWED_IPS |
vazio | IPs ou faixas CIDR que podem acessar. Vazia com DEBUG=False bloqueia tudo. |
TRUSTED_PROXY_COUNT |
0 |
Proxies reversos à frente. 0 ignora X-Forwarded-For. |
TRUSTED_PROXIES |
vazio | IPs/faixas dos proxies autorizados a preencher X-Forwarded-For. Vazia = header sempre ignorado, mesmo com TRUSTED_PROXY_COUNT > 0. |
CRONTAB_BACKEND |
system |
system usa crontab -l/crontab -; file usa CRONTAB_FILE. |
CRONTAB_FILE |
— | Caminho do arquivo quando o backend é file. |
CRONTAB_BACKUP_DIR |
backups/ |
Onde as cópias do crontab são gravadas. |
CRONTAB_BACKUP_KEEP |
20 |
Quantas cópias manter. 0 mantém todas. |
CRON_RUN_TIMEOUT |
30 |
Segundos até abortar a execução manual de um job. |
A chave que estava versionada neste repositório permanece no histórico do git e deve ser considerada comprometida. Gere uma nova e coloque-a no ambiente:
python3 manage.py generate_secret_keyTrocar a chave invalida todas as sessões, obrigando novo login.
Em Windows não existe crontab. Aponte o backend para um arquivo:
set DEBUG=1
set CRONTAB_BACKEND=file
set CRONTAB_FILE=.\dev-crontab.txt
python manage.py runserverReconstruir o CSS depois de mexer em classes do Tailwind:
npm run devpython3 manage.py test core -v 2A suíte usa o backend de arquivo e não depende do binário crontab, então roda
em qualquer sistema operacional.
runserver não é servidor de produção. Rode com gunicorn atrás de um nginx com
HTTPS, e com TRUSTED_PROXY_COUNT=1 para que a allowlist enxergue o IP real do
cliente. Bloqueio de força bruta no login fica por conta do fail2ban ou do CSF —
a aplicação não faz esse controle.
Gunicorn deve escutar só em loopback (127.0.0.1 ou socket Unix) — nunca em
0.0.0.0. Se o processo do gunicorn for alcançável diretamente, sem passar
pelo nginx, um atacante pode forjar o header X-Forwarded-For e tentar burlar
a allowlist de IP. TRUSTED_PROXY_COUNT sozinho não impede isso: é preciso
também configurar TRUSTED_PROXIES (ver tabela acima e .env.example) com o
endereço do próprio nginx — tipicamente 127.0.0.1, se colocado no mesmo
host da aplicação. Com TRUSTED_PROXIES vazia, o header é sempre ignorado,
mesmo com TRUSTED_PROXY_COUNT configurado.
SECURE_SSL_REDIRECT fica True por padrão quando DEBUG=False. O Django só
enxerga a requisição original como https quando o nginx manda o header
X-Forwarded-Proto. Se esse header não for configurado, toda requisição chega
como http aos olhos do Django, que redireciona para https; o nginx
reencaminha para o gunicorn de novo como http; e o resultado é um loop
infinito de redirecionamento. A checagem de Referer do CSRF também depende
desse header estar correto. Não pule essa configuração.
Unit do systemd (/etc/systemd/system/cron-manager.service), apontando para
o .env com EnvironmentFile= e o gunicorn preso em loopback:
[Unit]
Description=Cron Manager
After=network.target
[Service]
User=cron-manager
WorkingDirectory=/opt/cron-manager
EnvironmentFile=/opt/cron-manager/.env
ExecStart=/opt/cron-manager/venv/bin/gunicorn \
--bind 127.0.0.1:8000 \
--workers 3 \
cron_manager.wsgi:application
Restart=on-failure
[Install]
WantedBy=multi-user.targetBloco location do nginx, encaminhando para o gunicorn com os headers que a
aplicação precisa para funcionar corretamente (X-Forwarded-Proto evita o
loop de redirecionamento acima; X-Forwarded-For alimenta a allowlist de IP,
desde que TRUSTED_PROXIES inclua o IP do próprio nginx):
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}python3 manage.py generate_secret_keypython3 manage.py format_code