Vamos supor que você tenha um servidor 24/7 rodando em uma máquina Linux, que lida com conexões de entrada, bem como TCP "simples" como TLS (via OpenSSL). Para garantir que o serviço funcione, os clientes são obrigados a sempre manter uma conexão com este serviço. Infelizmente, alguns desses clientes não se reconectam imediatamente quando o servidor fecha a conexão, então o servidor tenta o seu melhor para manter a conexão ativa para sempre.
Entretanto, se o servidor precisar ser reinicializado, por exemplo, devido a manutenção, as conexões serão perdidas.
Para evitar a desconexão, quero "mover" sessões TCP estabelecidas para outra máquina usando o mecanismo TCP_REPAIR ( https://lwn.net/Articles/495304/ ). Basicamente, isso significa salvar as informações do soquete TCP da máquina A (como os números Syn/Ack), recuperar as informações do soquete TCP na máquina B e garantir que novos pacotes IP sejam enviados para a nova máquina.
Isso funciona muito bem com TCP simples sem que os clientes percebam que a conexão TCP está sendo redirecionada para outra máquina. Mas ao usar TLS, isso obviamente requer mais trabalho.
Para simplificar, vamos supor que não haja mensagens TLS na rede, nenhuma leitura SSL e nenhuma gravação SSL estejam pendentes e todas as mensagens TLS anteriores tenham sido enviadas e recebidas completamente.
O que tentei até agora:
Abordagem 1: recrie silenciosamente um novo objeto SSL usando o mesmo SSL_SESSION
Tente criar um novo objeto SSL, torná-lo "estabelecido" e anexá-lo ao fd:
- Na nova máquina, crie um cliente (temporário) SSL_CTX, adicione o objeto SSL_SESSION da máquina antiga. Como esse objeto SSL_SESSION foi tirado do servidor SSL_CTX na máquina antiga, essa abordagem provavelmente é completamente inútil, mas eu tentei mesmo assim.
- Crie dois novos objetos SSL (um do client-ctx (apenas temporário), um do server-ctx), conecte-os via memória BIO, defina o SSL_SESSION para o objeto cliente SSL.
- Iniciar aperto de mão
- Após a conclusão do handshake, altere o bio do objeto SSL do servidor para um BIO_fd usando a conexão TCP reencarnada
- Destrua os BIOs de memória (não mais necessários) e o objeto Client-SSL.
Isso não funcionou de jeito nenhum. Eu sempre vejo um handshake completo, SSL_session_reused retorna 0 para ambos os objetos SSL. E mesmo se SSL_SESSION fosse reutilizado, eu ainda duvido que isso seria suficiente.
Abordagem 2: A abordagem memcpy
Esta é basicamente uma tentativa de criar algo como os métodos "i2d_SSL" e "d2i_SSL".
- Crie um objeto SSL simples a partir do SSL_CTX do novo servidor.
- Anexe o SSL_SESSION do objeto SSL do servidor antigo
- Usando os cabeçalhos internos do OpenSSL, converta o objeto SSL para uma estrutura SSL_CONNECTION
- copie alguns campos da estrutura SSL_CONNECTION:
- os segredos
- os campos aleatórios na subestrutura .s3
- os campos *_md na subestrutura .s3.tmp
- os estados na subestrutura .statem
- ... e mais alguns (mais ou menos tentativa e erro).
Suponho que a Abordagem 2 poderia funcionar, mas é muito difícil descobrir os campos realmente relevantes — se é que isso pode funcionar.
Alguém pode me esclarecer isso?
Essa pergunta já me foi feita de tempos em tempos ao longo dos anos.
A resposta é que o OpenSSL não suporta isso. Um objeto SSL contém muitos estados temporários específicos de conexão, então a abordagem 1 está fadada ao fracasso. Para a abordagem 2, há muitos subobjetos dependentes dentro do SSL, por exemplo, para rastrear estados de cifra e hash ocultos atrás do objeto SSL de nível superior. Você precisaria replicar todos eles. Todos esses objetos terão referências à instância libssl/libcrypto específica (como os objetos do provedor carregados). Infelizmente, isso também está fadado ao fracasso.
Este seria um recurso importante a ser adicionado ao OpenSSL, exigindo muito esforço para fazê-lo funcionar. Simplesmente não é possível sem grandes mudanças nas bibliotecas subjacentes.