Como posso me conectar a um conjunto de réplica do MongoDB por trás de um proxy?

4

Eu tenho um conjunto de réplica do MongoDB no serviço de nuvem. Por motivos de segurança, o conjunto de réplicas está disponível para a rede interna da nuvem.

Eu segui o guia do serviço em nuvem e configurei um proxy para cada membro do conjunto de réplicas, no servidor proxy:

0.0.0.0:27017 -> member1-private-ip:27107
0.0.0.0:27018 -> member2-private-ip:27107
0.0.0.0:27019 -> member3-private-ip:27017
...

Consigo me conectar a todos os membros do conjunto de réplicas de uma rede pública no modo autônomo :

mongoUri = new MongoClientURI("mongodb://usr:pwd@proxy-server-public-ip:27017/db")  ;
client = MongoClient(mongoUri);

Mas quando eu tento conectá-lo no modo de conjunto de réplicas :

mongoUri = new MongoClientURI("mongodb://usr:pwd@proxy-server-public-ip:27017,proxy-server-public-ip:27018,proxy-server-public-ip:27019/db?replicaSet=replcaSetName");
client = MongoClient(mongoUri);

Eu falharia com o erro de conexão, já que o conjunto replicado informa ao driver para usar os endereços internos de cada membro (inacessíveis da rede pública).

p.s: Eu poderia conectar-me ao conjunto de réplicas no modo de conjunto de réplicas no servidor proxy.

Como posso me conectar ao meu conjunto de réplicas por trás de um servidor proxy?

Atualização: Eu uso o endereço público do servidor proxy durante a conexão.

    
por Albert Zhong 25.05.2017 / 05:31

2 respostas

3

Eu fiz esta pergunta em maio, mas agora estou aqui para responder minha própria pergunta.

Como discutido nos comentários, o servidor MongoDB retornará uma lista de seus membros, com seus endereços configurados. Como um conjunto de réplica Manged MongoDB é configurado com endereços privados, o servidor MongoDB fornecerá endereços particulares do membro.

Para resolver esse problema, precisamos de um proxy dedicado para conexões de cliente do Mongo. O proxy deve interceptar a resposta do servidor MongoDB ao comando isMaster e substituir o endereço privado pelo endereço público do servidor ou pelo endereço do seu servidor proxy. Depois que o cliente receber esses endereços interceptados, eles poderão conectar esses endereços no modo replicaSet.

Aqui está um código no Node.js:

clientConn.on("data", dataHandler(proxyConn, function(data) {
  const msg = new WireMessage(data);
  if (msg.isCommand("isMaster")) {
    remoteClient.recordForInterception(msg);
  }
  mongoConn.write(data);
}));

mongoConn.on("data", dataHandler(proxyConn, function(data) {
  const msg = new WireMessage(data, {skipBody: true});
  var changed = false;
  if (remoteClient.shouldInterceptReply(msg)) {
    // To Intercept message, we need a full parse
    msg.parseBody();
    changed = remoteClient.interceptReply(clientConn, msg);
  }
  if (changed) {
    // Serialize intercepted data
    data = msg.serialize();
  }
  clientConn.write(data);
}));

interceptReply = function (conn, replyMessage) {
  if ('isMaster'.toLowerCase() !== replyMessage.toLowerCase()) {
    return false;
  }
  var doc;
  if (replyMessage.body.metadata) {
    doc = replyMessage.body.metadata;
  } else if (replyMessage.body.documents instanceof Array && 
    replyMessage.body.documents.length > 0) {
    doc = replyMessage.body.documents[0];
  } else {
    this.logger.warn("No document to handle: %s.", replyMessage.toString());
    return false;
  }    
  return interceptHosts(doc, conn,  hostMappings);
};

interceptHosts = function (doc, conn, mappings) {
  if (doc.hosts) {
    var hosts = doc.hosts;
    for (var i = 0; i < hosts.length; ++i) {
      var host = hosts[i];
      hosts[i] = getReverseAddress(host, conn, mappings);
    }
  }
  if (doc.primary) {
    doc.primary = getReverseAddress(doc.primary, conn, mappings);
  }
  if (doc.me) {
    doc.me = getReverseAddress(doc.me, conn, mappings);
  }
  return doc;
};

function getReverseAddress(endpoint, conn, reverseAddressMapping) {
  var hostMap = reverseAddressMapping[endpoint];
  if (hostMap && hostMap.host === "0.0.0.0") {
    // If we are listening on ANY address, use 
    //   the effective address the client connect us.
    return conn.localAddress + ":" + hostMap.port;
  } else if (hostMap) {
    return hostMap.host + ":" + hostMap.port;
  } else {
      return endpoint;
  }
}

A alteração da biblioteca do cliente para mapear endereços privados para o endereço público deve ser uma solução alternativa. Mas pode ser um trabalho árduo suportar todos os idiomas e enviar sua biblioteca personalizada para a máquina de desenvolvimento do seu parceiro.

    
por 09.08.2017 / 12:42
2

Eu tive recentemente uma situação semelhante. Considere algumas fraudes de DNS, em que o cliente usa nomes DNS "reais" e os membros do conjunto de réplicas mongodb usam os mesmos nomes, mas os substituem em / etc / hosts para apontar para si mesmos.

Se você tem 3 membros, então, tem três nomes DNS que são o que seu cliente usará para rotear para o proxy, por exemplo:

member1.mynetwork.mydomain -> (proxy address)
member2.mynetwork.mydomain -> (proxy address)
member3.mynetwork.mydomain -> (proxy address)

Em seguida, nos membros do conjunto de réplicas do mongodb, crie uma entrada / etc / hosts em cada caixa que corresponda, mas aponte para o próprio IP do host, por exemplo:

/ etc / hosts:

10.1.1.11 member1.mynetwork.mydomain
10.1.1.22 member2.mynetwork.mydomain
10.1.1.33 member3.mynetwork.mydomain

Compile a configuração do conjunto de réplicas com o campo "host" de cada membro como member1.mynetwork.mydomain:27017 e assim por diante.

Configure o cliente para se conectar a algo como:

[member1.mynetwork.mydomain:27017,member2.mynetwork.mydomain:27017,member2.mynetwork.mydomain:27017]

O conjunto de réplicas responderá ao driver com uma definição de cluster baseada em sua própria lista de membros do conjunto de réplicas, que terão os mesmos nomes:

hosts=[member1.mynetwork.mydomain:27017,member2.mynetwork.mydomain:27017, member3.mynetwork.mydomain:27017]

E você deveria estar no negócio.

Se a sua situação de proxy não puder hospedar vários nomes DNS, você poderá alterar as portas em todas configurações (incluindo portas de ligação locais nos próprios membros mongodb) para o esquema 27017/27018/27019.

Alguns, inclusive eu, considerarão que as substituições / etc / hosts locais são icky e hack, dependendo da situação de gerenciamento do servidor / VM / contêiner.

Mas se você está envolvido, acho que é um truque mais elegante do que reescrever as respostas do mongodb.

    
por 06.12.2017 / 20:43

Tags