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.