2014-02-02 05:48:05 +0000 2014-02-02 05:48:05 +0000
117
117
Advertisement

Permitir que o processo não-raíz ligado aos portos 80 e 443?

Advertisement

É possível afinar um parâmetro do kernel para permitir que um programa de terra do utilizador se ligue à porta 80 e 443?

A razão pela qual pergunto é porque penso que é uma tolice permitir um processo privilegiado para abrir uma tomada e ouvir. Qualquer coisa que abra uma tomada e ouça é de alto risco, e as aplicações de alto risco não devem estar a funcionar como raiz.

Prefiro muito mais tentar descobrir que processo não privilegiado é escutar na porta 80 do que tentar remover o malware que se escondeu com privilégios de raiz.

Advertisement
Advertisement

Respostas (5)

176
176
176
2015-03-21 21:12:41 +0000

Não tenho a certeza a que outras respostas e comentários aqui se referem. Isto é possível com bastante facilidade. Há duas opções, ambas permitem o acesso a portos de baixo número sem ter de elevar o processo à raiz:

Opção 1: Usar CAP_NET_BIND_SERVICE para conceder acesso a portas de número baixo a um processo:

Com isto pode conceder acesso permanente a um binário específico para ligar a portas de número baixo através do comando setcap:

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

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

Depois de fazer isto, /path/to/binary será capaz de se ligar a portos de números baixos. Note-se que deverá usar setcap no próprio binário em vez de um link simbólico.

Opção 2: Use authbind para conceder acesso único, com controlo mais fino de utilizador/grupo/porta:

A ferramenta authbind página homem ) existe precisamente para isto.

  1. instale authbind usando o seu gestor de pacotes favorito.

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

  3. Agora execute o seu comando via authbind (especificando opcionalmente --deep ou outros argumentos, ver a página man):


Há vantagens e inconvenientes em ambos os anteriores. A opção 1 concede confiança ao binário mas não proporciona qualquer controlo sobre o acesso por porta. A opção 2 concede confiança ao utilizador/grupo e fornece controlo sobre o acesso por porta mas as versões mais antigas suportavam apenas IPv4 (uma vez que escrevi originalmente isto, foram lançadas versões mais recentes com suporte IPv6).

29
29
29
2014-02-02 16:21:39 +0000

Dale Hagglund está no ponto. Portanto, vou apenas dizer a mesma coisa, mas de uma forma diferente, com algumas especificidades e exemplos. ☺

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

  • ter um programa pequeno, simples, facilmente auditável, que corre como superutilizador e liga a tomada de escuta;
  • ter outro programa pequeno, simples, facilmente auditável, que deixa cair privilégios, gerado pelo primeiro programa;
  • ter a carne do serviço, num terceiro programa separado, executado sob uma conta de não-superutilizador e corrente carregada pelo segundo programa, esperando simplesmente herdar um descritor de ficheiro aberto para a tomada.

Tem a ideia errada de onde está o alto risco. O alto risco está em leitura da rede e agir sobre o que é lido e não nos simples actos de abrir um socket, ligá-lo a uma porta, e chamar listen(). É a parte de um serviço que faz a comunicação real que é o alto risco. As partes que abrem, bind(), e listen(), e mesmo (até certo ponto) a parte que accepts(), não são o alto risco e podem ser executadas sob a égide do superutilizador. Não utilizam e actuam sobre (com excepção dos endereços IP de origem no caso accept()) os dados que estão sob o controlo de estranhos não confiáveis na rede.

Há muitas formas de o fazer.

inetd

Como diz Dale Hagglund, o antigo “super servidor de rede” inetd faz isto. A conta sob a qual o processo de serviço é executado é uma das colunas em inetd.conf. 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 do serviço principal num programa separado, exec()ed num processo de serviço que desdobra com um descritor de ficheiro aberto para o socket.

A dificuldade de auditar não é assim tão grande problema, pois basta auditar o único programa. O maior problema de inetd não é a auditoria, mas sim o facto de não proporcionar um simples controlo do serviço de tempo de execução, em comparação com as ferramentas mais recentes.

UCSPI-TCP e daemontools

Daniel J. Bernstein’s UCSPI-TCP e daemontools pacotes foram concebidos para fazer isto em conjunto. Em alternativa, pode-se utilizar o conjunto de ferramentas daemontools-encore equivalentes de Bruce Guenter.

O programa para abrir o descritor de ficheiros do socket e ligar-se à porta local privilegiada é tcpserver , da UCSPI-TCP. Faz tanto o listen() como o accept().

tcpserver depois cria ou um programa de serviço que deixa cair os privilégios de raiz (porque o protocolo a ser servido envolve começar como superutilizador e depois “log on”, como é o caso, por exemplo, um FTP ou um daemon SSH) ou setuidgid que é um programa autónomo pequeno e facilmente auditável que apenas deixa cair os privilégios e depois carrega em cadeia para o programa de serviço propriamente dito (nenhuma parte do qual corre assim com privilégios de superutilizador, como é o caso de, digamos, qmail-smtpd ).

Um serviço run script seria assim, por exemplo (este para dummyidentd por fornecer serviço IDENT nulo):

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

nosh

O meu pacote nosh foi concebido para o fazer. Tem uma pequena utilidade setuidgid, tal como as outras. Uma ligeira diferença é que é utilizável com os serviços systemd estilo “LISTEN_FDS”, bem como com os serviços UCSPI-TCP, pelo que o tradicional programa tcpserver é substituído por dois programas separados: tcp-socket-listen e tcp-socket-accept.

Novamente, os utilitários de uso único desovam e carregam-se uns aos outros. Uma peculiaridade interessante do design é que se pode abandonar os privilégios de superutilizador após listen() mas antes mesmo do accept(). Aqui está um script run para qmail-smtpd que de facto faz exactamente 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 correm sob a égide do super-utilizador são as pequenas ferramentas de carregamento em cadeia agnóstica de serviços fdmove, clearenv, envdir, softlimit, tcp-socket-listen, setuidgid, e sh. Ao ponto de smtp ser iniciado, a tomada está aberta e ligada à porta daemontools, e o processo já não tem privilégios de superutilizador.

s6, s6-networking, e execline

Laurent Bercot’s s6 e s6-networking pacotes foram concebidos para fazer isto em conjunto. Os comandos são estruturalmente muito semelhantes aos do run e do UCSPI-TCP.

s6-tcpserver scripts seriam muito semelhantes, excepto a substituição de tcpserver por s6-setuidgid e setuidgid por chpst. Contudo, também se poderia optar por fazer uso do conjunto de ferramentas execline da M. Bercot ao mesmo tempo.

Aqui está um exemplo de um serviço FTP, ligeiramente modificado de Wayne Marshall’s original , que utiliza execline, s6, s6-networking, e o programa servidor FTP de 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

Gerrit Pape’s ipsvd é outro conjunto de ferramentas que funciona na mesma linha do ucspi-tcp e s6-networking. As ferramentas são tcpsvd e fnord desta vez, mas fazem a mesma coisa, e o código de alto risco que faz a leitura, processamento, e escrita das coisas enviadas através da rede por clientes não confiáveis ainda se encontram num programa separado.

Aqui está M. Pape’s exemplo de correr run num script systemd:

#!/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

inetd , o novo sistema de supervisão de serviços e init que pode ser encontrado em algumas distribuições Linux, destina-se a fazer o que systemd pode fazer . No entanto, não utiliza um conjunto de pequenos programas autónomos. É preciso auditar systemd na sua totalidade, infelizmente.

Com systemd cria-se ficheiros de configuração para definir um socket que systemd escuta, e um serviço que systemd inicia. O ficheiro de “unidade” do serviço tem definições que permitem um grande controlo sobre o processo do serviço, incluindo o utilizador como ele é executado.

Com esse utilizador definido como não-superutilizador, listen() faz todo o trabalho de abrir o socket, ligá-lo a uma porta, e chamar accept() (e, se necessário, 0x6&) em processo #1 como superutilizador, e o processo de serviço que gera corre sem privilégios de superutilizador.

17
Advertisement
17
17
2018-06-27 07:00:56 +0000
Advertisement

Tenho uma abordagem bastante diferente. Queria utilizar a porta 80 para um servidor node.js. Não o consegui fazer desde que o Node.js foi instalado para um utilizador não-sudo. Tentei usar symlinks, mas não funcionou para mim.

Depois fiquei a saber que posso reencaminhar ligações de uma porta para outra porta. Assim, iniciei o servidor na porta 3000 e estabeleci uma porta de encaminhamento da porta 80 para a porta 3000. Esta ligação fornece os comandos reais que podem ser usados para o fazer. 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

Usei o segundo comando e funcionou para mim. Portanto, penso que este é um meio-termo para não permitir o acesso directo do utilizador-processo às portas inferiores, mas dando-lhes acesso usando o encaminhamento de portas.

4
4
4
2014-02-02 06:49:22 +0000

Os seus instintos são inteiramente correctos: é uma má ideia ter um grande programa complexo a funcionar como raiz, porque a sua complexidade torna-os difíceis de confiar.

Mas, é também uma má ideia permitir aos utilizadores regulares ligarem-se a portos privilegiados, porque tais portos representam normalmente serviços importantes do sistema.

A abordagem padrão para resolver esta aparente contradição é separação de privilégios. A ideia básica é separar o seu programa em duas (ou mais) partes, cada uma das quais faz uma parte bem definida da aplicação global, e que comunicam através de interfaces simples e limitadas.

No exemplo que dá, pretende separar o seu programa em duas partes. Uma que corre como raiz e se abre e liga à tomada privilegiada, e depois entrega-a de alguma forma à outra parte, que corre como um utilizador regular.

Estas duas formas principais de conseguir esta separação.

  1. um único programa que começa como raiz. A primeira coisa que faz é criar a tomada necessária, de uma forma tão simples e limitada quanto possível. Depois, deixa cair os privilégios, ou seja, converte-se num processo regular de modo de utilizador, e faz todo o resto funcionar. Deixar cair os privilégios correctamente é complicado, por isso, por favor, tire algum tempo para estudar a forma correcta de o fazer.

  2. Um par de programas que comunicam sobre um par de soquetes criado por um processo parental. Um programa de condutores não privilegiados recebe argumentos iniciais e talvez faça alguma validação dos argumentos básicos. Ele cria um par de tomadas ligadas através do par de tomadas(), e depois forca e executa dois outros programas que farão o verdadeiro trabalho, e comunicam através do par de tomadas. Um destes é privilegiado e criará o socket de servidor, e quaisquer outras operações privilegiadas, e o outro fará a execução de aplicações mais complexas e, portanto, menos fiáveis.

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

3
Advertisement
3
3
2019-09-13 07:38:46 +0000
Advertisement

Solução mais simples : remover todas as portas privilegiadas em linux

funciona em 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 VirtualBox com conta não-root)

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

Advertisement

Questões relacionadas

6
10
5
15
21
Advertisement
Advertisement