Iptables: correspondência do tráfego de saída com o conntrack e o proprietário. Funciona com gotas estranhas

8

No meu script iptables, tenho experimentado escrever sobre regras tão granuladas quanto possível. Limito quais usuários podem usar os serviços, em parte por segurança e em parte como um exercício de aprendizado.

Usando o iptables v1.4.16.2 no Debian 6.0.6 executando o kernel 3.6.2.

No entanto, encontrei um problema que ainda não entendi ...

portas de saída para todos os usuários

Isso funciona perfeitamente bem. Não tenho regras de rastreamento de estado genéricas.

## Outgoing port 81
$IPTABLES -A OUTPUT -p tcp --dport 81 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
$IPTABLES -A INPUT -p tcp --sport 81 -s $MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT

portas de saída com correspondência de usuário

## outgoing port 80 for useraccount
$IPTABLES -A OUTPUT --match owner --uid-owner useraccount -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED --sport 1024:65535 -j ACCEPT
$IPTABLES -A INPUT -p tcp --sport 80 --dport 1024:65535 -d $MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT

Isso permite a saída da porta 80 apenas para a conta "useraccount", mas regras como essa para o tráfego TCP têm problemas.

## Default outgoing log + block rules
$IPTABLES -A OUTPUT -j LOG --log-prefix "BAD OUTGOING " --log-ip-options --log-tcp-options --log-uid
$IPTABLES -A OUTPUT -j DROP

O problema

Os trabalhos acima, o usuário "useraccount" pode obter arquivos perfeitamente bem. Nenhum outro usuário no sistema pode fazer conexões de saída para a porta 80.

useraccount@host:$ wget http://cachefly.cachefly.net/10mb.test

Mas o wget acima deixa x7 descartados no meu syslog:

Oct 18 02:00:35 xxxx kernel: BAD OUTGOING IN= OUT=eth0 SRC=xx.xx.xx.xx DST=205.234.175.175 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=12170 DF PROTO=TCP SPT=37792 DPT=80 SEQ=164520678 ACK=3997126942 WINDOW=979 RES=0x00 ACK URGP=0  

Eu não recebo essas gotas por regras semelhantes com tráfego UDP. Já tenho regras que limitam quais usuários podem fazer solicitações de DNS.

Os pacotes ACK de saída descartados parecem vir da conta raiz (URGP = 0), o que eu não entendo. Mesmo quando eu permito useraccount para root.

Acredito que os pacotes ACK são categorizados como novos porque o conntrack inicia o rastreamento de conexões após a 3ª etapa do handshake de 3 vias, mas por que eles estão sendo descartados?

Essas gotas podem ser ignoradas com segurança?

Editar

Então, muitas vezes vejo regras como essas, que funcionam bem para mim:

$IPTABLES -A OUTPUT -s $MYIP -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPTABLES -A INPUT -p tcp -m tcp --sport 80 -d $MYIP -m state --state ESTABLISHED -j ACCEPT

Troquei "-m state-state" por "-m conntrack --ctstate" porque a correspondência de estado é aparentemente obsoleta.

A melhor prática é ter regras genéricas de rastreamento de estado? As regras acima não são consideradas corretas?

Para um controle rigoroso das conexões de usuários que saem, algo assim seria melhor?

$IPTABLES -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
$IPTABLES -A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT

$IPTABLES -A OUTPUT -p tcp --dport 80 -s $SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m owner --uid-owner useraccount -j ACCEPT

$IPTABLES -A OUTPUT -p tcp --dport 80 -s $SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m owner --uid-owner otheraccount -j ACCEPT
    
por arcX 20.10.2012 / 09:03

2 respostas

12

Para resumir uma longa história, esse ACK foi enviado quando o soquete não pertencia a ninguém. Em vez de permitir que os pacotes pertencentes a um soquete que pertença ao usuário x , permita que os pacotes que pertencem a uma conexão que foi iniciada por um soquete do usuário x .

A história mais longa.

Para entender o problema, é útil entender como wget e solicitações HTTP funcionam em geral.

Em

wget http://cachefly.cachefly.net/10mb.test

wget estabelece uma conexão TCP com cachefly.cachefly.net e, uma vez estabelecido, envia uma solicitação no protocolo HTTP que diz: "Por favor, me envie o conteúdo de /10mb.test ( GET /10mb.test HTTP/1.1 ) e, a propósito, você poderia por favor, não feche a conexão depois que você terminar ( Connection: Keep-alive ). A razão disso é que, caso o servidor responda com um redirecionamento para uma URL no mesmo endereço IP, ele pode reutilizar a conexão.

Agora o servidor pode responder com "Aqui vem os dados que você solicitou, cuidado com os 10MB grandes ( Content-Length: 10485760 ), e sim OK, deixarei a conexão aberta". Ou se não souber o tamanho dos dados, "Aqui estão os dados, desculpe, eu não posso deixar a conexão aberta, mas vou dizer quando você pode parar de baixar os dados, fechando o meu fim da conexão".

No URL acima, estamos no primeiro caso.

Portanto, assim que wget obtiver os cabeçalhos da resposta, ela saberá que o trabalho está concluído após o download de 10 MB de dados.

Basicamente, o que wget faz é ler os dados até que 10MB tenham sido recebidos e saiam. Mas nesse ponto, há mais a ser feito. E quanto ao servidor? Foi dito para deixar a conexão aberta.

Antes de sair, wget fecha ( close chamada do sistema) o descritor de arquivo para o soquete. Após, o close , o sistema termina de reconhecer os dados enviados pelo servidor e envia um FIN para dizer: "Eu não estarei enviando mais dados". Nesse momento, close retorna e wget sai. Não há mais soquete associado à conexão TCP (pelo menos, não pertence a nenhum usuário). No entanto, ainda não está terminado. Ao receber esse FIN , o servidor HTTP vê fim de arquivo ao ler a próxima solicitação do cliente. Em HTTP, isso significa "não mais pedido, eu vou fechar o meu fim". Então, ele envia seu FIN também, para dizer: "Eu também não vou mandar nada, essa conexão está indo embora".

Ao receber esse FIN, o cliente envia um "ACK". Mas, nesse ponto, wget desapareceu há muito tempo, de modo que o ACK não é de nenhum usuário. É por isso que é bloqueado pelo seu firewall. Como o servidor não recebe o ACK, ele enviará o FIN repetidamente até que ele desista e você verá mais ACKs descartados. Isso também significa que, ao descartar esses ACKs, você está desnecessariamente usando recursos do servidor (que precisa manter um soquete no estado LAST-ACK) por algum tempo.

O comportamento teria sido diferente se o cliente não tivesse solicitado "Keep-alive" ou o servidor não tivesse respondido com "Keep-alive".

Como já foi mencionado, se você estiver usando o rastreador de conexão, o que você quer fazer é deixar todos os pacotes no ESTABLISHED e RELATED aparecerem e se preocupar apenas com NEW pacotes.

Se você permitir que NEW pacotes do usuário x mas não pacotes do usuário y , outros pacotes para conexões estabelecidas pelo usuário x serão processados e porque não pode haver conexões estabelecidas pelo usuário y (já que estamos bloqueando os pacotes NEW que estabeleceriam a conexão), não haverá nenhum pacote para o usuário y conexões passando.

    
por 20.10.2012 / 17:43
2

This allows port 80 out only for the account "useraccount"

- bem, pelo menos as regras que você mostrou não implicam isso, na verdade.

Há também uma sala para conselhos - não faça a verificação do usuário em fluxos ESTABLISHED, apenas faça a verificação em NOVO. Eu também não vejo um ponto em verificar porta de origem ao verificar Incoming ESTABLISHED, qual é a diferença que porta era, já está no estado ESTABELECIDO do PoV da conntrack. O firewall deve ser o mais simples possível, mas eficiente, portanto, a abordagem Navalha de Occam é a melhor opção.

    
por 20.10.2012 / 10:16