Containers Não Raiz
Execute o Node.js como um usuário não raiz para que uma fuga de container ou RCE não conceda privilégios de nível de host.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
FROM node:24-bookworm-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY dist ./dist
RUN chown -R node:node /app
USER node
ENV NODE_ENV=production
CMD ["node", "dist/main.js"]Quando usar isso: Toda imagem de produção Node. Use as melhores práticas de segurança do Docker e a maioria das políticas do K8s exigem UIDs não raiz.
Exemplo de Trabalho
FROM node:24-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY tsconfig.json src ./
RUN npm run build
FROM node:24-bookworm-slim AS prod
WORKDIR /app
# Instale como root, depois passe a propriedade
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
# Diretórios graváveis que o aplicativo precisa em tempo de execução
RUN mkdir -p /app/tmp /app/logs \
&& chown -R node:node /app
USER node
EXPOSE 3000
CMD ["node", "dist/main.js"]// src/main.ts - vincule uma porta não privilegiada
import express from "express";
const app = express();
const port = Number(process.env.PORT ?? 3000);
app.get("/health", (_req, res) => res.json({ status: "ok" }));
app.listen(port, "0.0.0.0");O que isso demonstra:
- A imagem oficial
nodeinclui um usuárionode(UID 1000) chownantes deUSERpara que o aplicativo possa lernode_modulese gravar logs- A porta 3000 não requer root (apenas portas abaixo de 1024 o fazem)
Mergulho Profundo
Como Funciona
- Containers Linux compartilham o kernel do host; root em um container ainda é perigoso (capacidades, montagens de socket)
USER nodecai para UID 1000 para o processoCMDe seus filhos- O
securityContext.runAsNonRoot: truedo Kubernetes rejeita imagens que iniciam como UID 0
Contexto de Segurança do Kubernetes
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: api
image: my-api:1.2.3
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: tmp
mountPath: /app/tmp
volumes:
- name: tmp
emptyDir: {}readOnlyRootFilesystem requer montagens graváveis para arquivos temporários e sockets Unix.
UID Personalizado para OpenShift
Algumas plataformas exigem UIDs arbitrários. Construa com diretórios graváveis pelo grupo:
RUN chgrp -R 0 /app && chmod -R g=u /app
USER 1001O OpenShift é executado como um UID aleatório no grupo root; g=u concede acesso de escrita.
Lista de Verificação de Permissões de Arquivo
| Caminho | Necessidade de permissão |
|---|---|
node_modules/ | leitura + execução |
dist/ | leitura + execução |
/app/tmp | escrita (uploads, arquivos pid) |
/app/logs | escrita se houver log de arquivo (prefira stdout) |
Armadilhas
USER nodeantes deCOPY- arquivos pertencentes ao root, o aplicativo não pode escrever. Correção:chownapós todas as cópias.- Vincular porta 80 - falha sem root. Correção: escute na porta 3000; o ingress mapeia 443 para 3000.
npm installcomonodena compilação - erros de permissão de cache. Correção: instale como root no estágio de compilação,chown, depoisUSER node.- Montagens de volume substituem a propriedade - diretórios de host pertencentes ao root. Correção:
fsGroupno K8s ouchownde initContainer. - Prisma/sqlite escrevendo em
/app- sistema de arquivos raiz somente leitura quebra. Correção: monteemptyDirem/app/data. - Assumindo que o usuário
nodeexiste no distroless - use o usuáriononrootem vez disso.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
USER node (UID 1000) | K8s e ECS padrão | UID arbitrário do OpenShift necessário |
| Usuário de aplicativo personalizado | Endurecimento de imagem multi-tenant | Desnecessário para a maioria das APIs internas |
Distroless nonroot | Imagem mínima + não raiz | Você precisa de depuração de shell no container |
| Root + capacidades descartadas | Imagens legadas em migração | Novas imagens (use não raiz desde o primeiro dia) |
FAQs
O não-root quebra o `npm` no container em execução?
Containers de produção não devem executar npm install em tempo de execução. Instale no estágio de compilação como root, execute o aplicativo como node.
Qual UID devo documentar?
Documente 1000 para imagens Node oficiais. Se estiver usando UIDs personalizados, documente no Helm chart e nos comentários do Dockerfile.
O modo cluster do NestJS pode rodar como não-root?
Sim. Primário e workers todos rodam como node. Use readOnlyRootFilesystem com um volume tmp para IPC, se necessário.
Como verifico localmente?
docker run --rm my-api id
# uid=1000(node) gid=1000(node)O AWS Fargate requer não-root?
Não estritamente, mas as melhores práticas de segurança da AWS e auditorias de clientes esperam isso. Defina user na definição da tarefa para 1000.
E quanto a scripts init que precisam de root?
Use um initContainer no K8s para migrações (prisma migrate) com permissões elevadas; o container do aplicativo permanece não-root.
Relacionados
- Noções Básicas de Docker - primeiro Dockerfile
- Builds Multi-Estágio - chown no estágio final
- Compromissos Distroless & Alpine - usuário
nonroot - Probes de Saúde e Prontidão - probes como não-root
- Melhores Práticas do Docker - lista de verificação de seção
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 e NestJS 11.