Gerenciando com Segurança Variáveis de Ambiente em Unidades de Serviço do Systemd
Configure variáveis de ambiente do systemd com Environment, EnvironmentFile, drop-ins e tratamento mais seguro de segredos.
Gerenciando com Segurança Variáveis de Ambiente em Unidades de Serviço do Systemd
Variáveis de ambiente são convenientes, mas não são automaticamente privadas. Em um serviço systemd, elas podem aparecer em arquivos de unidade, drop-ins, systemctl show, ferramentas de inspeção de processos, relatórios de falha, logs de depuração ou pacotes de suporte copiados. Isso não significa que você nunca pode usá-las. Significa que você deve ser deliberado sobre o que coloca nelas e quem pode ler os arquivos que as definem.
Este guia cobre as duas diretivas comuns, Environment= e EnvironmentFile=, e depois mostra como usar drop-ins para que a configuração local permaneça separada das unidades gerenciadas pelo pacote.
O Papel das Variáveis de Ambiente no Systemd
Variáveis de ambiente fornecem uma maneira direta de configurar um serviço sem alterar seu código. Quando o systemd inicia um serviço, ele constrói o ambiente do processo e aplica as variáveis definidas na unidade antes de executar ExecStart=.
O systemd fornece duas diretivas principais dentro da seção [Service] de um arquivo de unidade para gerenciar essas variáveis.
1. Definição Direta: A Diretiva Environment
Este método permite definir variáveis diretamente dentro do arquivo de unidade do Systemd. Isso é adequado para parâmetros de configuração não sensíveis que raramente mudam.
Uso e Sintaxe
A diretiva Environment aceita uma lista separada por espaços de atribuições de variáveis no formato "KEY=VALUE".
# /etc/systemd/system/my-app.service
[Unit]
Description=My Application Service
[Service]
User=myuser
WorkingDirectory=/opt/my-app
# Define variables directly in the unit file
Environment="APP_PORT=8080" "NODE_ENV=production"
ExecStart=/usr/local/bin/my-app --start
[Install]
WantedBy=multi-user.target
Limitações e Segurança
Embora conveniente, a diretiva Environment é um lugar ruim para senhas, tokens ou credenciais de banco de dados. Arquivos de unidade são frequentemente armazenados em sistemas de gerenciamento de configuração, copiados em tickets, ou legíveis por operadores que precisam inspecionar o comportamento do serviço, mas não devem ver segredos. Use-a para valores como portas, feature flags, níveis de log e caminhos.
2. Configuração Externa: A Diretiva EnvironmentFile
Para configurações maiores, carregar variáveis de um arquivo externo geralmente é mais limpo. Permite gerenciar as permissões do arquivo de variáveis independentemente do arquivo de unidade principal. Também mantém as unidades fornecidas pelo pacote legíveis enquanto as configurações locais residem em /etc.
Uso e Sintaxe
A diretiva EnvironmentFile recebe um caminho absoluto para um arquivo de configuração. O systemd lê este arquivo linha por linha, tratando cada linha como uma potencial atribuição KEY=VALUE.
[Service]
# Load variables from an external file
EnvironmentFile=/etc/config/my-app-settings.conf
ExecStart=/usr/local/bin/my-app --start
Formato do Arquivo de Ambiente
O arquivo externo deve aderir a um formato simples semelhante ao shell:
- Linhas começando com
#são tratadas como comentários. - Linhas começando com uma atribuição de variável vazia (
VAR=) limparão a variável se ela foi definida anteriormente. - Variáveis são definidas como
KEY=VALUE. - Colocar o valor entre aspas (
KEY="VALUE WITH SPACES") é suportado.
# /etc/config/my-app-settings.conf
# Non-sensitive variables
MAX_WORKERS=4
LOG_LEVEL=INFO
# Sensitive variable (requires strict file permissions and careful access control)
DB_PASSWORD=SecureRandomString12345
Evite hábitos de shell que o analisador de arquivos de ambiente do systemd não suporta da maneira que você espera. Não escreva export KEY=value. Não coloque espaços ao redor do sinal de igual. Se o valor contiver espaços, coloque-o entre aspas. Se o valor contiver aspas literais, barras invertidas ou novas linhas, teste-o antes de confiar nele em produção.
Lidando com Arquivos Ausentes
Por padrão, se o arquivo especificado por EnvironmentFile não existir, o Systemd falhará na inicialização do serviço. Se o arquivo de ambiente for opcional, você pode prefixar o caminho do arquivo com um hífen (-):
EnvironmentFile=-/etc/config/optional-settings.conf
Se o arquivo for prefixado com -, o Systemd ignorará erros causados pela ausência do arquivo.
Melhor Prática: Usando Unidades Drop-in para Dados Sensíveis
Modificar o arquivo de unidade principal (por exemplo, /usr/lib/systemd/system/my-app.service) é geralmente desencorajado, especialmente se o arquivo for gerenciado por um gerenciador de pacotes. Em vez disso, use arquivos de unidade drop-in para aplicar substituições ou adições de configuração.
Esta prática é importante porque separa os padrões do fornecedor da configuração local. Também facilita auditorias: a unidade diz de onde a configuração é carregada, e as permissões nesse arquivo dizem quem pode lê-lo.
Configuração Passo a Passo do Drop-in
1. Localizar/Criar o Diretório Drop-in
Para um serviço chamado my-app.service, o diretório drop-in deve ser nomeado my-app.service.d/ e residir na hierarquia /etc/systemd/system/.
sudo mkdir -p /etc/systemd/system/my-app.service.d/
2. Criar a Substituição de Configuração
Crie um arquivo dentro do diretório drop-in (por exemplo, secrets.conf). Este arquivo precisa apenas da seção [Service] e das diretivas específicas que você deseja substituir ou adicionar.
# /etc/systemd/system/my-app.service.d/secrets.conf
[Service]
# Load the secure credentials file
EnvironmentFile=/etc/secrets/my-app-credentials.env
3. Proteger o Arquivo de Ambiente Externo
Este é o passo de segurança mais crítico. Certifique-se de que o arquivo externo contendo os segredos tenha permissões restritivas. Idealmente, deve ser propriedade de root:root e legível apenas pelo usuário root ou pelo próprio usuário do serviço.
# Create the secrets file
sudo touch /etc/secrets/my-app-credentials.env
# Populate the file with secrets
sudo sh -c 'echo "DB_PASS=S3cr3tP@ssw0rd" >> /etc/secrets/my-app-credentials.env'
# Set restrictive permissions
sudo chmod 600 /etc/secrets/my-app-credentials.env
Se o arquivo referenciado por EnvironmentFile contiver credenciais, mantenha-o legível apenas pela conta que precisa gerenciar o serviço. 0600 root:root é comum quando o systemd lê o arquivo antes de reduzir privilégios com User=, mas alguns modelos operacionais usam um grupo dedicado de propriedade do root e 0640. A parte importante é que usuários comuns não possam ler o arquivo.
Seja honesto sobre o risco restante. Variáveis de ambiente são mais fáceis de lidar do que argumentos de linha de comando codificados, mas ainda não são um sistema completo de gerenciamento de segredos. Para credenciais de alto risco, considere um armazenamento de segredos dedicado, credenciais de curta duração, credenciais do systemd em distribuições mais recentes ou um mecanismo específico da aplicação que leia um arquivo protegido diretamente.
Solução de Problemas e Verificação
Após fazer qualquer alteração em arquivos de unidade ou drop-ins, você deve recarregar a configuração do gerenciador Systemd.
sudo systemctl daemon-reload
sudo systemctl restart my-app.service
Para verificar quais variáveis de ambiente foram carregadas com sucesso pelo Systemd para um serviço em execução, use o comando systemctl show e consulte especificamente a propriedade Environment:
systemctl show my-app.service --property=Environment
Exemplo de Saída (mostrando variáveis carregadas):
Environment=APP_PORT=8080 NODE_ENV=production DB_PASS=S3cr3tP@ssw0rd
Esse comando é útil para depuração, mas também é um lembrete: qualquer pessoa autorizada a executar os comandos de inspeção corretos como root pode ver os valores. Não cole esta saída em chat compartilhado, tickets ou relatórios de bugs públicos sem redigi-la.
Se o serviço falhar ao iniciar, verifique os logs do serviço usando journalctl -xeu my-app.service. Razões comuns para falha relacionadas a variáveis de ambiente incluem:
- Caminho de arquivo incorreto em
EnvironmentFile. - Arquivo ausente (e o caminho não foi prefixado com
-). - Sintaxe de variável incorreta no arquivo de ambiente externo (por exemplo, espaços ao redor do sinal
=).
Padrões Práticos que Funcionam
| Cenário | Diretiva a Usar | Melhor Prática de Localização | Considerações de Segurança |
|---|---|---|---|
| Configuração Estática e Não Sensível | Environment |
Arquivo de unidade direto ou drop-in | Risco de segurança baixo. |
| Credenciais Sensíveis (Segredos) | EnvironmentFile |
Arquivo externo, referenciado via um drop-in (*.service.d/) |
CRÍTICO: O arquivo de ambiente deve ter permissões 0600. |
| Modularidade e Substituições | EnvironmentFile |
Arquivo de unidade drop-in | Separa a configuração dos padrões do fornecedor. |
Ao aproveitar a diretiva EnvironmentFile dentro de uma unidade drop-in dedicada e garantir permissões estritas de arquivo, os administradores podem gerenciar configurações de serviço de forma segura e flexível, aderindo aos princípios de privilégio mínimo e separação de preocupações.
Para um pequeno serviço interno, uma configuração razoável geralmente se parece com isso:
# /etc/systemd/system/my-app.service.d/env.conf
[Service]
Environment="APP_ENV=production"
EnvironmentFile=/etc/my-app/runtime.env
EnvironmentFile=-/etc/my-app/local.env
runtime.env contém valores obrigatórios. local.env é opcional e permite que um operador substitua uma configuração durante uma janela de manutenção sem editar a unidade principal. Após uma alteração:
sudo systemctl daemon-reload
sudo systemctl restart my-app.service
sudo journalctl -u my-app.service -n 50 --no-pager
O hábito mais seguro é simples: mantenha padrões não sensíveis na unidade ou em um arquivo de configuração normal, mantenha segredos fora de unidades de propriedade do pacote, bloqueie qualquer arquivo que contenha credenciais e verifique o ambiente carregado sem vazá-lo para lugares onde não pertence.
Erros Comuns que Vale a Pena Evitar
O primeiro erro é colocar segredos em ExecStart=:
ExecStart=/usr/local/bin/my-app --db-password=s3cret
Isso parece inofensivo quando você está com pressa, mas argumentos de linha de comando são frequentemente mais fáceis de expor do que arquivos de ambiente. Eles podem aparecer em listagens de processos, ferramentas de monitoramento, histórico do shell, relatórios de falha ou definições de serviço copiadas. Se a aplicação suportar a leitura de um arquivo de configuração protegido, isso geralmente é melhor. Se ela espera uma variável de ambiente, use um EnvironmentFile= protegido e mantenha o valor fora da linha de comando.
O segundo erro é editar a unidade do fornecedor diretamente. Uma atualização de pacote pode substituir o arquivo, e a próxima reinicialização pode silenciosamente descartar suas configurações de ambiente. Use um drop-in:
sudo systemctl edit my-app.service
Em seguida, adicione apenas a substituição local:
[Service]
EnvironmentFile=/etc/my-app/my-app.env
O terceiro erro é assumir que o serviço vê o mesmo ambiente de shell que você vê no seu terminal. Geralmente não vê. Seu shell interativo pode ter variáveis de .bashrc, .profile, uma sessão SSH ou uma ferramenta de implantação. Um serviço de sistema inicia a partir do ambiente gerenciado do systemd. Se o aplicativo precisar de PATH, JAVA_HOME, NODE_ENV, LD_LIBRARY_PATH ou um valor semelhante, defina-o explicitamente ou use caminhos absolutos.
Por exemplo, isso é frágil:
ExecStart=npm start
Isso é mais fácil de raciocinar:
WorkingDirectory=/opt/my-app
Environment="NODE_ENV=production"
ExecStart=/usr/bin/npm start
O quarto erro é tornar o arquivo de ambiente gravável pelo usuário do serviço quando não precisa ser. Um aplicativo web que pode sobrescrever seu próprio arquivo de ambiente pode transformar um bug normal de aplicação em um problema de persistência. Em muitas configurações, o usuário do serviço deve ler dados do aplicativo e escrever logs ou uploads, mas não deve ser capaz de reescrever as credenciais usadas para iniciar o serviço.
Quando Variáveis de Ambiente São a Ferramenta Errada
Variáveis de ambiente são populares porque são simples, mas nem sempre são a melhor interface. Se um valor é grande, estruturado, rotacionado com frequência ou compartilhado por vários serviços, um arquivo de configuração real ou armazenamento de segredos geralmente é mais fácil de gerenciar.
Uma URL de banco de dados é uma variável de ambiente razoável:
DATABASE_URL=postgresql://[email protected]:5432/app
Um documento JSON completo de conta de serviço é menos agradável. Colocar entre aspas se torna estranho, quebras de linha acidentais causam falhas e as pessoas são mais propensas a colá-lo em logs durante a depuração. Nesse caso, armazene o JSON em um arquivo protegido e passe o caminho do arquivo:
GOOGLE_APPLICATION_CREDENTIALS=/etc/my-app/google-service-account.json
Em seguida, proteja o arquivo JSON separadamente:
sudo chown root:my-app /etc/my-app/google-service-account.json
sudo chmod 640 /etc/my-app/google-service-account.json
Isso não torna o segredo mágico. O aplicativo ainda pode lê-lo. O root ainda pode lê-lo. Mas evita enfiar um segredo complexo no analisador de ambiente do systemd e torna a auditoria em nível de arquivo mais clara.
Uma Lista de Verificação de Revisão Mais Segura
Antes de reiniciar um serviço que usa variáveis de ambiente, verifique quatro coisas:
systemctl cat my-app.service
sudo ls -l /etc/my-app/my-app.env
sudo systemd-analyze verify /etc/systemd/system/my-app.service
sudo systemctl daemon-reload
systemctl cat confirma quais drop-ins estão ativos. ls -l confirma que as permissões são as que você pretendia. systemd-analyze verify pode detectar alguns problemas de sintaxe da unidade antes de você reiniciar. Não validará todas as configurações específicas do aplicativo, mas ainda é uma proteção útil.
Após a reinicialização, verifique o journal em busca de erros de inicialização:
sudo systemctl restart my-app.service
sudo journalctl -u my-app.service -n 100 --no-pager
Se você precisar confirmar que uma variável foi carregada, consulte-a cuidadosamente e redija a saída antes de compartilhá-la. Para serviços sensíveis, prefiro verificar primeiro uma variável não secreta como APP_ENV ou LOG_LEVEL. Se essa foi carregada do mesmo arquivo, o caminho do arquivo e a sintaxe do analisador provavelmente estão corretos, e você pode não precisar imprimir os valores que contêm segredos.
Um ponto prático final: planeje a rotação antes de precisar dela. Se uma senha ou token estiver armazenado em um arquivo de ambiente, anote qual serviço deve reiniciar após a alteração do valor e se essa reinicialização causa tempo de inatividade. Uma credencial que é fácil de definir, mas difícil de rotacionar, acabará se tornando um incidente. Para serviços pequenos, o runbook de rotação pode ter apenas quatro linhas:
sudoedit /etc/my-app/my-app.env
sudo systemctl restart my-app.service
sudo systemctl status my-app.service
sudo journalctl -u my-app.service -n 50 --no-pager
Isso é suficiente se todos conhecerem o raio de explosão. Para sistemas maiores, prefira credenciais que possam se sobrepor durante a rotação, para que você possa implantar o novo valor, verificá-lo e remover o valor antigo sem uma janela de interrupção apressada.