this.conection.clientAddress returns server IP (not client IP)?


#1

Method is defined inside server scope in /server/methods/mymethod.js

Meteor.methods({
  myMethod: function() {
    console.log(this.connection.clientAddress);
  }
});

I call method from the client (file /client/myfile.js) with Meteor.call("myMethod"); and it always returns the same - server’s IP address (tried app from mobile phone connected to different ISP etc.).

Am I doing something wrong or this is normal or … ?


#2

BTW, Meteor version is 1.5


#3

And console.log(this.connection) looks like this:

I20170609-13:20:25.265(2)? { id: 'YFdpzhTj8TmbvAMP9',
I20170609-13:20:25.266(2)?   close: [Function],
I20170609-13:20:25.267(2)?   onClose: [Function],
I20170609-13:20:25.267(2)?   clientAddress: '93.86.68.77',
I20170609-13:20:25.268(2)?   httpHeaders: 
I20170609-13:20:25.268(2)?    { 'x-forwarded-for': '93.86.68.77',
I20170609-13:20:25.269(2)?      'x-forwarded-proto': 'ws',
I20170609-13:20:25.269(2)?      host: '93.86.68.77:3000',
I20170609-13:20:25.269(2)?      'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) CriOS/59.0.3071.84 Mobile/14F89 Safari/602.1' } }

(app executing on my machine, I accessesd app via my iPhone which is connected to different ISP… and clientAddress is the same as server address).


#4

Is there any proxy running, f.e. NGINX?


#5

No proxy (nginx). I simply started meteor on port 3000 and make NAT on my home router to redirect port 3000 to my machine’s local IP.


#6

And the same happens if I deploy app to the “real” server


#7

Can you try setting HTTP_FORWARDED_COUNT environment variable to 1 (or more) as described here and see if that helps?


#8

@serkandurusoy thanks for advice. I tried that but doesn’t help. Tried with different numbers 1, 2, 3… but still the same.


#9

By the way, when HTTP_FORWARDED_COUNT is set, then clientAddress is null, but I solved the problem by reading x-forwarder-for http header:

I20170610-11:04:43.613(2)? { id: 'gYkA35SuTeauWpHdh',
I20170610-11:04:43.614(2)?   close: [Function],
I20170610-11:04:43.614(2)?   onClose: [Function],
I20170610-11:04:43.614(2)?   clientAddress: null,
I20170610-11:04:43.615(2)?   httpHeaders: 
I20170610-11:04:43.618(2)?    { 'x-forwarded-for': '192.168.1.11',
I20170610-11:04:43.619(2)?      'x-forwarded-proto': 'ws',
I20170610-11:04:43.619(2)?      host: '192.168.1.2:3000',
I20170610-11:04:43.619(2)?      'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36',
I20170610-11:04:43.620(2)?      'accept-language': 'en-US,en;q=0.8,hr;q=0.6,bs;q=0.4' } }

Now, problem is solved but … is this normal behavior or is bug in Meteor?


#10

No… doesn’t work in production. When deployed to server then this.connection is undefined. :scream:


#11

@perak have you ever solved it am on same situation Thanks in advance


#12

Make sure you aren’t using an arrow function for the server method. Did you check below? I would ensure you’re running it on the server and the client is making the request, it’s not a nested method called by the server.

connection.httpHeaders['x-forwarded-for'].split(',')

This code is old/unused (don’t judge me!) but it worked fine in dev way back, if it can be of assistance.

import AuditBase from '/imports/AuditBase'
import rlog from '/imports/rlog'
import analytics from '/imports/server/analytics'

function getTenantIdFromUserId (userId) {
  if (typeof userId === 'string') {
    const userDoc = Meteor.users.findOne({_id: userId})
    if (userDoc && userDoc._id === userId && typeof userDoc.tenant === 'string') {
      return userDoc.tenant
    }
  }
};

function makeAppTenantUserObj (userId, tenantId, connectionId) {
  if (typeof userId === 'string' && typeof tenantId === 'string' && typeof connectionId === 'string') {
    return {
      _tenantId: tenantId,
      _userId: userId,
      _connectionId: connectionId
    }
  } else {
    return null
  }
};



function verifyAppTenantObj (appObj = {}, connectionId) {
  const result = typeof connectionId === 'string' && Match.test(appObj, {
    _userId: String,
    _tenantId: String,
    _connectionId: String
  })
  return result && appObj._connectionId === connectionId
}

Accounts.onLogin(function (user) {
  const appObj = makeAppTenantUserObj(user.user._id, user.user.tenant || getTenantIdFromUserId(user.user._id), user.connection.id)
  if (appObj && verifyAppTenantObj(appObj, user.connection.id)) {
    user.connection._app = appObj
  } else {
    user.connection._app = null
    console.log('err - appObj not valid, setting user tenant to null')
  }
  analytics.track({
    userId: user.user._id,
    event: 'App Login',
    properties: {
      allowed: user.allowed,
      methodName: user.methodName,
      connection: user.connection,
      tenantId: user.user.tenant
    }
  })
})

Meteor.onConnection(function (res) {
  try {
    return new AuditBase('connection', res)
    // base.insertEvent()
    // res.onClose(function() {
    //   console.log('res in onClose', res)
    // })
  } catch (e) {
    rlog(e, 'err in Meteor.onConnection Audit event')
  }
})

Meteor.methods({
  getUserTenant () {
    check(this.userId, String)
    check(this.connection.id, String)
    this.unblock()
    if (verifyAppTenantObj(this.connection._app, this.connection.id)) {
      return this.connection._app
    } else {
      throw new Meteor.Error('err - invalid tenant for user:', this.userId)
    }
  }
})

Meteor.methods({
  getCurrentDetails () {
    this.unblock()
    const sessions = []
    _.each(Meteor.server.sessions, doc => {
      doc._namedSubs && _.each(doc._namedSubs, sub => {
        if (sub._ready) {
          sessions.push({
            name: sub._name,
            userId: sub.userId,
            connectionId: sub.connection.id,
            subId: sub._subscriptionId,
            params: sub._params,
            documents: sub._documents
          })
        }
      })
    })
    return sessions
  }
})