Permitir que o processo não raiz se ligue às portas 80 e 443?

104

É possível ajustar um parâmetro do kernel para permitir que um programa da terra do usuário seja vinculado às portas 80 e 443?

A razão pela qual pergunto é que acho tolo permitir que um processo privilegiado abra um soquete e ouça. Qualquer coisa que abra um soquete e escute é de alto risco, e os aplicativos de alto risco não devem ser executados como raiz.

Prefiro tentar descobrir qual processo sem privilégios está escutando na porta 80, em vez de tentar remover malware que ocultava privilégios de root.

jww
fonte
1
Consulte serverfault.com/questions/268099 e stackoverflow.com/questions/413807 . A resposta curta é não.
Sami Laine
10
A resposta longa é sim. Portanto, a resposta curta também deve ser sim.
BT
4
A resposta curta é sim.
Jason C #

Respostas:

163

Não sei ao que as outras respostas e comentários aqui estão se referindo. Isso é possível com bastante facilidade. Existem duas opções, ambas que permitem o acesso a portas de número baixo sem precisar elevar o processo para o root:

Opção 1: use CAP_NET_BIND_SERVICEpara conceder acesso à porta com número baixo a um processo:

Com isso, você pode conceder acesso permanente a um binário específico para vincular a portas de número baixo através do setcapcomando:

sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary

Para mais detalhes sobre a parte e / i / p, consulte cap_from_text.

Depois de fazer isso, /path/to/binaryserá capaz de vincular a portas de número baixo. Observe que você deve usar setcapo próprio binário em vez de um link simbólico.

Opção 2: use authbindpara conceder acesso único, com melhor controle de usuário / grupo / porta:

A ferramenta authbind( página de manual ) existe precisamente para isso.

  1. Instale authbindusando o seu gerenciador de pacotes favorito.

  2. Configure-o para conceder acesso às portas relevantes, por exemplo, para permitir 80 e 443 de todos os usuários e grupos:

    sudo touch /etc/authbind/byport/80
    sudo touch /etc/authbind/byport/443
    sudo chmod 777 /etc/authbind/byport/80
    sudo chmod 777 /etc/authbind/byport/443
    
  3. Agora execute seu comando via authbind(opcionalmente especificando --deepou outros argumentos, consulte a página de manual):

    authbind --deep /path/to/binary command line args
    

    Por exemplo

    authbind --deep java -jar SomeServer.jar
    

Há vantagens e desvantagens em ambos os itens acima. A opção 1 concede confiança ao binário, mas não fornece controle sobre o acesso por porta. A opção 2 concede confiança ao usuário / grupo e fornece controle sobre o acesso por porta, mas o AFAIK suporta apenas IPv4.

Jason C
fonte
Realmente precisa de rwxpermissão?
Matt
Para reverter a operação na Opção 1, você executaria o comando novamente usando -pintead de +eip?
precisa saber é o seguinte
5
Observe que, com o setcap, se você sobrescrever o executável ao qual concede privilégios (por exemplo: fazer uma reconstrução), ele perde seu status de porta privilegiada e você deve conceder privilégios novamente: |
Rogerdpack
1
Algo que eu tive que mexer; Eu estava tentando executar um serviço sysv, que executa um executável ruby ​​que usa ruby. Você precisa dar setcappermissão para o executável ruby ​​específico da versão , por exemplo/usr/bin/ruby1.9.1
Christian Rondeau
3
Tenho minhas dúvidas de que, chmodpara 777 os byportarquivos, seja a melhor idéia. Eu já vi dando permissões variando de 500até 744. Eu continuaria com o mais restritivo que funciona para você.
Pere
28

Dale Hagglund está no local. Então, eu vou dizer a mesma coisa, mas de uma maneira diferente, com alguns detalhes e exemplos. ☺

A coisa certa a fazer nos mundos Unix e Linux é:

  • ter um programa pequeno, simples e facilmente auditável, executado como superusuário e vinculando o soquete de escuta;
  • ter outro programa pequeno, simples e facilmente auditável que descarte privilégios, gerado pelo primeiro programa;
  • para ter o conteúdo do serviço, em um terceiro programa separado , executado sob uma conta e cadeia não-superusuário e carregada pelo segundo programa, esperando simplesmente herdar um descritor de arquivo aberto para o soquete.

Você tem uma idéia errada de onde está o alto risco. O alto risco está em ler a partir da rede e agir de acordo com o que é lido, não nos simples atos de abrir um soquete, vinculá-lo a uma porta e chamar listen(). É a parte de um serviço que faz a comunicação real, que é de alto risco. As partes que abrem, bind()e listen(), e até (até certo ponto) a parte que accepts()não são de alto risco e podem ser executadas sob a égide do superusuário. Eles não usam e agem com base em dados (com exceção dos endereços IP de origem no accept()caso) que estão sob o controle de estranhos não confiáveis ​​na rede.

Existem muitas maneiras de fazer isso.

inetd

Como diz Dale Hagglund, o antigo "super servidor da rede" inetdfaz isso. A conta na qual o processo de serviço é executado é uma das colunas em inetd.conf. Ele não separa a parte de escuta e a parte de privilégios de queda em dois programas separados, pequenos e facilmente auditáveis, mas separa o código de serviço principal em um programa separado, exec()editado em um processo de serviço que gera com um descritor de arquivo aberto para o soquete.

A dificuldade da auditoria não é um grande problema, pois é preciso apenas auditar o único programa. inetdO maior problema de não é auditar tanto, mas sim que ele não fornece um controle simples e refinado do serviço de tempo de execução, em comparação com as ferramentas mais recentes.

UCSPI-TCP e daemontools

Os pacotes UCSPI-TCP e daemontools de Daniel J. Bernstein foram projetados para fazer isso em conjunto. Como alternativa, pode-se usar o conjunto de ferramentas daemontools-encore em grande parte equivalente de Bruce Guenter .

O programa para abrir o descritor de arquivo do soquete e ligar à porta local privilegiada é tcpserver, a partir do UCSPI-TCP. Faz tanto o listen()quanto o accept().

tcpserverem seguida, gera um programa de serviço que descarta os privilégios de root (porque o protocolo que está sendo servido envolve iniciar como superusuário e, em seguida, "efetuar logon", como é o caso, por exemplo, de um daemon FTP ou SSH) ou setuidgidque é um programa pequeno e facilmente auditável, independente, que apenas descarta privilégios e, em seguida, encadea cargas no programa de serviço propriamente dito (nenhuma parte dos quais é executada com privilégios de superusuário, como é o caso, por exemplo qmail-smtpd).

Um runscript de serviço seria, por exemplo, (este para dummyidentd por fornecer serviço de IDENT nulo):

#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl

nosh

Meu pacote nosh é projetado para fazer isso. Ele tem um pequeno setuidgidutilitário, assim como os outros. Uma pequena diferença é que ele pode ser usado com os systemdserviços "LISTEN_FDS", bem como com os serviços UCSPI-TCP, portanto, o tcpserverprograma tradicional é substituído por dois programas separados: tcp-socket-listene tcp-socket-accept.

Novamente, utilitários de uso único geram e carregam em cadeia. Uma peculiaridade interessante do design é que é possível eliminar privilégios de superusuário depois, listen()mas antes mesmo accept(). Aqui está um runscript para o qmail-smtpdque realmente faz exatamente isso:

#!/bin/nosh
fdmove -c 2 1
clearenv --keep-path --keep-locale
envdir env/
softlimit -m 70000000
tcp-socket-listen --combine4and6 --backlog 2 ::0 smtp
setuidgid qmaild
sh -c 'exec \
tcp-socket-accept -v -l "${LOCAL:-0}" -c "${MAXSMTPD:-1}" \
ucspi-socket-rules-check \
qmail-smtpd \
'

Os programas que são executados sob a égide do superusuário são as pequenas ferramentas cadeia de carregamento de serviço agnóstico fdmove, clearenv, envdir, softlimit, tcp-socket-listen, e setuidgid. No momento em que shé iniciado, o soquete está aberto e vinculado à smtpporta e o processo não possui mais privilégios de superusuário.

s6, s6-networking e execline

Os pacotes de rede s6 e s6 de Laurent Bercot foram projetados para fazer isso em conjunto. Os comandos são estruturalmente muito semelhantes aos do daemontoolsUCSPI-TCP.

runos scripts seriam praticamente os mesmos, exceto pela substituição de s6-tcpserverfor tcpservere s6-setuidgidfor setuidgid. No entanto, pode-se optar por usar o conjunto de ferramentas execline de M. Bercot ao mesmo tempo.

Aqui está um exemplo de um serviço FTP, modificado levemente do original de Wayne Marshall , que usa execline, s6, s6-networking e o programa de servidor FTP do publicfile :

#!/command/execlineb -PW
multisubstitute {
    define CONLIMIT 41
    define FTP_ARCHIVE "/var/public/ftp"
}
fdmove -c 2 1
s6-envuidgid pubftp 
s6-softlimit -o25 -d250000 
s6-tcpserver -vDRH -l0 -b50 -c ${CONLIMIT} -B '220 Features: a p .' 0 21 
ftpd ${FTP_ARCHIVE}

ipsvd

O ipsvd de Gerrit Pape é outro conjunto de ferramentas que funciona da mesma maneira que as redes ucspi-tcp e s6-networking. As ferramentas são chpste tcpsvddesta vez, mas elas fazem a mesma coisa, e o código de alto risco que faz a leitura, o processamento e a gravação das coisas enviadas pela rede por clientes não confiáveis ​​ainda está em um programa separado.

Aqui está o exemplo de M. Pape de execução fnordem um runscript:

#!/bin/sh
exec 2>&1
cd /public/10.0.5.4
exec \
chpst -m300000 -Uwwwuser \
tcpsvd -v 10.0.5.4 443 sslio -v -unobody -//etc/fnord/jail -C./cert.pem \
fnord

systemd

systemd, o novo sistema de supervisão de serviço e init que pode ser encontrado em algumas distribuições Linux, pretende fazer o que inetdpode . No entanto, ele não usa um conjunto de pequenos programas independentes. É preciso fazer uma auditoria systemdcompleta, infelizmente.

Com systemdum cria arquivos de configuração para definir um soquete que systemdescuta, e um serviço que é systemdiniciado. O arquivo "unidade" de serviço possui configurações que permitem um grande controle sobre o processo de serviço, incluindo qual usuário ele é executado.

Com esse usuário definido como não-superusuário, systemdfaz todo o trabalho de abrir o soquete, vinculá-lo a uma porta e chamar listen()(e, se necessário accept()) no processo 1 como superusuário e o processo de serviço que ele spawns é executado sem privilégios de superusuário.

JdeBP
fonte
2
Obrigado pelo elogio. Esta é uma ótima coleção de conselhos concretos. +1.
Dale Hagglund
11

Eu tenho uma abordagem bastante diferente. Eu queria usar a porta 80 para um servidor node.js. Não consegui fazer isso desde que o Node.js foi instalado para um usuário não sudo. Tentei usar links simbólicos, mas não funcionou para mim.

Então eu soube que posso encaminhar conexões de uma porta para outra. Então, iniciei o servidor na porta 3000 e configurei uma porta para a frente da porta 80 para a porta 3000.

Este link fornece os comandos reais que podem ser usados ​​para fazer isso. Aqui estão os comandos -

localhost / loopback

sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 3000

externo

sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000

Eu usei o segundo comando e funcionou para mim. Então, acho que esse é um meio termo para não permitir que o processo do usuário acesse as portas inferiores diretamente, mas conceda acesso a elas usando o encaminhamento de portas.

novato
fonte
6
+1 para pensar fora da caixa
Richard Wiseman
4

Seus instintos estão totalmente corretos: é uma má idéia ter um programa grande e complexo rodando como raiz, porque sua complexidade os torna difíceis de confiar.

Mas também é uma má idéia permitir que usuários comuns se conectem a portas privilegiadas, porque essas portas geralmente representam serviços importantes do sistema.

A abordagem padrão para resolver essa aparente contradição é a separação de privilégios . A idéia básica é separar seu programa em duas (ou mais) partes, cada uma das quais faz uma parte bem definida do aplicativo geral e se comunica por interfaces limitadas simples.

No exemplo que você dá, você deseja separar seu programa em duas partes. Um que roda como root, abre e liga ao soquete privilegiado e o entrega de alguma forma à outra parte, que é executada como um usuário comum.

Essas duas maneiras principais de alcançar essa separação.

  1. Um único programa que inicia como raiz. A primeira coisa que faz é criar o soquete necessário, da maneira mais simples e limitada possível. Em seguida, elimina privilégios, ou seja, converte-se em um processo regular no modo de usuário e faz todo o restante trabalho. A remoção correta de privilégios é complicada; portanto, reserve um tempo para estudar a maneira correta de fazê-lo.

  2. Um par de programas que se comunicam através de um par de soquetes criado por um processo pai. Um programa de driver não privilegiado recebe argumentos iniciais e talvez faça alguma validação básica de argumento. Ele cria um par de soquetes conectados via socketpair () e, em seguida, bifurca e executa dois outros programas que farão o trabalho real e se comunicarão através do par de soquetes. Um deles é privilegiado e criará o soquete do servidor, e quaisquer outras operações privilegiadas, e o outro fará a execução do aplicativo mais complexa e, portanto, menos confiável.

[1] http://en.m.wikipedia.org/wiki/Privilege_separation

Dale Hagglund
fonte
O que sua proposta não é considerada uma prática recomendada. Você pode olhar para o inetd, que pode escutar em um soquete privilegiado e depois entregá-lo a um programa não privilegiado.
Dale Hagglund
3

Solução mais simples: remova todas as portas privilegiadas no linux

Funciona no ubuntu / debian:

#save configuration permanently
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf
#apply conf
sysctl --system

(funciona bem para o VirtualBox com conta não raiz)

Agora, tenha cuidado com a segurança, porque todos os usuários podem ligar todas as portas!

soleuu
fonte
Isso é esperto. Um pequeno detalhe: a configuração abre 80 e 443, mas também abre todas as outras portas. Permissões de relaxamento nas outras portas podem não ser desejadas.
jww 13/09