Execução com privilégios mínimos e disciplina no sudo
Remova privilégios, restrinja cuidadosamente as regras do sudo e valide o UID efetivo antes de operações arriscadas.
Execução com privilégios mínimos e disciplina no sudo é uma aula grátis de DevOps Bootcamp no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DevOps Bootcamp, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DevOps Bootcamp inclui 4 aulas no total.
Por que o Princípio do Menor Privilégio é Importante em Scripts de Shell
A maioria das violações de segurança em automações acontece não por causa de explorações exóticas, mas porque os scripts são executados com mais privilégios do que precisam. Um trabalho do cron executado como root que só precisa alternar um arquivo de registro é um acidente prestes a acontecer.
O princípio do menor privilégio afirma: todo processo deve operar usando somente as permissões necessárias para realizar sua tarefa — e nada além disso. Em scripts Bash, isso significa:
- Executar como um usuário sem privilégios sempre que possível
- Elevar para root somente nos comandos específicos que exigem isso
- Remover os privilégios assim que o trabalho elevado terminar
- Nunca armazenar nem herdar credenciais além do seu escopo
Esta lição apresenta as técnicas concretas: delimitação de sudo, remoção de privilégios com su, proteções de validação de UID e reforço do sudoers — construindo um modelo disciplinado de privilégios para scripts de produção.
Verificando o UID Efetivo Antes de Operações Arriscadas
Antes de qualquer bloco de código que realmente exija root, seu script deve verificar se está sendo executado com o UID efetivo esperado. Nunca presuma; sempre faça uma asserção.
$EUID é uma variável especial do Bash que contém o ID de usuário efetivo do processo atual. Root sempre tem EUID 0. Verificá-lo no início de um script — ou ao redor de um bloco privilegiado — impede a execução acidental sob a identidade errada.
Use este padrão de proteção:
#!/usr/bin/env bash
set -euo pipefail
# Guard: this script must NOT run as root.
if [[ "$EUID" -eq 0 ]]; then
echo "ERROR: Do not run this script as root. Use a normal user account." >&2
exit 1
fi
echo "Running as UID $EUID — proceeding safely."Exigindo Root Somente Quando Necessário
Alguns scripts precisam legitimamente de root. Nesse caso, a proteção é invertida: falhe imediatamente se root estiver ausente, em vez de deixar o script chegar a uma chamada de sistema privilegiada e produzir um erro de permissão confuso no meio da execução.
Combinar a saída antecipada com uma mensagem de uso útil torna os scripts autoexplicativos:
#!/usr/bin/env bash
set -euo pipefail
require_root() {
if [[ "$EUID" -ne 0 ]]; then
echo "ERROR: $(basename "$0") must be run as root." >&2
echo " Try: sudo $(basename "$0") $*" >&2
exit 1
fi
}
require_root "$@"
echo "Root confirmed (EUID=0). Starting privileged work..."Limitando sudo a Comandos Individuais
O erro mais comum é colocar sudo no início de um script e então executar tudo como root. Em vez disso, aplique sudo somente ao comando exato que precisa dele — todo o restante é executado como seu usuário normal.
Isso limita o alcance do dano: se um invasor injetar código no seu script, ele só poderá executar com permissões de root as partes que não têm sudo à frente.
Compare os dois padrões abaixo. O segundo é muito mais seguro:
#!/usr/bin/env bash
set -euo pipefail
# BAD: escalate early, do everything as root (avoid this)
# sudo bash -c '
# cp config.conf /etc/app/config.conf
# chown app:app /etc/app/config.conf
# systemctl restart app
# '
# GOOD: escalate only for the commands that require it
LOCAL_CONF="./config.conf"
DEST="/etc/app/config.conf"
# Unprivileged: validate the config before touching anything as root
if ! grep -q '^[[:space:]]*\[main\]' "$LOCAL_CONF"; then
echo "ERROR: config.conf is missing [main] section" >&2
exit 1
fi
# Privileged: only these three commands run under sudo
sudo cp "$LOCAL_CONF" "$DEST"
sudo chown app:app "$DEST"
sudo systemctl restart app
echo "Config deployed and service restarted."Escrevendo Regras Rigorosas do sudoers
Chamar sudo somecommand em um script só funciona com segurança se o arquivo sudoers estiver configurado para permitir exatamente esse comando — e nada mais. Evite regras como ALL=(ALL) NOPASSWD: ALL para contas de serviço.
Em vez disso, restrinja as regras a comandos específicos com argumentos específicos, usando a sintaxe segura para visudo. Campos importantes em uma regra do sudoers:
- Usuário — quem pode invocar o sudo
- Host — em qual máquina (use
ALLpara portabilidade) - RunAs — qual identidade assumir (quase sempre
root) - Comando — caminho absoluto completo, opcionalmente com argumentos literais
Exemplos de regras rigorosas para uma conta de serviço de implantação (deployer):
# /etc/sudoers.d/deployer (edit with: sudo visudo -f /etc/sudoers.d/deployer)
#
# Allow 'deployer' to restart exactly one service — nothing else
deployer ALL=(root) NOPASSWD: /usr/bin/systemctl restart app
# Allow copying a config file to a fixed destination only
deployer ALL=(root) NOPASSWD: /usr/bin/cp /home/deployer/staging/config.conf /etc/app/config.conf
# Allow chown of that specific file only
deployer ALL=(root) NOPASSWD: /usr/bin/chown app\:app /etc/app/config.conf
# NEVER do this — gives full root shell:
# deployer ALL=(ALL) NOPASSWD: ALLRemovendo Privilégios com su e runuser
Quando um script começa como root (por exemplo, iniciado por um sistema de inicialização ou pelo cron executado como root), mas a maior parte do trabalho deve ocorrer como um usuário sem privilégios, remova os privilégios explicitamente em vez de executar o script inteiro como root.
Duas ferramentas para isso:
su -s /bin/bash -c 'command' username— inicia um shell como username e executa o comandorunuser -u username -- command args— preferível no Linux para alternar dentro de scripts pertencentes ao root; é mais limpo quesu
O padrão abaixo mostra um encapsulador de implantação pertencente ao root que muda para o usuário app para executar a lógica real da aplicação:
#!/usr/bin/env bash
# This script is called by systemd as root during pre-deployment
set -euo pipefail
APP_USER="app"
DEPLOY_DIR="/opt/myapp"
# Step 1: privileged — fix ownership of deploy directory
chown -R "${APP_USER}:${APP_USER}" "$DEPLOY_DIR"
# Step 2: drop to app user for the actual migration/startup logic
# runuser is available on most modern Linux systems
runuser -u "$APP_USER" -- bash -c "
cd $DEPLOY_DIR
./bin/migrate.sh
./bin/start.sh
"
echo "Deploy complete. Privileged wrapper exiting."Usando sudo -u para Executar um Único Comando como Outro Usuário
Nem sempre é necessário mudar para uma sessão completa de shell. sudo -u username command executa um único comando como o usuário especificado e depois retorna à identidade que fez a chamada. Isso é útil para manipular arquivos pertencentes a uma conta de serviço sem conceder a essa conta qualquer acesso interativo.
Combine isso com uma regra do sudoers que permita exatamente essa combinação de usuário e comando:
#!/usr/bin/env bash
set -euo pipefail
# Scenario: deploy script runs as 'deployer'; DB migrations must run as 'postgres'
# sudoers entry needed:
# deployer ALL=(postgres) NOPASSWD: /opt/app/bin/run_migrations.sh
DB_MIGRATION_SCRIPT="/opt/app/bin/run_migrations.sh"
if [[ ! -x "$DB_MIGRATION_SCRIPT" ]]; then
echo "ERROR: migration script not found or not executable: $DB_MIGRATION_SCRIPT" >&2
exit 1
fi
echo "Running DB migrations as postgres user..."
sudo -u postgres "$DB_MIGRATION_SCRIPT"
echo "Migrations done. Returning to deployer context (EUID=$EUID)."Evitando Escalação de Privilégios por Variáveis de Ambiente
Uma superfície de ataque sutil são as variáveis de ambiente herdadas por uma sessão do sudo. Por padrão, o sudo redefine o ambiente, mas substituições env_keep ou env_reset configuradas incorretamente podem passar variáveis controladas pelo invasor, como LD_PRELOAD, PATH ou PYTHONPATH, para comandos privilegiados.
Boas práticas:
- Sempre use caminhos absolutos em scripts executados sob
sudo— nunca dependa de$PATH - Passe somente as variáveis de que você precisa explicitamente:
sudo env VAR=value /path/to/cmd - No sudoers, evite
env_keep += PATHouenv_keep += LD_* - Use
sudo -Esomente quando você controlar e confiar completamente no ambiente que faz a chamada
#!/usr/bin/env bash
set -euo pipefail
# BAD: relies on $PATH — attacker who controls PATH can hijack 'cp'
# sudo cp config.conf /etc/app/
# GOOD: absolute paths for every command called under elevated context
SUDO_BIN="/usr/bin/sudo"
CP_BIN="/usr/bin/cp"
CHOWN_BIN="/usr/bin/chown"
SYSTEMCTL_BIN="/usr/bin/systemctl"
"$SUDO_BIN" "$CP_BIN" ./config.conf /etc/app/config.conf
"$SUDO_BIN" "$CHOWN_BIN" app:app /etc/app/config.conf
"$SUDO_BIN" "$SYSTEMCTL_BIN" restart app
echo "Deployed with hardened absolute-path invocations."Restringindo o sudo com Validação de Argumentos do Comando
Mesmo quando uma regra do sudoers permite um script específico, argumentos arbitrários ainda podem ser passados a esse script, a menos que a regra também os restrinja. Uma armadilha comum:
deployer ALL=(root) NOPASSWD: /opt/scripts/manage.shIsso permite sudo /opt/scripts/manage.sh restart — mas também sudo /opt/scripts/manage.sh --arbitrary-flag. Se manage.sh passar argumentos cegamente para subprocessos privilegiados, você terá um problema.
Faça uma defesa em profundidade: valide os argumentos dentro do script privilegiado e também no sudoers:
#!/usr/bin/env bash
# /opt/scripts/manage.sh — called via sudo; must validate its own args
set -euo pipefail
# Allowlist of valid actions
declare -A ALLOWED_ACTIONS=(
[restart]=1
[status]=1
[reload]=1
)
ACTION="${1:-}"
if [[ -z "$ACTION" ]]; then
echo "Usage: $(basename "$0") <restart|status|reload>" >&2
exit 1
fi
if [[ -z "${ALLOWED_ACTIONS[$ACTION]:-}" ]]; then
echo "ERROR: Unknown action '${ACTION}'. Allowed: ${!ALLOWED_ACTIONS[*]}" >&2
exit 2
fi
/usr/bin/systemctl "$ACTION" app
echo "Action '$ACTION' executed successfully."Elevação Temporária de Privilégios com uma Armadilha de Limpeza
Quando um script precisa manter temporariamente um arquivo, uma credencial ou um recurso privilegiado, use o trap do Bash para garantir que a limpeza ocorra mesmo em caso de erro ou sinal. Isso evita o vazamento de privilégios — por exemplo, um binário setuid temporário ou um soquete pertencente ao root que permaneça no sistema se o script falhar.
O padrão abaixo cria um arquivo temporário como root, usa-o e depois o remove — algo garantido por uma armadilha em EXIT:
#!/usr/bin/env bash
set -euo pipefail
# Must run as root for this demo
if [[ "$EUID" -ne 0 ]]; then
echo "Run as root" >&2; exit 1
fi
TMP_SECRET=""
cleanup() {
local exit_code=$?
if [[ -n "$TMP_SECRET" && -f "$TMP_SECRET" ]]; then
# Overwrite before deletion to reduce forensic recovery risk
shred -u "$TMP_SECRET" 2>/dev/null || rm -f "$TMP_SECRET"
echo "[cleanup] Removed privileged temp file." >&2
fi
exit "$exit_code"
}
trap cleanup EXIT INT TERM
# Create a root-owned temp file for a short-lived secret
TMP_SECRET="$(mktemp /tmp/deploy_secret.XXXXXXXX)"
chmod 600 "$TMP_SECRET"
# Simulate fetching a secret into the temp file
echo "super-secret-token" > "$TMP_SECRET"
# Use the secret (e.g., pass to a sub-command via file descriptor)
/usr/bin/some-privileged-tool --key-file "$TMP_SECRET"
echo "Privileged operation complete."Auditoria e Registro de Ações Privilegiadas
O princípio do menor privilégio é mais fácil de aplicar quando cada evento de elevação é registrado com contexto: quem executou o quê, quando e por quê. Combine duas camadas:
- o próprio sudo —
/var/log/auth.log(Debian/Ubuntu) ou/var/log/secure(RHEL) registra automaticamente cada chamada de sudo - registro de auditoria no nível do script — grave uma entrada estruturada no início de cada função privilegiada para que a intenção seja registrada junto com o registro do sistema
Usar uma função simples para registros estruturados mantém as trilhas de auditoria consistentes e fáceis de pesquisar com grep:
#!/usr/bin/env bash
set -euo pipefail
AUDIT_LOG="/var/log/app_deploy_audit.log"
log_privileged_action() {
local action="$1"
local reason="${2:-unspecified}"
local ts
ts="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
printf '{"ts":"%s","user":"%s","euid":%d,"action":"%s","reason":"%s"}\n' \
"$ts" "${SUDO_USER:-$USER}" "$EUID" "$action" "$reason" \
| sudo tee -a "$AUDIT_LOG" > /dev/null
}
# Each privileged step is logged before execution
log_privileged_action "cp_config" "deploy release v2.4.1"
sudo /usr/bin/cp ./config.conf /etc/app/config.conf
log_privileged_action "chown_config" "ensure app user owns config"
sudo /usr/bin/chown app:app /etc/app/config.conf
log_privileged_action "restart_service" "activate new config"
sudo /usr/bin/systemctl restart app
echo "Deployment complete. Audit entries written to $AUDIT_LOG"Verificação de Conhecimento: Escopo do sudo
Teste sua compreensão sobre a execução com menor privilégio em scripts Bash.
Recapitulação: Execução com Menor Privilégio e Disciplina no Uso do sudo
Nesta lição, você aprendeu as técnicas para executar scripts Bash com o privilégio mínimo necessário em cada etapa. Veja um resumo conciso dos princípios fundamentais:
- Proteja com
$EUID— falhe rapidamente se o script estiver sendo executado com a identidade incorreta, seja por precisar recusar o root, seja por exigir essa identidade - Limite o escopo do
sudoa comandos individuais — nunca eleve um script inteiro; apliquesudosomente às linhas que realmente precisam dele - Escreva regras sudoers restritas — especifique caminhos absolutos completos e argumentos literais; evite curingas e
ALL - Abandone privilégios com
runuserousudo -u— quando um script iniciado como root precisar transferir o controle para um usuário sem privilégios, use a ferramenta adequada em vez de executar tudo como root - Use caminhos absolutos — nunca dependa de
$PATHdentro de código privilegiado; defina os caminhos dos binários para evitar sequestros - Valide os argumentos dentro dos scripts privilegiados — as regras sudoers são a primeira linha de defesa, não a única; use listas de permissões
- Crie armadilhas e faça a limpeza — use
trap cleanup EXITpara garantir que recursos privilegiados temporários sejam destruídos mesmo em caso de erro - Registre cada elevação — entradas de auditoria estruturadas, combinadas ao syslog integrado do sudo, oferecem rastreabilidade para cada ação privilegiada
Aplicadas de forma consistente, essas práticas reduzem a superfície de ataque da sua automação de root o tempo todo para root somente onde for comprovadamente necessário — uma característica marcante de um Bash robusto e adequado para produção.
Perguntas Frequentes
A aula “Execução com privilégios mínimos e disciplina no sudo” é grátis?
Sim — o texto completo de “Execução com privilégios mínimos e disciplina no sudo” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DevOps Bootcamp, atualize para CoddyKit PRO. O curso de DevOps Bootcamp inclui 4 aulas no total.
O que vou aprender em “Execução com privilégios mínimos e disciplina no sudo”?
Remova privilégios, restrinja cuidadosamente as regras do sudo e valide o UID efetivo antes de operações arriscadas. Você pratica DevOps Bootcamp com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar DevOps Bootcamp?
Nenhuma experiência prévia é necessária. DevOps Bootcamp no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Execução com privilégios mínimos e disciplina no sudo”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de DevOps Bootcamp?
Sim. Cada aula de DevOps Bootcamp inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Prevenção de injeção de comandos e argumentos
- Tratamento seguro de segredos e higiene do ambiente
- Execução com privilégios mínimos e disciplina no sudo
- Análise estática e auditoria com ShellCheck