首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何连接到代理后面的MongoDB副本?

问如何连接到代理后面的MongoDB副本?
EN

Server Fault用户
提问于 2017-05-25 03:31:22
回答 2查看 19.5K关注 0票数 9

我在云服务上有一个MongoDB副本集。出于安全考虑,云的内部网络可以使用副本集。

我遵循云服务的指南,在代理服务器上为副本集的每个成员设置了一个代理:

代码语言:javascript
复制
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
...

我能够以独立模式从公共网络连接到副本集的每个成员:

代码语言:javascript
复制
mongoUri = new MongoClientURI("mongodb://usr:pwd@proxy-server-public-ip:27017/db")  ;
client = MongoClient(mongoUri);

但是,当我尝试以复制集模式连接它时:

代码语言:javascript
复制
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:我可以在代理服务器上以副本集模式连接到副本集。

如何连接到代理服务器后面的副本集?

更新:我在连接时使用代理服务器的公共地址。

EN

回答 2

Server Fault用户

发布于 2017-12-06 19:43:10

我最近也遇到过类似的情况。考虑一些DNS欺骗,客户端使用“真实”DNS名称,mongodb副本集成员使用相同的名称,但在/etc/主机中重写它们以指向自己。

如果您有3个成员,那么您的客户端将使用三个DNS名称路由到代理,例如:

member1.mynetwork.mydomain -> (代理地址) member2.mynetwork.mydomain -> (代理地址) member3.mynetwork.mydomain -> (代理地址)

然后,在mongodb副本集成员上,在每个匹配的框上创建/etc/ host条目,但指向它们自己的主机IP,例如:

/etc/东道国:

代码语言:javascript
复制
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等。

将客户端配置为连接到以下内容:

代码语言:javascript
复制
[member1.mynetwork.mydomain:27017,member2.mynetwork.mydomain:27017,member2.mynetwork.mydomain:27017]

副本集将根据其自己的副本集成员列表使用集群定义来响应驱动程序,该列表将是相同的名称:

代码语言:javascript
复制
hosts=[member1.mynetwork.mydomain:27017,member2.mynetwork.mydomain:27017, member3.mynetwork.mydomain:27017]

你应该做生意。

如果代理情况不能承载多个DNS名称,则可以将所有信任的端口(包括mongodb成员本身的本地绑定端口)更改为27017/27018/27019方案。

有些人,包括我自己,会认为本地/etc/host重写是不正常的和黑客行为,这取决于您的服务器/ VM /容器管理情况。

但是如果您处于困境中,我认为这是一个比重写mongodb响应更优雅的攻击。

票数 5
EN

Server Fault用户

发布于 2017-08-09 10:42:27

我在五月问过这个问题,但现在我在这里回答我自己的问题。

正如注释中所讨论的,MongoDB服务器将返回其成员列表及其配置的地址。由于人工创建的MongoDB副本集配置了私有地址,MongoDB服务器将提供成员的私有地址。

为了解决这个问题,我们需要一个专用的Mongo客户端连接代理。代理应该拦截MongoDB服务器对isMaster命令的响应,并将私有地址覆盖到服务器的公共地址或代理服务器的地址。当客户端接收到这些被截获的地址后,他们将能够以replicaSet模式连接这些地址。

下面是Node.js中的一些代码:

代码语言:javascript
复制
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;
  }
}

将客户端库更改为将私有地址映射到公共地址应该是另一种解决方案。但是,支持所有语言并将您的自定义库推到合作伙伴的开发机器上可能是一项艰巨的工作。

票数 4
EN
页面原文内容由Server Fault提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://serverfault.com/questions/859503

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档