У меня есть набор реплик 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.