Controle de Acesso
Vídeo da aula estará disponível em breve
Proteção vs Segurança
Proteção é um mecanismo interno do SO: controlar quais processos podem acessar quais recursos. Segurança é mais amplo: garantir integridade do sistema contra ameaças externas (malware, invasores, exploits). Proteção é a base sobre a qual segurança é construída.
PROTEÇÃO (mecanismos internos):
├── Quem pode ler /etc/shadow?
├── Quem pode matar o processo do outro usuário?
├── Quem pode acessar a porta 80?
└── Quem pode montar um filesystem?
SEGURANÇA (ameaças externas):
├── Como impedir que malware escale privilégios?
├── Como detectar um buffer overflow exploit?
├── Como autenticar quem diz ser "root"?
└── Como isolar um processo comprometido?
Princípio fundamental:
LEAST PRIVILEGE (menor privilégio)
Cada processo deve ter apenas as permissões mínimas
necessárias para executar sua tarefa.
Exemplo: servidor web Apache
- Precisa: ler /var/www, escutar porta 80
- NÃO precisa: ler /etc/shadow, carregar módulos do kernel
- Roda como usuário "www-data", não como root
Domínios de Proteção
Um domínio de proteção define um conjunto de pares (recurso, permissões). Cada processo executa dentro de um domínio que determina o que ele pode fazer.
Domínio D0 (root):
{ (arquivo1, read/write/exec), (arquivo2, read/write),
(impressora, print), (rede, listen/connect),
(kernel, load_module) }
Domínio D1 (user marco):
{ (arquivo1, read), (arquivo3, read/write),
(impressora, print), (rede, connect) }
Domínio D2 (user www-data):
{ (/var/www/*, read), (porta 80, listen),
(rede, connect) }
No Unix/Linux, o domínio é definido por:
- UID (User ID): identifica o usuário
- GID (Group ID): identifica o grupo
- Supplementary groups: grupos adicionais
- Capabilities: permissões granulares do kernel
Troca de domínio:
- su / sudo: muda para domínio de outro usuário
- setuid: processo assume domínio do dono do arquivo
- login: após autenticação, cria domínio do usuário
Matriz de Acesso
A matriz de acesso é o modelo teórico de proteção. Linhas representam domínios (sujeitos), colunas representam objetos (recursos), e cada célula contém as operações permitidas.
│ arquivo1 │ arquivo2 │ arquivo3 │ impressora │ porta80 │
──────────┼──────────┼──────────┼──────────┼────────────┼─────────┤
root │ r,w,x │ r,w │ r,w,x │ print │ listen │
──────────┼──────────┼──────────┼──────────┼────────────┼─────────┤
marco │ r │ │ r,w │ print │ │
──────────┼──────────┼──────────┼──────────┼────────────┼─────────┤
www-data │ │ │ r │ │ listen │
──────────┼──────────┼──────────┼──────────┼────────────┼─────────┤
A matriz é ESPARSA (maioria das células vazia).
Não é prático armazenar como matriz completa.
Duas implementações reais:
1. ACL: armazena por COLUNA (por objeto)
2. Capability: armazena por LINHA (por sujeito)
ACLs (Access Control Lists)
ACLs armazenam a matriz por coluna: cada objeto lista quem pode acessá-lo e como. É a abordagem usada pelo Unix/Linux (permissões rwx) e pelo Windows (NTFS ACLs).
# Permissões Unix clássicas: owner, group, others
$ ls -la /etc/passwd /etc/shadow
-rw-r--r-- 1 root root 2847 mai 1 /etc/passwd
-rw-r----- 1 root shadow 1501 mai 1 /etc/shadow
# Decodificando: -rw-r--r--
# tipo│owner│group│others
# - │rw- │r-- │r--
# file│read+│read │read
# │write│only │only
# /etc/shadow: -rw-r-----
# owner (root): read+write
# group (shadow): read only
# others: NADA (senhas protegidas)
# Modificar permissões
$ chmod 750 script.sh # rwxr-x--- (owner all, group r+x, others none)
$ chmod u+x,g-w arquivo # +exec owner, -write group
# ACLs estendidas (POSIX ACLs) — mais granulares
$ getfacl documento.txt
# file: documento.txt
# owner: marco
# group: devs
user::rw-
user:ana:r-- # ana pode ler (além do owner/group/others)
group::rw-
group:qa:r-- # grupo qa pode ler
mask::rw-
other::---
$ setfacl -m u:ana:rw documento.txt # dar read+write para ana
Capabilities (Capacidades)
Capabilities armazenam a matriz por linha: cada sujeito carrega um token que lista o que pode fazer. Em vez de o arquivo listar quem pode acessá-lo, o processo carrega uma lista do que pode acessar.
ACL (por objeto):
arquivo.txt: { (marco, rw), (ana, r), (root, rwx) }
✓ Fácil ver QUEM tem acesso a um recurso
✓ Fácil revogar acesso a um recurso (edita a ACL)
✗ Difícil ver tudo que um usuário pode acessar
Capability (por sujeito):
marco: { (arquivo.txt, rw), (dir/*, r), (porta 8080, listen) }
✓ Fácil ver o que um processo pode fazer
✓ Fácil delegar: "dê ao subprocess estas capabilities"
✗ Difícil revogar: como retirar capability de todos os processos?
Linux usa AMBOS:
- ACLs: permissões de arquivo (rwx, POSIX ACLs)
- Capabilities: permissões de kernel (cap_net_bind_service, etc.)
Linux capabilities (granularidade fina em vez de root):
CAP_NET_BIND_SERVICE → escutar portas < 1024
CAP_NET_RAW → usar raw sockets (ping)
CAP_SYS_ADMIN → mount, sethostname, etc.
CAP_DAC_OVERRIDE → ignorar permissões de arquivo
CAP_KILL → enviar sinais a qualquer processo
# Ver capabilities de um executável
$ getcap /usr/bin/ping
/usr/bin/ping cap_net_raw=ep
# Dar capability a um executável (em vez de setuid root)
$ sudo setcap cap_net_bind_service=+ep /usr/bin/node
# Agora node pode escutar porta 80 sem ser root!
# Ver capabilities do processo atual
$ grep Cap /proc/self/status
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 000001ffffffffff
# Decodificar
$ capsh --decode=000001ffffffffff
RBAC (Role-Based Access Control)
Em vez de atribuir permissões diretamente a usuários, RBAC cria papéis (roles) com conjuntos de permissões, e atribui usuários a papéis. É o modelo dominante em sistemas empresariais.
Sem RBAC (permissões diretas):
marco → { ler_código, escrever_código, deploy, ler_logs }
ana → { ler_código, escrever_código, ler_logs }
bob → { ler_código, ler_logs }
(cada novo usuário: configurar tudo manualmente)
Com RBAC (via papéis):
Papéis:
dev_senior → { ler_código, escrever_código, deploy, ler_logs }
dev_junior → { ler_código, escrever_código, ler_logs }
leitor → { ler_código, ler_logs }
Atribuições:
marco → dev_senior
ana → dev_junior
bob → leitor
Novo dev junior? Basta: assign(carol, dev_junior)
Sem editar permissão por permissão.
Implementações:
Linux: groups (parcialmente RBAC — group = role)
Postgres: GRANT role TO user
Kubernetes: RBAC nativo (ClusterRole, RoleBinding)
AWS IAM: policies attached to roles
setuid e setgid
O bit setuid permite que um processo execute com as permissões do dono do arquivo, não do usuário que o executou. É o mecanismo que permite que programas como passwd e su funcionem.
# passwd precisa escrever em /etc/shadow (owned by root)
$ ls -la /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 /usr/bin/passwd
↑
s = setuid bit! Executa como root, independente de quem chamou.
# Quando marco executa passwd:
# 1. Shell de marco (UID=1000) chama exec("/usr/bin/passwd")
# 2. Kernel vê bit setuid → EUID muda para 0 (root)
# 3. passwd roda com EUID=0, pode escrever /etc/shadow
# 4. passwd termina, EUID volta ao normal
# Perigo: setuid root + bug = escalação de privilégios
# Se passwd tiver um buffer overflow, atacante ganha root!
# Listar todos os binários setuid no sistema
$ find / -perm -4000 -type f 2>/dev/null
/usr/bin/passwd
/usr/bin/su
/usr/bin/sudo
/usr/bin/mount
/usr/bin/newgrp
# setgid: mesma ideia, mas com GID
$ ls -la /usr/bin/wall
-rwxr-sr-x 1 root tty 19024 /usr/bin/wall
↑ s no grupo = setgid
ping ser setuid root (acesso total ao sistema), ele recebe apenas CAP_NET_RAW (permissão para raw sockets). Se ping tiver um bug, o atacante ganha apenas raw sockets, não root completo. É o princípio de menor privilégio aplicado.
No harness.os
O controle de acesso do SO tem paralelo direto com como o harness.os gerencia permissões de agentes:
Modelo de proteção do SO harness.os
═══════════════════════ ══════════
UID/GID Agent ID / Session ID
Identifica o sujeito Identifica quem está operando
Domínio de proteção Escopo do agente
Conjunto de permissões Tools disponíveis + regras ativas
de um processo Definidos pelo project_slug
ACL (por recurso) Row-Level Security (Postgres)
arquivo: quem pode r/w/x Tabela: quem pode SELECT/INSERT
/etc/shadow: root + shadow decisions: agente vê só suas
Capabilities MCP Tools
Permissões granulares Cada tool é uma capability
cap_net_bind → escutar porta log_decision → pode logar decisão
cap_sys_admin → mount run_sql → acesso direto ao banco
RBAC (papéis) Agent Roles (futuro)
dev_senior → deploy build_agent → build tools
leitor → read-only review_agent → read-only + govern
Least privilege:
Agente de QA não precisa de run_sql
Agente de build não precisa de govern_audit_decisions
Cada agente recebe apenas as tools necessárias
Exercícios
- Execute
find / -perm -4000 -type f 2>/dev/nullno seu sistema. Quantos binários setuid existem? Escolha 3 e explique por que precisam de setuid (qual recurso privilegiado acessam). - Crie um arquivo
secreto.txtcomchmod 600. Verifique que outro usuário não pode lê-lo. Depois, usesetfacl -m u:outro_usuario:r secreto.txtpara dar acesso de leitura apenas a esse usuário. Explique a diferença entre permissões Unix tradicionais e POSIX ACLs. - Projete um esquema RBAC para um sistema com 3 papéis (admin, dev, viewer) e 4 recursos (código, deploy, logs, config). Desenhe a matriz de acesso e explique como adicionar um novo dev seria mais fácil com RBAC do que com ACLs diretas.
Resumo
Verifique seu entendimento
Qual é o principal risco de segurança dos binários setuid root?
- Proteção controla quais processos acessam quais recursos; segurança protege contra ameaças externas
- A matriz de acesso é o modelo teórico; ACLs (por objeto) e capabilities (por sujeito) são implementações práticas
- Unix usa permissões rwx (owner/group/others) como ACL simplificada; POSIX ACLs permitem granularidade fina
- Linux capabilities substituem setuid com permissões granulares do kernel, aplicando o princípio de menor privilégio
- RBAC (papéis) simplifica administração: atribui permissões a papéis, e usuários a papéis
- setuid é poderoso mas perigoso: qualquer bug no binário setuid root pode dar acesso total ao sistema