Назад | Перейти на главную страницу

Как я могу подключиться к набору реплик MongoDB через прокси?

У меня есть набор реплик MongoDB в облачной службе. По соображениям безопасности набор реплик доступен для внутренней сети облака.

Я последовал этому руководству по облачному сервису и настроил прокси для каждого члена набора реплик на прокси-сервере:

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
...

Я могу подключиться к каждому члену набора реплик из общедоступной сети в автономный Режим:

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

Но когда я пытаюсь подключить его режим набора реплик:

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);

У меня возникнет ошибка подключения, поскольку набор репликации сообщает драйверу, что нужно использовать внутренние адреса каждого члена (недоступные из общедоступной сети).

p.s: Я смог подключиться к набору реплик в режиме набора реплик на прокси-сервере.

Как я могу подключиться к моей реплике, установленной за прокси-сервером?


Обновление: при подключении я использую публичный адрес прокси-сервера.

Я задавал этот вопрос в мае, но теперь я здесь, чтобы ответить на свой вопрос.

Как обсуждалось в комментариях, сервер MongoDB вернет список своих членов с их настроенными адресами. Поскольку управляемый набор реплик MongoDB настроен с использованием частных адресов, сервер MongoDB будет предоставлять частные адреса участников.

Чтобы решить эту проблему, нам нужен выделенный прокси для клиентских подключений Mongo. Прокси-сервер должен перехватывать ответ сервера MongoDB на isMaster и замените частный адрес общедоступным адресом сервера или адресом вашего прокси-сервера. После того, как клиент получит этот перехваченный адрес, он сможет подключить этот адрес в режиме replicaSet.

Вот код в 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;
  }
}

Альтернативным решением должно быть изменение клиентской библиотеки для сопоставления частных адресов с общедоступными. Но, возможно, будет непросто поддерживать все языки и переносить вашу настроенную библиотеку на машину разработки вашего партнера.

У меня недавно была похожая ситуация. Рассмотрим некоторый обман DNS, когда клиент использует «настоящие» DNS-имена, а члены набора реплик mongodb используют те же имена, но переопределяют их в / etc / hosts, чтобы указать на себя.

Если у вас 3 члена, то имейте три DNS-имени, которые ваш клиент будет использовать для маршрутизации к прокси-серверу, например:

member1.mynetwork.mydomain -> (адрес прокси)
member2.mynetwork.mydomain -> (адрес прокси)
member3.mynetwork.mydomain -> (адрес прокси)

Затем в членах набора реплик mongodb создайте запись / etc / hosts в каждом поле, которое соответствует, но указывает на их собственный IP-адрес хоста, например:

/ etc / hosts:

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

Создайте конфигурацию набора реплик с полем "host" каждого члена как member1.mynetwork.mydomain:27017 и так далее.

Настройте клиента для подключения к чему-то вроде:

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

Набор реплик ответит драйверу определением кластера на основе его собственного списка членов набора реплик, которые будут иметь те же имена:

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

И ты должен заниматься бизнесом.

Если ваша ситуация с прокси-сервером не может размещать несколько имен DNS, вы можете изменить порты в все конфиги (включая локальные порты привязки на самих членах mongodb) к схеме 27017/27018/27019.

Некоторые, в том числе и я, сочтут локальные переопределения / etc / hosts непривлекательными и взломанными, в зависимости от ситуации с управлением вашим сервером / виртуальной машиной / контейнером.

Но если вы в затруднении, я думаю, что это более элегантный прием, чем переписывание ответов mongodb.