web-dev-qa-db-ja.com

プロキシの背後にある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
...

standaloneモードのパブリックネットワークからレプリカセットのすべてのメンバーに接続できます。

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:プロキシサーバーのレプリカセットモードでレプリカセットに接続できました。

プロキシサーバーの背後にあるレプリカセットに接続するにはどうすればよいですか?


更新:接続中にプロキシサーバーのパブリックアドレスを使用します。

6
Albert Zhong

5月にこの質問をしましたが、今は自分の質問に答えるためにここにいます。

コメントで説明したように、MongoDBサーバーは、構成されたアドレスとともに、メンバーのリストを返します。管理されたMongoDBレプリカセットはプライベートアドレスで構成されているため、MongoDBサーバーはメンバーのプライベートアドレスを提供します。

この問題を解決するには、Mongoクライアント接続専用のプロキシが必要です。プロキシはisMasterコマンドに対するMongoDBサーバーの応答をインターセプトし、プライベートアドレスをサーバーのパブリックアドレスまたはプロキシサーバーのアドレスにオーバーライドする必要があります。クライアントはこれらの傍受したアドレスを受信した後、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;
  }
}

プライベートアドレスをパブリックアドレスにマップするようにクライアントライブラリを変更することは、別の解決策です。しかし、すべての言語をサポートし、カスタマイズしたライブラリをパートナーの開発マシンにプッシュするのは、大変な作業になる可能性があります。

4
Albert Zhong

私は最近同様の状況にありました。クライアントが「実際の」DNS名を使用し、mongodbレプリカセットのメンバーは同じ名前を使用しますが、/ etc/hostsでそれらを上書きして、DNSの不正行為を考慮してください。

メンバーが3人いる場合は、クライアントがプロキシにルーティングするために使用するDNS名が3つあります。例:

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

各メンバーの「ホスト」フィールドを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名をホストできない場合は、all configs(mongodbメンバー自体のローカルバインドポートを含む)のポートを27017/27018/27019スキームに変更できます。

私自身を含む一部の人は、サーバー/ VM /コンテナー管理の状況に応じて、ローカルの/ etc/hostsオーバーライドを厄介でハックであると見なします。

しかし、あなたが束縛されているなら、私はmongodb応答を書き直すよりもエレガントなハックだと思います。

2
Aaron Oas