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" inetd
faz 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. inetd
O 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()
.
tcpserver
em 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 setuidgid
que é 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 run
script 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 setuidgid
utilitário, assim como os outros. Uma pequena diferença é que ele pode ser usado com os systemd
serviços "LISTEN_FDS", bem como com os serviços UCSPI-TCP, portanto, o tcpserver
programa tradicional é substituído por dois programas separados: tcp-socket-listen
e 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 run
script para o qmail-smtpd
que 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 à smtp
porta 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 daemontools
UCSPI-TCP.
run
os scripts seriam praticamente os mesmos, exceto pela substituição de s6-tcpserver
for tcpserver
e s6-setuidgid
for 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 chpst
e tcpsvd
desta 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 fnord
em um run
script:
#!/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 inetd
pode . No entanto, ele não usa um conjunto de pequenos programas independentes. É preciso fazer uma auditoria systemd
completa, infelizmente.
Com systemd
um cria arquivos de configuração para definir um soquete que systemd
escuta, e um serviço que é systemd
iniciado. 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, systemd
faz 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.