DevSecOps

22 mar, 2011

Limitando recursos no Linux com PAM

Publicidade

Qual a importância da segurança em um aplicativo? Este tema será
sempre procurado e discutido à medida que novas técnicas surgem em um
sistema operacional open source como o Linux.

Soluções cada vez
melhores surgem de forma a reforçar este tema e a estimular os usuários
a conhecer uma gama cada vez maior de opções de autenticação. O PAM (Pluggable Authentication Module) é uma delas.

Poder escolher a forma como o aplicativo autentica o usuário
já é um privilégio aos adeptos do PAM. Se você considera as senhas
insuficientes para garantir segurança e deseja utilizar um outro sistema
de autenticação – como leitor de digital ou retina, se não quer permitir
que o usuário se conecte em determinados horários do dia ou não tenha
acesso a certos recursos do computador.

Ou ainda quer configurar seu
aplicativo para somente o usuário root fazer login além de
desejar que um dicionário seja consultado na hora da criação de uma
senha, o PAM é a API indicada. Estas tarefas são apenas um pequeno
exemplo do que ele pode fazer se configurado de forma correta.

Antes da utilização do PAM, era usado um sistema de login
próprio a cada programa, com senha criptografada, onde a autenticação
consistia basicamente em associar usuários a suas entradas no arquivo
/etc/passwd.

Se o método de autenticação fosse mudado, os programas
também teriam que ser alterados, além de inviabilizar uma mudança da
base de usuários para fora do /etc/passwd, por exemplo, tornando a
tarefa de reconfiguração árdua e tediosa. Assim, o PAM trouxe
praticidade e maior segurança ao sistema de autenticação do Linux e
Unix.

O PAM surgiu como um mecanismo intermediário, uma camada
entre as aplicações e a autenticação real. Ele trabalha com bibliotecas
carregáveis chamadas pelas aplicações e permite o administrador de
sistema ter flexibilidade ao escolher o método adotado pela aplicação
para autenticar o usuário. Se quisermos fazer alguma modificação sobre o
método de autenticação, basta modificar a configuração do PAM.

Podemos configurar o PAM através do diretório “/etc/pam.d” ou
do arquivo “/etc/pam.conf”. Se ambos forem configurados, o arquivo
“/etc/pam.conf”
será ignorado, dando preferência ao diretório.

No diretório “/etc/pam.d” há um arquivo de configuração para
cada aplicação. Dentro destes arquivos encontramos o nome do serviço,
tipo de módulo, opções de controle, caminho do módulo e opções do módulo
ou argumentos.

  • Nome do serviço: tipo de serviço associado a esta entrada.
  • Tipo de módulo: especifica o tipo de módulo usado. Eles podem ser:
  1. Auth: referente à autenticação;
  2. Account: autorização para uso de programas que utilizam login;
  3. Passwd: diz respeito à alteração e atualização de senha;
  4. Session: inicia e termina sessões e determina o ambiente do usuário.
  • Opções de controle: controla a prioridade de cada módulo e indica
    como a biblioteca do PAM vai reagir diante do sucesso ou falha do
    módulo. Suas opções podem ser:
  • Required: Todos os módulos devem ser aceitos. Sua falha não será mostrada ao usuário até que todos
    os módulos restantes sejam executados.
  • Requisite: Parecido com o required mas no caso de falha, o controle é retornado à aplicação.
  • Sufficient: Se este módulo obtiver êxito e nenhum módulo
    required anterior falhar, todos os outros módulos da pilha serão
    ignorados.
  • Optional: Só possui influência caso módulos anteriores da mesma
    classe não tenham êxito e não forem required. Neste caso, apenas um
    módulo opcional pode obter sucesso.
  • Caminho do Módulo: Indica o caminho para a biblioteca a ser utilizada pelo módulo.
  • Argumentos: Opção semelhante aos argumentos disponíveis em um comando Linux.

Uma configuração geral ocuparia uma linha e teria a seguinte forma:

<Nome do serviço> <Tipo de módulo> <Opções de controle> <Caminho do Módulo>  <Argumentos>	

Exemplo de um arquivo contido no diretório “/etc/pam.d”. O arquivo “rlogin”:
#%PAM-1.0
auth required  /lib/security/pam_nologin.so.
Este módulo é responsável por negar login aos usuários que não sejam root.

Configuração do pam_limits

Limitar os usuários a determinadas ações ou recursos no sistema pode
ser algo muito útil. É possível evitar um ataque DoS de sistema, que consiste em impedir usuários de utilizarem um serviço ou
recurso computacional, ou mesmo dividir os recursos de forma mais
justa, quando cada usuário ou grupo tem um limite definido. Vamos
abordar agora o módulo responsável por tal tarefa: o pam_limits.

Para configurá-lo, primeiramente devemos tê-lo habilitado na
distribuição do Linux que estamos utilizando. Em uma distribuição
Debian, o pam_limits deve vir habilitado, portanto presente no diretório
/etc/pam.d com a seguinte linha não comentada (uma linha está comentado
quando é iniciada com o caractere #) :

session  required  pam_limits.so

No diretório /lib/security, encontraremos o arquivo binário pam_limits.so. Seu arquivo de configuração é o /etc/security/limits.conf. Depois da
leitura desse arquivo, os arquivos individuais *.conf em
/etc/security/limits.d/
são lidos.

O efeito dos arquivos individuais são
os mesmos que se eles estivessem concatenados. O arquivo limits.conf é o
arquivo em que os limites para usuários ou grupos são definidos. Ao
visualizarmos o conteúdo do arquivo podemos observar a forma de
descrição de limites e suas definições:

<usuário/grupo> <tipo de limite> <recurso>  <valor do limite>

Cada campo no limits.conf é separado por tabulação. Nele, cada opção
de limitação de recurso oferecida pelo PAM tem uma breve explicação na Tabela 1 e um exemplo. A primeira coluna (usuário/grupo) deve conter o
nome do grupo ou usuário. Se for um grupo, seu nome deve ser precedido
por “@”.

No tipo de limite, podemos ter soft ou hard. O tipo soft apenas emite um aviso quando o limite é atingido. Já no tipo hard, quando o limite é alcançado, o sistema restringe o recurso.

Com relação ao recurso e valor do limite, temos alguns exemplos abaixo:

Recurso Descrição
core Limita o tamanho do arquivo coredump (KB)
data Tamanho de dados que um processo pode usar (KB)
fsize Limita o tamanho de um arquivo a ser criado (KB)
memlock Tamanho de memória alocada utilizada pelos processos (KB)
nofile Número de arquivos abertos ao mesmo tempo
rss Tamanho máximo de memória compartilhada (KB). Ignorado no Linux 2.4.30 e superiores
stack Valor máximo da pilha de um processo (KB)
cpu Tempo máximo de uso da CPU (em minutos)
nproc Limita o número de processos executados ao mesmo tempo
as Limita o espaço de endereçamento (KB)
maxlogins Número de logins efetuado pelo usuário (Linux 2.4 e superiores)
maxsyslogins Número de logins no sistema
priority Estabelece a prioridade a ser usada para rodar os processos do usuário
locks Número máximo de locks em arquivos que o usuário pode ter
sigpending Número máximo de sinais pendentes do usuário (Linux 2.6 e superiores)
msgqueue Memória máxima utilizada pela fila de mensagens POSIX em bytes (Linux 2.6 e superiores)
nice Número máximo de prioridade determinada pelo usuário, de -20 até 19 (Linux 2.6.12 e superiores)
rtprio Máxima prioridade de tempo real permitida (Linux 2.6.12 e superioes)

Pontos importantes:

  • Todos os recursos, exceto nice e priority, aceitam o valor do limite como -1, unlimited ou infinity indicando sem limites;
  • Os limites impostos pelo pam_limits são baseados por usuário/grupo;
  • Usuários com uid=0 são afetados, ou seja, é possível limitar recursos nas sessões do usuário root
  • Limites por usuário têm prioridade sobre limites por grupo; por exemplo, se não existem limites para o grupo desenvolvedores, mas um dos membros do grupo tem um recurso limitado, o usuário será limitado a esse recurso;
  • Por último, temos que ressaltar que todos os limites configurados são por login/sessão.
    Esses limites também não são permanentes e nem globais, perduram somente na sessão.

Para testar o pam_limits, deve-se abrir o arquivo limits.conf e
acrescentar a linha com as configurações de acordo com a necessidade.
Segue um exemplo.

Vamos limitar o número de arquivos abertos ao mesmo tempo com o recurso nofile: usuário soft nofile 70

Agora, vamos limitar o número de processos rodando ao mesmo tempo na CPU: usuário soft nproc 42

Limitando o tamanho de um arquivo criado pelo usuário: usuário hard fsize 1000

Depois é necessário abrir uma nova sessão, e ao tentar criar um arquivo com o comando: dd if=/dev/zero of=teste bs=1024 count=1001, veremos que não é possível.

Um comando muito interessante para ver todos os limites impostos para o usuário na sessão atual é ulimit –a. Na próxima sessão será apresentado um exemplo um pouco mais
sofisticado, porém não tão complicado, para evitar alguns tipos de
ataque contra o sistema operacional.

Evitando fork e malloc bomb

Como boas práticas, podemos citar alguns recursos que devem ter maior
atenção. O número máximo de processos criados numa sessão deve ser
configurado para evitar um ataque fork bomb, o qual cria
diversos processos até atingir o limite máximo do sistema operacional,
impossibilitando a criação de processos novos por qualquer usuário.

Para
que a configuração desse limite funcione bem, deve-se configurar também
o limite máximo de logins permitidos ao mesmo tempo, pois caso
contrário, o usuário pode executar o fork bomb em vários logins.

Limitar o tamanho máximo de memória por programa também é interessante, para evitar o malloc bomb ou memory leaks, que requisitam memória em tempo de execução até não restar memória RAM disponível.

Abaixo um exemplo simples para evitar esses dois ataques de negação
de serviço (DoS). A configuração consiste em limitar o número máximo de
processos, o tamanho máximo de memória por processo e, para que as
configurações funcionem, limitar o número máximo de logins simultâneos por usuários.

*             hard   data          15000
brites hard data 5000
@operadores hard data 10000
@operadores hard nproc 10
* hard nproc 20
* - maxlogins 2
* - maxsyslogins 20

Os grupos são definidos pelo símbolo “@”, o “*” significa todos os
usuários. O símbolo “–“ indica que a regra é para ambos os limites, hard e soft. A ordem das linhas não influencia em nada o limite dos recursos, no texto agrupamos por recurso (terceira coluna).

A memória RAM é limitada nas três primeiras linhas. A
primeira linha limita o tamanho da memória de um processo em 15MB para
todos os usuários. A segunda linha é uma restrição específica para o
usuário brites em somente 5MB de memória por processo.

A terceira linha indica que o grupo operadores pode somente utilizar 10MB de memória para cada processo, é importante notar que mesmo que o usuário brites pertença ao grupo operadores, a maior restrição prevalece, ou seja, 5MB por processo.

Nas próximas duas linhas são definidos os limites para o nproc
número total de processos permitidos por sessão. Em nosso exemplo, 20
processos para todos os usuários, exceto para os usuários do grupo operadores, que só é permitido 10 processos por sessão.

As duas últimas linhas são de extrema importância para a proteção correta contra o fork e malloc bomb. A linha que define maxlogins
permite que cada usuário só consiga ter duas sessões ao mesmo tempo.

A
última linha define que em todo o sistema só pode ter 20 logins
simultâneos. Esta configuração não é necessária quando se tem poucos
usuários na máquina, mas define exatamente o máximo de recursos que
serão utilizados. No caso da nossa configuração: 300MB de memória RAM e 400 processos.

Conclusão

Com os limites configurados nos servidores corporativos, podemos evitar
vários inconvenientes como indisponibilidade por ações indevidas, mas
não intencionais por parte dos usuários.

Isso é mandatório quando temos
que cumprir SLAs(Service Level Agreements – contratos entre um
fornecedor de serviços de TI e seu cliente, em que o fornecedor deve
cumprir determinados níveis de serviços) altíssimos de disponibilidade.

Recursos

***

artigo publicado originalmente no developerWorks Brasil, por Heloisa Brites e Ricardo Piantola

Heloisa Brites é formada em publicidade pela Universidade Presbiteriana Mackenzie e
atualmente cursando Ciência da Computação na Universidade
Paulistana. Atua há um ano e meio na área de desenvolvimento de
software na IBM. Possui duas certificações em IBM DB2 9 e tem
interesse na área de desenvolvimento e teste de games. Ver perfil na Comunidade developerWorks

Ricardo Piantola é Engenheiro de Software do IBM Brazil Software
Development Laboratory, onde desenvolve produtos da linha IBM Smart
Analytics System. É formado em Ciência de Computação, pós-graduado
em Segurança da Informação pela Universidade Federal do Estado de
Rio de Janeio e mestre em engenharia de computação pela Escola
Politécnica da Universidade de São Paulo. Atualmente cursando o
programa de doutorado da Universidade de São Paulo. Possui 11 anos
de experiência em Outsoucing e desenvolvimento de software.
Ver perfil na Comunidade developerWorks