Existe uma maneira de processos não raiz se ligarem a portas "privilegiadas" no Linux?


389

É muito chato ter essa limitação na minha caixa de desenvolvimento, quando nunca haverá usuários além de mim.

Estou ciente das soluções padrão , mas nenhuma delas faz exatamente o que eu quero:

  1. authbind (A versão no teste Debian, 1.0, suporta apenas IPv4)
  2. Usando o destino REDIRECT do iptables para redirecionar uma porta baixa para uma porta alta (a tabela "nat" ainda não foi implementada para ip6tables, a versão IPv6 do iptables)
  3. sudo (Correr como root é o que estou tentando evitar)
  4. SELinux (ou similar). (Esta é apenas a minha caixa de desenvolvimento, não quero apresentar muita complexidade extra.)

Existe alguma sysctlvariável simples para permitir que processos não raiz se liguem a portas "privilegiadas" (portas menores que 1024) no Linux, ou estou sem sorte?

Edição: Em alguns casos, você pode usar os recursos para fazer isso.


5
Na minha experiência, uma razão para tentar isso é escrever um servidor da Web em vez de usar o Apache (ou lighttpd).
S.Lott

Eu adicionei o material setcap a minha resposta à quase idêntico stackoverflow.com/questions/277991/...
Paul Tomblin

15
Porque, neste caso, eu estava usando o IPv6, e foi por isso que algumas das soluções "habituais" (authbind e iptables REDIRECT) não funcionaram para mim.
Jason Creighton


11
Existem algumas maneiras de fazer isso. Consulte Permitir que o processo não raiz se ligue às portas 80 e 443? no Superusuário.
JWW

Respostas:


392

Ok, obrigado às pessoas que apontaram o sistema de CAP_NET_BIND_SERVICErecursos e recursos. Se você possui um kernel recente, é realmente possível usá-lo para iniciar um serviço como não raiz, mas vincular portas baixas. A resposta curta é que você faz:

setcap 'cap_net_bind_service=+ep' /path/to/program

E, a qualquer momento, a programpartir de então, ele terá a CAP_NET_BIND_SERVICEcapacidade. setcapestá no pacote debian libcap2-bin.

Agora, para as advertências:

  1. Você precisará de pelo menos um kernel 2.6.24
  2. Isso não funcionará se o seu arquivo for um script. (ou seja, usa uma linha #! para iniciar um intérprete). Nesse caso, até onde eu entendi, você teria que aplicar a capacidade ao próprio executável do intérprete, o que obviamente é um pesadelo de segurança, pois qualquer programa que use esse intérprete terá essa capacidade. Não consegui encontrar uma maneira fácil e limpa de solucionar esse problema.
  3. O Linux desativará o LD_LIBRARY_PATH em qualquer um programque tenha privilégios elevados, como setcapou suid. Portanto, se você programusa seus próprios .../lib/, talvez seja necessário procurar outra opção, como encaminhamento de porta.

Recursos:

Nota: O RHEL adicionou isso pela primeira vez na v6 .


3
Solução parcial para scripts: crie uma cópia do intérprete (por exemplo, bash), forneça os recursos, mas restrinja o acesso à cópia aos usuários que precisarem. Obviamente, esses usuários devem ser confiáveis, mas podem mudar o script de qualquer maneira.
Erich Kitzmueller 19/11/2009

3
Além do pacote debian acima mencionada (binário), o site do desenvolvedor é friedhoff.org/posixfilecaps.html papéis associados / apresentações / etc ...
RandomNickName42

11
no suse SLES 11.1, tive que adicionar o parâmetro do kernel file_caps = 1 ao grub menu.lst para que isso funcionasse.
Shay

2
Sim @ C.Ross, pois teria que ser aplicado /usr/bin/javae, em seguida, abriria a capacidade para qualquer aplicativo java em execução no sistema. Recursos muito ruins também não podem ser definidos por usuário.
precisa saber é o seguinte

5
A configuração do setcap persiste durante as reinicializações; caso contrário, existe um local padrão para colocar essa regra para que seja executada durante a inicialização do sistema? Existe /etc/security/capability.confalguma ajuda no Debian / Ubuntu?
precisa saber é o seguinte

34

Você pode fazer um redirecionamento de porta. É o que faço para um servidor de políticas do Silverlight em execução em uma caixa Linux

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300

5
Infelizmente, isso funcionará apenas para conexões roteadas, ou seja, não da máquina local.
Zbyszek

@joeytwiddle Acho que está no script que executa (inicia) o servidor, mas posso estar errado. Eu também gostaria de saber: P
Camilo Martin

@CamiloMartin Olhando novamente, a documentação do Debian que foi vinculada à pergunta recomenda colocá-la no seu script de firewall ou criar uma em /etc/network/if-up.d/firewall.
Joeytwiddle

8
@zbyszek Isso pode funcionar para conexões de máquinas locais, com uma regra adicional de iptables: stackoverflow.com/a/31795603/1356953
00500005

Nos kernels antigos, parece que isso não é suportado pelo IPv6 . Mas, aparentemente, ele é suportado no ip6tables v1.4.18 e no kernel Linux v3.8.
Craig McQueen

31

A maneira padrão é torná-los "setuid" para que eles sejam inicializados como root e descartam esse privilégio root assim que se conectam à porta, mas antes de começarem a aceitar conexões com ela. Você pode ver bons exemplos disso no código fonte do Apache e INN. Disseram-me que o Lighttpd é outro bom exemplo.

Outro exemplo é o Postfix, que usa vários daemons que se comunicam por meio de pipes, e apenas um ou dois deles (que fazem muito pouco, exceto aceitam ou emitem bytes) são executados como root e o restante é executado com um privilégio mais baixo.


11
Curiosamente, isso não funciona nas versões recentes do Linux (talvez apenas no Ubuntu) sem o conjunto CAP_SETUID. Portanto, se você precisar de setuid, precisará definir esse recurso de qualquer maneira.
19413 Matt

Descartar privs raiz é o caminho certo para fazer isso. Embora não seja necessário configurar o setuid se você tiver init / upstart / systemd, inicie o serviço.
Michael Hampton

2
Isso é difícil se o programa for escrito em uma linguagem interpretada ou interpretador de bytecode, como C # (Mono), Java, Python. (Aparentemente Perl tem feito via binfmt_misce sua bandeira 'C', eu não tenho certeza sobre os outros.)
Craig McQueen

muito pouco, exceto emitir bytes, isso não está fazendo muito pouco.
21716 Ryan

Se você definir um setuid binário, ele será executado como um processo raiz. A pergunta, no entanto, pergunta como fazer isso sem executar o seu binário como processo raiz. Esta solução é a resposta para uma pergunta diferente, a saber: "Como um usuário não privilegiado pode ter um processo vinculado a uma porta privilegiada", mas essa não é a pergunta, IMHO?
Michael Beer

24

Ou faça um patch do seu kernel e remova a verificação.

(Opção de último recurso, não recomendada).

Em net/ipv4/af_inet.c, remova as duas linhas que lêem

      if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
              goto out;

e o kernel não verifica mais portas privilegiadas.


22
Uma péssima idéia por várias razões.
Adam Lassek

24
Isso é uma má ideia. Mas realmente funcionaria. E com certeza me fez rir.
Oto Brglez

23
Claro que é uma má ideia. Foi por isso que disse último recurso. O objetivo do código aberto é que, se não funcionar da maneira desejada, você poderá alterá-lo.
Joshua

18
Não sei por que você foi votado. Os criadores de malware não se importam em qual porta eles escutam e ficam felizes em abrir uma porta maior que 1024. Parece-me que todas as portas devem exigir privilégios, ou nenhuma porta deve exigir privilégios. E exigir que o root abra uma porta abaixo de 1024 significa apenas que você tem um aplicativo de alto risco em execução como root. Parece-me uma ideia muito idiota. Talvez eu estou faltando alguma coisa aqui ...
JWW

9
Naquela época, a porta <1024 era usada nos protocolos UNIX para UNIX para provar que o código em execução na outra extremidade estava em execução como raiz. Isso funcionou razoavelmente bem para um conjunto de servidores UNIX com segurança comum.
217 Joshua Joshua

22

Você pode configurar um túnel SSH local, por exemplo, se você deseja que a porta 80 atinja seu aplicativo vinculado a 3000:

sudo ssh $USERNAME@localhost -L 80:localhost:3000 -N

Isso tem a vantagem de trabalhar com servidores de script e ser muito simples.


11
Meu favorito pessoal. Muito fácil de ligar e desligar e não requer alterações em todo o sistema. Boa ideia!
Dan Passaro

Isso é incrível. De longe a resposta mais simples.
precisa

11
Eu esperaria que isso fosse moderadamente caro e não fosse uma boa idéia em qualquer situação vinculada ao desempenho, mas não posso ter certeza sem testá-lo. A criptografia SSH custa uma quantia diferente de zero.
Lahwran

3
Isso poderia ser feito com netcat com nenhuma sobrecarga ssh:sudo nc -l 80 | nc localhost 3000
Bugster

11
Você está criptografando + decriptografando apenas para mover os dados para outra porta na mesma máquina? Além disso, isso não funciona para o tráfego UDP.
Daniel F

19

Atualização 2017:

Use authbind


Muito melhor do que CAP_NET_BIND_SERVICE ou um kernel personalizado.

  • CAP_NET_BIND_SERVICE concede confiança ao binário, mas não fornece controle sobre o acesso por porta.
  • O Authbind concede confiança ao usuário / grupo e fornece controle sobre o acesso por porta e suporta IPv4 e IPv6 (o suporte ao IPv6 foi adicionado recentemente).

    1. Instalar: apt-get install authbind

    2. Configure o acesso às portas relevantes, por exemplo, 80 e 443 para 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. Execute seu comando via authbind
      (opcionalmente especificando --deepou outros argumentos, consulte a página do manual):

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

      por exemplo

      authbind --deep java -jar SomeServer.jar
      

Como acompanhamento da fabulosa recomendação de Joshua (= não recomendada, a menos que você saiba o que faz) para hackear o kernel:

Eu publiquei aqui primeiro .

Simples. Com um kernel normal ou antigo, você não.
Como apontado por outros, o iptables pode encaminhar uma porta.
Como também apontado por outros, CAP_NET_BIND_SERVICE também pode fazer o trabalho.
É claro que CAP_NET_BIND_SERVICE falhará se você iniciar seu programa a partir de um script, a menos que defina o limite no interpretador de shell, o que é inútil, você poderá executar o serviço como root ...
por exemplo, para Java, você deve aplicá-lo para a JVM JAVA

sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/java-8-openjdk/jre/bin/java

Obviamente, isso significa que qualquer programa Java pode ligar portas do sistema.
Dito para mono / .NET.

Também tenho certeza de que o xinetd não é a melhor das idéias.
Mas como os dois métodos são hacks, por que não apenas elevar o limite, elevando a restrição?
Ninguém disse que você precisa rodar um kernel normal, então você pode rodar o seu próprio.

Você acabou de baixar a fonte para o kernel mais recente (ou o mesmo que você possui atualmente). Depois, você vai para:

/usr/src/linux-<version_number>/include/net/sock.h:

Lá você procura esta linha

/* Sockets 0-1023 can't be bound to unless you are superuser */
#define PROT_SOCK       1024

e mude para

#define PROT_SOCK 0

se você não quiser ter uma situação ssh insegura, altere-a para o seguinte: #define PROT_SOCK 24

Geralmente, eu usaria a configuração mais baixa que você precisa, por exemplo, 79 para http ou 24 ao usar SMTP na porta 25.

Isso já é tudo.
Compile o kernel e instale-o.
Reinicie.
Concluído - esse limite estúpido é GONE e também funciona para scripts.

Aqui está como você compila um kernel:

https://help.ubuntu.com/community/Kernel/Compile

# You can get the kernel-source via package linux-source, no manual download required
apt-get install linux-source fakeroot

mkdir ~/src
cd ~/src
tar xjvf /usr/src/linux-source-<version>.tar.bz2
cd linux-source-<version>

# Apply the changes to PROT_SOCK define in /include/net/sock.h

# Copy the kernel config file you are currently using
cp -vi /boot/config-`uname -r` .config

# Install ncurses libary, if you want to run menuconfig
apt-get install libncurses5 libncurses5-dev

# Run menuconfig (optional)
make menuconfig

# Define the number of threads you wanna use when compiling (should be <number CPU cores> - 1), e.g. for quad-core
export CONCURRENCY_LEVEL=3
# Now compile the custom kernel
fakeroot make-kpkg --initrd --append-to-version=custom kernel-image kernel-headers

# And wait a long long time

cd ..

Em poucas palavras, use o iptables se quiser permanecer seguro, compile o kernel se quiser ter certeza de que essa restrição nunca mais o incomoda.


Alguma razão para o voto negativo? Odiadores de raiz ou as instruções de compilação do kernel não funcionaram?
Stefan Steiger

4
modificar o kernel torna as atualizações futuras muito mais dolorosas. eu não faria isso, porque eu sei que ficaria com preguiça de continuar atualizando meu kernel regularmente. é bom que isso seja possível em teoria, mas não é uma opção viável em muitas circunstâncias.
kritzikratzi # 18715

Talvez uma maneira mais segura de fazer isso seja alterar o chmod 777 / etc / authbind / byport / 80 pelo chmod 544 / etc / authbind / byport / 23 do que o login do chown: group / etc / authbind / byport / 23
Jérôme B

18

Os recursos de arquivo não são ideais, pois podem ser interrompidos após uma atualização do pacote.

A solução ideal, IMHO, deve ser a capacidade de criar um shell com um CAP_NET_BIND_SERVICEconjunto herdável .

Aqui está uma maneira um tanto complicada de fazer isso:

sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
     --caps='cap_net_bind_service+pei' -- \
     YOUR_COMMAND_GOES_HERE"

capshO utilitário pode ser encontrado no pacote libcap2-bin nas distribuições Debian / Ubuntu. Aqui está o que se passa:

  • sgaltera o ID do grupo efetivo para o do usuário daemon. Isso é necessário porque capshdeixa o GID inalterado e, definitivamente, não o queremos.
  • Define o bit 'manter recursos na alteração do UID'.
  • Muda o UID para $DAEMONUSER
  • Descarta todos os limites (neste momento, todos os limites ainda estão presentes devido a --keep=1), exceto herdáveiscap_net_bind_service
  • Executa seu comando ('-' é um separador)

O resultado é um processo com usuário e grupo especificados e cap_net_bind_serviceprivilégios.

Como exemplo, uma linha do ejabberdscript de inicialização:

sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"

E na verdade não funciona. As maiúsculas não são preservadas na opção de ID do usuário. Funcionará se você quiser executar como root, mas com todos os recursos descartados.
precisa

Se eu tentar isso, recebo "não é possível executar o arquivo binário". Na verdade, não consigo capshfazer nada além de "não é possível executar o arquivo binário" ao usar as opções --ou ==. Eu me pergunto o que estou perdendo.
Craig McQueen

@CraigMcQueen, tudo depois --é passado para /bin/bash, então você pode tentar -c 'your command'. Infelizmente, parece que estou enfrentando o mesmo problema que o @Cyberax, porque recebo "permissão negada" ao tentar bind.
Amir

Para que isso funcione sem definir recursos de arquivo (ou executado como root), você precisaria usar recursos ambientais , conforme descrito em esta resposta Unix.SE . Você também pode usar --gidem vez de sg(ou --userquais conjuntos --uid, --gide --groups): sudo capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' --keep=1 --user="$service_user" --addamb=cap_net_bind_service -- -c 'exec $service $service_args'
Kevinoid

18

Por algum motivo, ninguém menciona sobre a redução do sysctl net.ipv4.ip_unprivileged_port_start para o valor necessário. Exemplo: precisamos vincular nosso aplicativo à porta 443.

sysctl net.ipv4.ip_unprivileged_port_start=443

Alguns podem dizer que há um problema de segurança em potencial: usuários sem privilégios agora podem se conectar a outras portas privilegiadas (444-1024). Mas você pode resolver esse problema facilmente com o iptables, bloqueando outras portas:

iptables -I INPUT -p tcp --dport 444:1024 -j DROP
iptables -I INPUT -p udp --dport 444:1024 -j DROP

Comparação com outros métodos. Este método:

  • a partir de algum ponto (IMO) é ainda mais seguro do que configurar CAP_NET_BIND_SERVICE / setuid, já que um aplicativo não é totalmente configurado, mesmo parcialmente (os recursos realmente são). Por exemplo, para capturar um coredump de aplicativo habilitado para recurso, você precisará alterar sysctl fs.suid_dumpable (o que leva a outros possíveis problemas de segurança). Além disso, quando CAP / suid é definido, o diretório / proc / PID é de propriedade da raiz, portanto seu usuário não root não terá informações / controle completos do processo em execução; por exemplo, o usuário não poderá (em casos comuns) determinar quais conexões pertencem ao aplicativo via / proc / PID / fd / (netstat -aptn | grep PID).
  • tem uma desvantagem de segurança: enquanto o aplicativo (ou qualquer aplicativo que use as portas 443-1024) estiver inativo por algum motivo, outro aplicativo poderá assumir a porta. Mas esse problema também pode ser aplicado ao CAP / suid (caso você o configure no intérprete, por exemplo, java / nodejs) e no iptables-redirect. Use o método systemd-socket para excluir esse problema. Use o método authbind para permitir apenas a ligação especial do usuário.
  • não requer a configuração de CAP / suid toda vez que você implanta uma nova versão do aplicativo.
  • não requer suporte / modificação de aplicativo, como o método systemd-socket.
  • não requer reconstrução do kernel (se a versão em execução suportar esta configuração sysctl)
  • não faz LD_PRELOAD como o método authbind / privbind, isso pode afetar o desempenho, a segurança, o comportamento (não? Não foi testado). No resto, authbind é um método realmente flexível e seguro.
  • super executa o método REDIRECT / DNAT do iptables, uma vez que não requer conversão de endereço, rastreamento do estado da conexão, etc. Isso é perceptível apenas em sistemas de alta carga.

Dependendo da situação, eu escolheria entre sysctl, CAP, authbind e iptables-redirect. E isso é ótimo, pois temos tantas opções.


2
Obrigado pela ótima resposta! Parece que essa funcionalidade apareceu pela primeira vez no Linux 4.11 em abril de 2017 , então não existia em 2009 quando eu fiz essa pergunta. Também fiz um teste rápido e parece que também funciona para o IPV6, mesmo que "ipv4" esteja no nome do sysctl.
Jason Creighton

15

Duas outras possibilidades simples:

Existe uma solução antiga (fora de moda) para "um daemon que se liga a uma porta baixa e controla o seu daemon". É chamado inetd (ou xinetd). Os contras são:

  • seu daemon precisa falar sobre stdin / stdout (se você não controla o daemon - se você não possui a fonte - então isso talvez seja um limitador de exibição, embora alguns serviços possam ter um sinalizador de compatibilidade com inetd)
  • um novo processo daemon é bifurcado para cada conexão
  • é um elo extra na cadeia

Prós:

  • disponível em qualquer UNIX antigo
  • Depois que o sysadmin tiver configurado a configuração, você estará pronto para o desenvolvimento (quando reconstruir seu daemon, poderá perder os recursos do setcap? E depois precisará voltar ao administrador "por favor, senhor .. . ")
  • o daemon não precisa se preocupar com esse material de rede, apenas com o stdin / stdout
  • pode configurar para executar seu daemon como um usuário não root, conforme solicitado

Outra alternativa: um proxy hackeado (netcat ou até algo mais robusto ) da porta privilegiada para alguma porta arbitrária e de numeração alta, na qual você pode executar o daemon de destino. (O Netcat obviamente não é uma solução de produção, mas "apenas minha caixa de desenvolvimento", certo?). Dessa forma, você poderia continuar usando uma versão do servidor compatível com a rede, precisaria apenas do root / sudo para iniciar o proxy (na inicialização), não dependeria de recursos complexos / potencialmente frágeis.


11
Boa sugestão. Por alguma razão, eu nem pensei em usar o inetd. Exceto que o serviço em questão é baseado em UDP, por isso é um pouco mais complicado que os serviços TCP. Eu tenho uma solução trabalhando agora com o setcap, mas vou ter que pensar um pouco sobre isso.
Jason Creighton

Para a segunda opção, você pode provavelmente mesmo de começar o proxy usando inetd ... (Mas iptables é provavelmente menor sobrecarga ...)
Gert van den Berg

14

Minha "solução padrão" usa socat como o redirecionador de espaço do usuário:

socat tcp6-listen:80,fork tcp6:8080

Cuidado para não escalar, bifurcar é caro, mas é assim que o socat funciona.


13

O Linux suporta recursos para suportar permissões mais refinadas do que apenas "este aplicativo é executado como raiz". Um desses recursos é CAP_NET_BIND_SERVICEsobre a ligação a uma porta privilegiada (<1024).

Infelizmente, não sei como explorar isso para executar um aplicativo como não raiz e ainda o fornecer CAP_NET_BIND_SERVICE(provavelmente usando setcap, mas é provável que exista uma solução existente para isso).


Não funciona para scripts ou programas individuais JVM / mono.
Stefan Steiger

13

Eu sei que essa é uma pergunta antiga, mas agora com os kernels recentes (> = 4.3), finalmente há uma boa resposta para isso - os recursos do ambiente.

A resposta rápida é pegar uma cópia da versão mais recente (ainda não lançada) do libcap do git e compilá-la. Copie o progs/capshbinário resultante em algum lugar ( /usr/local/biné uma boa escolha). Então, como root, inicie seu programa com

/usr/local/bin/capsh --keep=1 --user='your-service-user-name' \
    --inh='cap_net_bind_service' --addamb='cap_net_bind_service' \ 
    -- -c 'your-program'

Em ordem, estamos

  • Declarando que, quando trocamos de usuário, queremos manter nossos conjuntos de recursos atuais
  • Alternando usuário e grupo para 'your-service-user-name'
  • Adicionando a cap_net_bind_servicecapacidade aos conjuntos herdados e ambientais
  • Bifurcação bash -c 'your-command'(desde que capshinicia automaticamente o bash com os argumentos seguintes --)

Há muita coisa acontecendo sob o capô aqui.

Em primeiro lugar, estamos rodando como root; portanto, por padrão, temos um conjunto completo de recursos. Incluído nisso está a capacidade de alternar uid & gid com os syscalls setuide setgid. No entanto, normalmente quando um programa faz isso, ele perde seu conjunto de recursos - é para que a maneira antiga de eliminar raiz setuidainda funcione. O --keep=1sinalizador informa capshpara emitir o prctl(PR_SET_KEEPCAPS)syscall, que desativa a queda de recursos ao alterar o usuário. A mudança real dos usuários capshacontece com a --userbandeira, que é executada setuide setgid.

O próximo problema que precisamos resolver é como definir as capacidades de uma maneira que continue depois que nós, execnossos filhos. O sistema de recursos sempre teve um conjunto de recursos 'herdado', que é "um conjunto de recursos preservados em um execve (2)" [ recursos (7) ]. Embora isso pareça resolver nosso problema (apenas defina a cap_net_bind_servicecapacidade como herdada, certo?), Na verdade, isso só se aplica a processos privilegiados - e nosso processo não é mais privilegiado, porque já mudamos de usuário (com o --usersinalizador).

O novo conjunto de recursos ambientais soluciona esse problema - é "um conjunto de recursos preservados em uma execve (2) de um programa que não é privilegiado". Ao colocar cap_net_bind_serviceo ambiente definido, quando o capshexec for nosso programa de servidor, nosso programa herdará esse recurso e poderá vincular ouvintes a portas baixas.

Se você estiver interessado em saber mais, a página do manual de recursos explica isso em detalhes. Correndo capshatravés stracetambém é muito informativo!


A --addambbandeira foi introduzida com a libcap 2.26. Meu sistema tinha 2,25 e eu tive que construir a partir da fonte.
ngreen

12

TLDR: para "a resposta" (como eu a vejo), vá para a parte >> TLDR << nesta resposta.

OK, eu descobri (desta vez de verdade), a resposta a esta pergunta, e essa minha resposta também é uma maneira de me desculpar por promover outra resposta (aqui e no twitter) que eu pensei que era "a melhor ", mas depois de tentar, descobri que eu estava enganado sobre isso. Aprenda com meus erros, crianças: não promova algo até que você realmente tente!

Mais uma vez, revi todas as respostas aqui. Eu tentei alguns deles (e optei por não tentar outros porque simplesmente não gostei das soluções). Eu pensei que a solução era usar systemdcom seus Capabilities=e CapabilitiesBindingSet=configurações. Depois de lutar com isso por algum tempo, descobri que essa não é a solução porque:

Os recursos destinam-se a restringir os processos raiz!

Como o OP afirmou sabiamente, é sempre melhor evitar isso (para todos os seus daemons, se possível!).

Você não pode usar as opções relacionadas aos Recursos com User=e Group=nos systemdarquivos da unidade, porque os recursos SEMPRE são redefinidos quando execev(ou seja qual for a função) é chamada. Em outras palavras, quando systemdbifurca e diminui suas permissões, os recursos são redefinidos. Não há como contornar isso, e toda essa lógica de ligação no kernel é básica em torno de uid = 0, não de recursos. Isso significa que é improvável que os Recursos sejam a resposta certa para essa pergunta (pelo menos em breve). Aliás, setcapcomo outros já mencionaram, não é uma solução. Não funcionou para mim, não funciona muito bem com scripts, e eles são redefinidos de qualquer maneira sempre que o arquivo é alterado.

Na minha escassa defesa, afirmei (no comentário que apaguei agora), que a sugestão de iptables de James (que o OP também menciona) era a "segunda melhor solução". :-P

>> TLDR <<

A solução é combinar systemdcom iptablescomandos on-the-fly , como este ( extraído do DNSChain ):

[Unit]
Description=dnschain
After=network.target
Wants=namecoin.service

[Service]
ExecStart=/usr/local/bin/dnschain
Environment=DNSCHAIN_SYSD_VER=0.0.1
PermissionsStartOnly=true
ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
User=dns
Group=dns
Restart=always
RestartSec=5
WorkingDirectory=/home/dns
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyDirectories=/etc

# Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
# Capabilities=cap_net_bind_service+pei
# SecureBits=keep-caps

[Install]
WantedBy=multi-user.target

Aqui realizamos o seguinte:

  • O daemon escuta 5333, mas as conexões são aceitas com sucesso em 53 graças a iptables
  • Podemos incluir os comandos no próprio arquivo da unidade e, assim, salvamos dores de cabeça nas pessoas. systemdlimpa as regras de firewall para nós, removendo-as quando o daemon não está em execução.
  • Nós nunca rodamos como root e tornamos impossível a escalação de privilégios (pelo menos é o que systemdafirma), supostamente mesmo se o daemon estiver comprometido e configurado uid=0.

iptablesainda é, infelizmente, um utilitário feio e difícil de usar. Se o daemon estiver ouvindo em eth0:0vez de eth0, por exemplo, os comandos serão ligeiramente diferentes .


2
Ninguém deve usar aliases de estilo antigo como eth0:0mais, a menos que eles tenham uma distribuição Linux realmente antiga . Eles estão obsoletos há anos e acabarão desaparecendo.
Michael Hampton

11
Eu acho que você quer dizer OpenVZ. (O SolusVM é um painel de controle.) E sim, o OpenVZ faz muitas coisas erradas, sendo a rede apenas uma delas.
Michael Hampton

11
Não, quero dizer com SolusVM. De / etc / network / interfaces:# Generated by SolusVM
Greg Slepak

11
Ainda não é o OpenVZ ... Pelo menos não estou usando, nem meu VPS.
Greg Slepak

7
Obrigado por apontar os recursos do systemd. No entanto, não há necessidade de tabelas de ip complexas quando o systemd inicia o binário diretamente (não um script). Definir AmbientCapabilities=CAP_NET_BIND_SERVICEfuncionou perfeitamente bem para mim em combinação com User=.
Ruud


10

O redirecionamento de porta fazia mais sentido para nós, mas encontramos um problema em que nosso aplicativo resolveria um URL localmente que também precisava ser redirecionado; (isso significa que você brinca ).

Isso também permitirá que você seja redirecionado ao acessar o URL na máquina local.

iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -A OUTPUT -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080

9

Suporte moderno ao Linux /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.


8

Com o systemd, você só precisa modificar levemente seu serviço para aceitar soquetes pré-ativados.

Posteriormente, você pode usar o systemd socket ativar .

Não são necessários recursos, iptables ou outros truques.

Este é o conteúdo dos arquivos systemd relevantes deste exemplo de servidor http python simples

Arquivo httpd-true.service

[Unit]
Description=Httpd true 

[Service]
ExecStart=/usr/local/bin/httpd-true
User=subsonic

PrivateTmp=yes

Arquivo httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

Na inicialização:

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080

Em seguida, você pode vincular à porta para a qual encaminhar.


não --to-portexiste? man iptablesapenas menciona --to-ports(plural).
Abdull

ive notado algumas diferenças e eu fui pulando
James

Veja esta resposta para saber como combinar isso systemd.
Greg Slepak


3

Há também o 'caminho djb'. Você pode usar esse método para iniciar seu processo como root em execução em qualquer porta em tcpserver; depois, ele entregará o controle do processo ao usuário especificado imediatamente após o início do processo.

#!/bin/sh

UID=$(id -u username)
GID=$(id -g username)
exec tcpserver -u "${UID}" -g "${GID}" -RHl0 0 port /path/to/binary &

Para obter mais informações, consulte: http://thedjbway.b0llix.net/daemontools/uidgid.html


1

Como o OP é apenas desenvolvimento / teste, soluções menos sofisticadas podem ser úteis:

O setcap pode ser usado no intérprete de um script para conceder recursos aos scripts. Se setcaps no binário global do intérprete não for aceitável, faça uma cópia local do binário (qualquer usuário pode) e instale o root no setcap nesta cópia. Python2 (pelo menos) funciona corretamente com uma cópia local do intérprete na sua árvore de desenvolvimento de scripts. Nenhum suid é necessário para que o usuário root possa controlar quais recursos os usuários têm acesso.

Se você precisar rastrear atualizações de todo o sistema para o intérprete, use um shell script como o seguinte para executar seu script:

#!/bin/sh
#
#  Watch for updates to the Python2 interpreter

PRG=python_net_raw
PRG_ORIG=/usr/bin/python2.7

cmp $PRG_ORIG $PRG || {
    echo ""
    echo "***** $PRG_ORIG has been updated *****"
    echo "Run the following commands to refresh $PRG:"
    echo ""
    echo "    $ cp $PRG_ORIG $PRG"
    echo "    # setcap cap_net_raw+ep $PRG"
    echo ""
    exit
}

./$PRG $*

1

Eu tentei o método iptables PREROUTING REDIRECT. Em kernels antigos, parece que esse tipo de regra não é suportado no IPv6 . Mas, aparentemente, agora é suportado no ip6tables v1.4.18 e no kernel do Linux v3.8.

Também descobri que PREROUTING REDIRECT não funciona para conexões iniciadas dentro da máquina. Para trabalhar com conexões da máquina local, adicione também uma regra de SAÍDA - consulte o redirecionamento de porta iptables não funcionando para o host local . Por exemplo, algo como:

iptables -t nat -I OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080

Também descobri que PREROUTING REDIRECT também afeta pacotes encaminhados . Ou seja, se a máquina também estiver encaminhando pacotes entre interfaces (por exemplo, se estiver atuando como um ponto de acesso Wi-Fi conectado a uma rede Ethernet), a regra iptables também capturará as conexões dos clientes conectados aos destinos da Internet e as redirecionará para a máquina. Não era isso que eu queria; eu só queria redirecionar as conexões direcionadas à própria máquina. Descobri que posso afetar apenas pacotes endereçados à caixa, adicionando -m addrtype --dst-type LOCAL. Por exemplo, algo como:

iptables -A PREROUTING -t nat -p tcp --dport 80 -m addrtype --dst-type LOCAL -j REDIRECT --to-port 8080

Uma outra possibilidade é usar o encaminhamento de porta TCP. Por exemplo, usando socat:

socat TCP4-LISTEN:www,reuseaddr,fork TCP4:localhost:8080

No entanto, uma desvantagem desse método é que o aplicativo que está atendendo na porta 8080 não conhece o endereço de origem das conexões de entrada (por exemplo, para log ou outros fins de identificação).


0

Resposta em 2015 / Sep:

O ip6tables agora suporta NAT do IPV6: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Você precisará do kernel 3.7+

Prova:

[09:09:23] root@X:~ ip6tables -t nat -vnL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:80 redir ports 8080
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:443 redir ports 1443

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.