Devolver um endereço de um script client-connect parece que devia definir o IP do cliente. Não o faz: o único canal do hook para o servidor é um ficheiro temporário cujo caminho chega como último argumento do script, e o endereço tem de ser escrito aí na sintaxe de ifconfig-push. Um script que imprime o endereço ou termina com zero sem escrever nada produz um cliente ligado com um endereço da pool, e o registo do servidor parece perfeitamente saudável.

A confusão é razoável, porque vários outros hooks respondem de facto através de códigos de saída ou variáveis de ambiente. Este tem o seu próprio contrato, e o contrato começa com um nome de ficheiro.

O hook escreve um ficheiro, não um valor de retorno

A declaração são duas linhas: script-security 2 para permitir a chamada, e depois client-connect /etc/openvpn/server/assign-address.sh. Quando um cliente se autentica, o servidor executa esse script com o caminho do ficheiro gerado acrescentado como último argumento, que é $1 apenas se a declaração não passar argumentos adicionais próprios. Tudo o que se aplica a esse cliente vai nesse ficheiro como diretivas ordinárias, uma por linha, exatamente como apareceriam numa configuração. Um invólucro que acrescenta os seus próprios parâmetros desloca a posição, e o sintoma clássico é um script a escrever num caminho que parece --verbose.

O estado de saída é a outra metade do contrato. Zero significa aceita e lê o ficheiro; qualquer outra coisa rejeita a ligação, e é assim que uma identidade não mapeada é recusada em vez de voltar silenciosamente à pool. Escreve a diretiva completa antes de devolver sucesso, para que um ficheiro a meio escrever nunca se torne a configuração de um cliente.

Duas diretivas merecem atenção porque interagem com tudo o resto. ifconfig-push fixa o endereço, enquanto uma route enviada diz ao cliente o que é alcançável através do túnel; devolve a primeira e esquece a segunda e obténs um cliente com endereço e sem sítio para onde ir. A ordem dentro do ficheiro não importa, mas a integralidade importa. Um hook que envia uma rota para um prefixo a que nenhum par consegue chegar é o mesmo erro visto do outro extremo.

As entradas chegam por variáveis de ambiente: common_name, trusted_ip, ifconfig_pool_remote_ip e o resto. Trata todas como cadeias de fora do servidor, nunca como fragmentos de shell.

O mapeamento de endereços é uma política, não aritmética

Um atalho tentador é analisar o nome do certificado e usar os seus dígitos como último octeto. Isso não é um alocador, e falha de formas comuns: dois nomes com o mesmo sufixo colidem, um nome que acaba em 300 não é um endereço, e um certificado renomeado por um administrador move um dispositivo em silêncio. Valida a pertença à sub-rede, o intervalo de anfitriões utilizável, os endereços reservados e a unicidade em cada chamada, e mantém o mapeamento numa tabela que uma pessoa possa ler.

Quem controla o nome controla o endereço, por isso decide o que common_name representa realmente nesta implementação. Com username-as-common-name ativado, a identidade é o início de sessão e não o sujeito do certificado. Nunca passes esse valor à avaliação da shell, e nunca deixes que o cliente influencie um caminho de ficheiro.

Um alocador suportado por um registo tem de sobreviver à concorrência. Reserva o endereço de forma atómica, trata uma escrita falhada sem admitir um cliente a meio configurar, e etiqueta cada alocação com a ligação que a possui, para que uma desconexão tardia de uma sessão antiga não possa libertar um endereço que uma mais recente já detém. Várias instâncias de servidor que partilham uma pool tornam esse requisito mais agudo, não opcional.

A topologia decide o que significa ifconfig-push

Com topology subnet em 10.77.0.0/24, ifconfig-push 10.77.0.20 255.255.255.0 é um endereço mais a sua máscara de rede. Em topology net30 o segundo argumento não é de todo uma máscara mas o endereço do par, por isso copiar a forma de sub-rede para um servidor net30 dá-te um cliente que se associa e não consegue passar tráfego. Muda a topologia deliberadamente, nunca para que um exemplo pareça coerente. Um servidor que já corre net30 tem clientes vivos no endereçamento antigo, por isso a mudança é uma migração com janela de manutenção, não uma edição de uma linha.

Os endereços estáticos têm de ficar fora da pool dinâmica e fora de qualquer outra atribuição estática, incluindo as distribuídas pelos ficheiros client-config-dir normais. Se dois mecanismos podem produzir o mesmo endereço para clientes diferentes, um deles acabará por o fazer.

Testa quatro casos antes de confiares nele

Executa as mesmas verificações contra uma identidade mapeada, uma não mapeada, uma religação e uma falha de registo simulada. openvpn --config client.ovpn --verb 4 mostra o endereço atribuído na resposta push, e no servidor grep -A2 PUSH_REPLY /var/log/openvpn/openvpn.log confirma o que foi realmente enviado. Para a identidade não mapeada o resultado esperado é uma ligação rejeitada, não um endereço da pool.

Depois inspeciona o diretório onde o daemon escreve esses ficheiros temporários. O script corre no contexto de serviço reduzido, por isso um caminho escrevível pelo root mas não pelo daemon transforma cada ligação numa rejeição com um erro de permissões enterrado várias linhas abaixo no registo.

Atribuir não é autorizar: um endereço fixado continua a não dar acesso a cada destino atrás do servidor. Mantém a política de encaminhamento e firewall na sua própria camada, e verifica-a contra o endereço que acabaste de fixar.

Artigos relacionados