A primeira sessão vence: restringir os logins de OpenVPN antes da expulsão
Rejeitar um login posterior não é o mesmo que substituir o antigo. A decisão tem de ocorrer antes da expulsão do mesmo nome, e a verificação da vaga tem de ser atómica.
Uma política que permite uma sessão por conta parece resolvida até chegar o segundo login. São possíveis dois desfechos: o recém-chegado substitui a sessão em curso, ou a sessão em curso mantém a vaga e o recém-chegado é recusado. Só o segundo é uma política em que a primeira sessão vence. A opção de cliente a que a maioria recorre ativa a concorrência e, por isso, não a produz.
Dois contratos escondem-se atrás de uma só definição
Com a concorrência desativada, uma segunda ligação que apresente o mesmo nome de certificado pode desalojar a sessão já em execução. Ativar a concorrência permite várias sessões ao mesmo tempo, o que é o contrário de um limite. Nenhum dos estados é uma política de recusa, pelo que a decisão tem de ser tomada noutro lugar.
Escreva o contrato antes de implementar seja o que for: quem é recusado, que prova identifica a sessão em curso e o que acontece quando uma sessão morre sem um corte limpo. Uma política que ninguém formulou assim não pode ser testada mais tarde.
A expulsão ocorre antes do seu hook de ligação
No percurso de código do servidor 2.6, a preparação inicial de um novo cliente remove as instâncias existentes com o mesmo nome quando a concorrência está desativada. Essa remoção acontece antes de a cadeia de gestores de ligação correr. Um script que deteta o duplicado e devolve falha recusa, portanto, o recém-chegado depois de a sessão em curso já ter sido expulsa, exatamente o resultado que a política pretendia evitar. A ordem das chamadas de retorno na implementação upstream é a única resposta fiável para a sua compilação.
| Stage | Who can still refuse | Effect on the incumbent |
|---|---|---|
| Certificate check | Server trust configuration | None |
| Authentication decision | The authentication controller | None, because establishment has not run |
| Connect handler | A profile script | Already evicted in the same-name case |
| Post-establishment | Status tooling, monitoring | Both sessions already exist or one is gone |
Consultar o ficheiro de estado também não é uma porta. Duas tentativas que cheguem juntas podem ambas ver uma vaga vazia antes de qualquer uma a confirmar, e ambas prosseguem.
Mova a decisão para a autenticação
A interface de gestão pode assumir a decisão de autenticação depois da verificação do certificado. Nessa disposição, o controlador não é um monitor: recebe cada instância de cliente que se liga e responde se essa instância pode prosseguir. Como a resposta chega antes do estabelecimento, uma recusa deixa a sessão em curso intacta. O mesmo canal comunica os cortes, e é assim que o controlador sabe que uma vaga foi libertada legitimamente.
Cada nova instância tem de passar por essa porta, incluindo reconexões e qualquer percurso que se reautentique sem pedido. Uma porta a que algumas instâncias nunca chegam não protege nada, e a omissão permanece invisível até alguém experimentar o atalho.
Os tokens podem passar ao lado da porta
Depois de um token ser emitido, o servidor pode validá-lo internamente e dispensar a autenticação externa para essa ligação. A chamada de gestão nunca acontece, pelo que uma verificação de vaga implementada apenas ali nunca vê a nova instância. Esse comportamento é uma comodidade para os utilizadores e um buraco numa política de concorrência. Faça com que as ligações autenticadas por token passem por uma verificação autoritativa equivalente, ou desative o percurso do token quando a política exigir uma decisão em cada ligação.
Não compense isso aceitando todos os tokens sem condições. Um token expirado ou inválido tem de falhar na mesma, e tem de falhar pelo mesmo ponto de decisão que tudo o resto.
Ligue a reserva a uma instância, não a um endereço
Um endereço é uma identidade fraca: uma reconexão do mesmo portátil pode chegar de outro endereço, e uma credencial copiada pode chegar do mesmo. Ligue a reserva à instância de cliente que o servidor atribui, para que uma reautenticação da instância já admitida seja reconhecida como proprietária. Um novo identificador de instância tem de adquirir a sua própria vaga através da decisão do primeiro a chegar, mesmo quando apresenta o mesmo token.
Liberte apenas a reserva pertencente à tentativa que falhou. Um evento de corte atrasado de uma tentativa mais antiga não pode limpar a vaga de um proprietário mais recente, e é por isso que a libertação nomeia a instância e não a conta. Após o reinício do controlador, reconcilie as sessões ativas antes de admitir alguém, e aplique a mesma regra em cada gateway que sirva a conta.
Decida em separado como é recuperada uma vaga realmente obsoleta. O administrador precisa de uma forma documentada de a limpar, e esse caminho não deve estar acessível a um utilizador que apenas clica em restabelecer.
Teste os pares que importam: dois logins quase simultâneos, uma reconexão após um encerramento limpo, uma reautenticação da sessão em curso, um recém-chegado recusado e um reinício do controlador. Em todos os casos a sessão em curso tem de permanecer utilizável e o recém-chegado tem de ser recusado sem danos colaterais para outras contas.