Merge pull request #419 from mozilla/meteor_login

Added logic to allow the option of using meteor accounts for sign-in …
This commit is contained in:
Brandon Myers 2017-07-21 17:37:29 -05:00 коммит произвёл GitHub
Родитель 012a27da15 5b8490cbef
Коммит f31f048869
4 изменённых файлов: 276 добавлений и 119 удалений

Просмотреть файл

@ -192,16 +192,18 @@ Marvel plugin
`Marvel`_ is a monitoring plugin developed by Elasticsearch (the company).
WARNING: this plugin is NOT open source. At the time of writing, Marvel is free for development but you have to get a license for production.
WARNING: this plugin is NOT open source. At the time of writing, Marvel is free for 30 days.
After which you can apply for a free basic license to continue using it for it's key monitoring features.
To install Marvel, on each of your elasticsearch node, from the Elasticsearch home directory::
sudo bin/plugin -i elasticsearch/marvel/latest
sudo bin/plugin install license
sudo bin/plugin install marvel-agent
sudo service elasticsearch restart
You should now be able to access to Marvel at http://any-server-in-cluster:9200/_plugin/marvel
.. _Marvel: http://www.elasticsearch.org/overview/marvel/
.. _Marvel: https://www.elastic.co/guide/en/marvel/current/introduction.html
Web and Workers nodes
---------------------
@ -228,9 +230,9 @@ On APT-based systems::
Then::
su - mozdef
wget http://python.org/ftp/python/2.7.6/Python-2.7.6.tgz
tar xvzf Python-2.7.6.tgz
cd Python-2.7.6
wget https://www.python.org/ftp/python/2.7.11/Python-2.7.11.tgz
tar xvzf Python-2.7.11.tgz
cd Python-2.7.11
./configure --prefix=/opt/mozdef/python2.7 --enable-shared
make
make install
@ -247,7 +249,7 @@ Then::
source mozdef/bin/activate
pip install -r MozDef/requirements.txt
At this point when you launch python, It should tell you that you're using Python 2.7.6.
At this point when you launch python, It should tell you that you're using Python 2.7.11.
Whenever you launch a python script from now on, you should have your mozdef virtualenv actived and your LD_LIBRARY_PATH env variable should include /opt/mozdef/python2.7/lib/
@ -256,14 +258,21 @@ RabbitMQ
`RabbitMQ`_ is used on workers to have queues of events waiting to be inserted into the Elasticsearch cluster (storage).
RabbitMQ does provide a zero-dependency RPM that you can find for RedHat/CentOS here::
https://github.com/rabbitmq/erlang-rpm
For Debian/Ubuntu based distros you would need to install erlang separately.
To install it, first make sure you enabled `EPEL repos`_. Then you need to install an Erlang environment.
If you prefer to install all the dependencies on a Red Hat based system you can do the following::
On Yum-based systems::
sudo yum install erlang
You can then install the rabbitmq server::
sudo rpm --import http://www.rabbitmq.com/rabbitmq-signing-key-public.asc
sudo rpm --import https://www.rabbitmq.com/rabbitmq-signing-key-public.asc
sudo yum install rabbitmq-server
To start rabbitmq at startup::
@ -307,34 +316,52 @@ For meteor, in a terminal::
curl https://install.meteor.com/ | sh
wget http://nodejs.org/dist/v0.10.26/node-v0.10.26.tar.gz
tar xvzf node-v0.10.26.tar.gz
cd node-v0.10.26
wget https://nodejs.org/dist/v4.7.0/node-v4.7.0.tar.gz
tar xvzf node-v4.7.0.tar.gz
cd node-v4.7.0
./configure
make
sudo make install
Make sure you have meteorite/mrt (run as root/admin)::
npm install -g meteorite
Then from the meteor subdirectory of this git repository (/opt/mozdef/MozDef/meteor) run::
mrt add iron-router
mrt add accounts-persona
meteor add iron-router
You may want to edit the app/lib/settings.js file to properly point to your elastic search server::
If you wish to use meteor as the authentication handler you'll also need to install the Accounts-Password pkg::
meteor add accounts-password
elasticsearch={
address:"http://servername:9200/",
healthurl:"_cluster/health",
docstatsurl:"_stats/docs"
}
You may want to edit the app/lib/settings.js file to properly configure the URLs and Authentication
The default setting will use Meteor Accounts, but you can just as easily install an external provider like Github, Google, Facebook or your own OIDC::
mozdef = {
rootURL: "localhost",
port: "443",
rootAPI: "https://localhost:8444",
kibanaURL: "https://localhost:9443/app/kibana#",
enableBlockIP: true,
enableClientAccountCreation: true,
authenticationType: "meteor-password"
}
or for an OIDC implementation that passes a header to the nginx reverse proxy (for example using OpenResty with Lua and Auth0)::
mozdef = {
rootURL: "localhost",
port: "443",
rootAPI: "https://localhost:8444",
kibanaURL: "https://localhost:9443/app/kibana#",
enableBlockIP: true,
enableClientAccountCreation: false,
authenticationType: "OIDC"
}
Then start meteor with::
meteor
.. _meteor: https://guide.meteor.com/
.. _meteor-accounts: https://guide.meteor.com/accounts.html
Node
******
@ -344,9 +371,9 @@ Alternatively you can run the meteor UI in 'deployment' mode using a native node
First install node::
yum install bzip2 gcc gcc-c++ sqlite sqlite-devel
wget http://nodejs.org/dist/v0.10.25/node-v0.10.25.tar.gz
tar xvfz node-v0.10.25.tar.gz
cd node-v0.10.25
wget https://nodejs.org/dist/v4.7.0/node-v4.7.0.tar.gz
tar xvfz node-v4.7.0.tar.gz
cd node-v4.7.0
python configure
make
make install
@ -363,7 +390,7 @@ You can then deploy the meteor UI for mozdef as necessary::
This will create a 'bundle' directory with the entire UI code below that directory.
You will need to update the settings.js file to match your servername/port::
If you didn't update the settings.js before bundling the meteor installation, you will need to update the settings.js file to match your servername/port::
vim bundle/programs/server/app/app/lib/settings.js
@ -374,7 +401,8 @@ the fibers node module::
rm -rf fibers
sudo npm install fibers@1.0.1
Then run the mozdef UI via node::
There are systemd unit files available in the systemd directory of the public repo you can use to start meteor using node.
If you aren't using systemd, then run the mozdef UI via node manually::
export MONGO_URL=mongodb://mongoservername:3002/meteor
export ROOT_URL=http://meteorUIservername/
@ -400,11 +428,11 @@ On apt-get based system::
If you don't have this package in your repos, before installing create `/etc/yum.repos.d/nginx.repo` with the following content::
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/6/$basearch/
gpgcheck=0
enabled=1
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/OS/OSRELEASE/$basearch/
gpgcheck=0
enabled=1
.. _nginx: http://nginx.org/
@ -413,9 +441,9 @@ UWSGI
We use `uwsgi`_ to interface python and nginx::
wget http://projects.unbit.it/downloads/uwsgi-2.0.2.tar.gz
tar zxvf uwsgi-2.0.2.tar.gz
cd uwsgi-2.0.2
wget https://projects.unbit.it/downloads/uwsgi-2.0.12.tar.gz
tar zxvf uwsgi-2.0.12.tar.gz
cd uwsgi-2.0.12
~/python2.7/bin/python uwsgiconfig.py --build
~/python2.7/bin/python uwsgiconfig.py --plugin plugins/python core
cp python_plugin.so ~/envs/mozdef/bin/
@ -442,16 +470,16 @@ We use `uwsgi`_ to interface python and nginx::
sudo vim /etc/nginx/nginx.conf
sudo service nginx restart
.. _uwsgi: http://projects.unbit.it/uwsgi/
.. _uwsgi: https://uwsgi-docs.readthedocs.io/en/latest/
Kibana
******
`Kibana`_ is a webapp to visualize and search your Elasticsearch cluster data::
wget https://download.elasticsearch.org/kibana/kibana/kibana-3.0.0milestone5.tar.gz
tar xvzf kibana-3.0.0milestone5.tar.gz
mv kibana-3.0.0milestone5 kibana
wget https://download.elastic.co/kibana/kibana/kibana-4.6.2-linux-x86_64.tar.gz
tar xvzf kibana-4.6.2-linux-x86_64.tar.gz
ln -s kibana-4.6.2 kibana
# configure /etc/nginx/nginx.conf to target this folder
sudo service nginx reload
@ -460,11 +488,13 @@ To initialize elasticsearch indices and load some sample data::
cd examples/es-docs/
python inject.py
.. _Kibana: http://www.elasticsearch.org/overview/kibana
.. _Kibana: https://www.elastic.co/products/kibana
Start Services
**************
TO DO: Add in services like supervisord, and refer to systemd files.
Start the following services
cd ~/MozDef/mq
@ -541,21 +571,22 @@ Manual Installation
6. Installing Kibana ::
$ cd /tmp/
$ curl -L https://download.elasticsearch.org/kibana/kibana/kibana-3.1.0.tar.gz | tar -C /opt -xz
$ /bin/ln -s /opt/kibana-3.1.0 /opt/kibana
$ curl -L https://download.elastic.co/kibana/kibana/kibana-4.6.2-linux-x86_64.tar.gz | tar -C /opt -xz
$ /bin/ln -s /opt/kibana-4.6.2 /opt/kibana
$ cp $MOZDEF_PATH/examples/kibana/dashboards/alert.js /opt/kibana/app/dashboards/alert.js
$ cp $MOZDEF_PATH/examples/kibana/dashboards/event.js /opt/kibana/app/dashboards/event.js
7. Installing Elasticsearch ::
$ wget https://gist.githubusercontent.com/yashmehrotra/3209a7e2c696c2ac5110/raw/9161ffb32ee79d48f4bce224f8710ac8c7e85922/ElasticSearch.sh
# You can download any version of ELasticSearch
$ ./ElasticSearch.sh 1.6.0
For Red Hat based:
$ wget https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/rpm/elasticsearch/2.4.5/elasticsearch-2.4.5.rpm
For Debian based:
$ wget https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/2.4.5/elasticsearch-2.4.5.deb
# You can download and install any version of ELasticSearch > 2.x and < 5.x
8. Setting up Meteor ::
$ curl -L https://install.meteor.com/ | /bin/sh
$ npm install -g meteorite
$ cd $MOZDEF_PATH/meteor
$ meteor

Просмотреть файл

@ -9,6 +9,7 @@ Jeff Bryner jbryner@mozilla.com
Anthony Verez averez@mozilla.com
Yash Mehrotra yashmehrotra95@gmail.com
Avijit Gupta 526avijit@gmail.com
Alicia Smith asmith@mozilla.com
*/
if (Meteor.isClient) {
@ -25,10 +26,10 @@ if (Meteor.isClient) {
Session.set('attackerlimit','10');
getAllPlugins();
//assumes connection to an nginx/apache front end
//serving up an site handling authentication set via
//server side header
Meteor.loginViaHeader();
// Sends us to register our login handler
// and then to the login function of choice
// based on how enableClientAccountCreation was set at deployment.
Meteor.login();
//see if we have a myo armband
try{
@ -369,7 +370,27 @@ if (Meteor.isClient) {
}
});
//sample login function
// login abstraction
Meteor.login = function(callback) {
var authenticationType = mozdef.authenticationType;
switch(authenticationType){
case 'meteor-password':
Meteor.loginViaPassword(callback);
break;
case 'oidc':
Meteor.loginViaHeader(callback);
break;
default:
// non-breaking default of header
// todo: evaluate defaulting to meteor-password
Meteor.loginViaHeader(callback);
break;
}
}
//assumes connection to an nginx/apache front end
//serving up an site handling authentication set via
//server side header
Meteor.loginViaHeader = function(callback) {
//create a login request to pass to loginHandler
var loginRequest = {};
@ -380,4 +401,8 @@ if (Meteor.isClient) {
});
};
Meteor.loginViaPassword = function(callback) {
// noop - allow meteor to pass through
};
};

Просмотреть файл

@ -8,6 +8,7 @@ Contributors:
Jeff Bryner jbryner@mozilla.com
Anthony Verez averez@mozilla.com
Brandon Myers bmyers@mozilla.com
Alicia Smith asmith@mozilla.com
*/
//configuration settings
@ -18,6 +19,7 @@ mozdef = {
rootAPI: "https://localhost:8444",
kibanaURL: "https://localhost:9443/app/kibana#",
enableBlockIP: true,
enableClientAccountCreation: false
enableClientAccountCreation: true,
authenticationType: "meteor-password"
}

Просмотреть файл

@ -7,13 +7,14 @@ Copyright (c) 2014 Mozilla Corporation
Contributors:
Jeff Bryner jbryner@mozilla.com
Anthony Verez averez@mozilla.com
Alicia Smith asmith@mozilla.com
*/
if (Meteor.isServer) {
Meteor.startup(function () {
// Since we only connect to localhost or to ourselves, adding a hack to bypass cert validation
process.env.NODE_TLS_REJECT_UNAUTHORIZED="0";
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
console.log("MozDef starting")
//important to set this so persona can validate the source request
@ -27,82 +28,180 @@ if (Meteor.isServer) {
//Pull in settings.js entries into a collection
//so the client gets dynamic content
mozdefsettings.remove({});
mozdefsettings.insert({ key:'rootURL',
value : mozdef.rootURL + ':' + mozdef.port });
mozdefsettings.insert({ key:'rootAPI',
value : mozdef.rootAPI });
mozdefsettings.insert({ key:'kibanaURL',
value : mozdef.kibanaURL });
mozdefsettings.insert({ key:'enableBlockIP',
value : mozdef.enableBlockIP });
mozdefsettings.insert({
key: 'rootURL',
value: mozdef.rootURL + ':' + mozdef.port
});
mozdefsettings.insert({
key: 'rootAPI',
value: mozdef.rootAPI
});
mozdefsettings.insert({
key: 'kibanaURL',
value: mozdef.kibanaURL
});
mozdefsettings.insert({
key: 'enableBlockIP',
value: mozdef.enableBlockIP
});
//allow local account creation?
//http://docs.meteor.com/#/full/accounts_config
mozdefsettings.insert({ key:'enableClientAccountCreation',
value : mozdef.enableClientAccountCreation || false});
Accounts.registerLoginHandler("headerLogin",function(loginRequest) {
//there are multiple login handlers in meteor.
//a login request go through all these handlers to find it's login hander
//so in our login handler, we only consider login requests which are via a header
var self=this;
var sessionData = self.connection || (self._session ? self._session.sessionData : self._sessionData);
var session = Meteor.server.sessions[self.connection.id];
//ideally we would use a header unique to the installation like HTTP_OIDC_CLAIM_ID_TOKEN_EMAIL
//however sockJS whitelists only certain headers
// https://github.com/sockjs/sockjs-node/blob/8b03b3b1e7be14ee5746847f517029cb3ce30ca7/src/transport.coffee#L132
// choose one that is passed on and set it in your http server config:
var headerName='via';
console.log('connection headers',this.connection.httpHeaders);
console.log('target header:',this.connection.httpHeaders[headerName]);
//our authentication logic
//check for user email header
if(this.connection.httpHeaders[headerName] == undefined) {
console.log('refused login request due to missing http header')
return null;
}
console.log('handling login request',loginRequest);
//grab the email from the header
var userEmail = this.connection.httpHeaders[headerName];
//we create a user if needed, and get the userId
var userId = null;
var user = Meteor.users.findOne({profile:{email:userEmail}});
if(!user) {
console.log('creating user:',userEmail)
userId = Meteor.users.insert({
profile: { email: userEmail},
username: userEmail,
emails: [{address:userEmail , "verified": true}],
createdAt: new Date()
});
} else {
userId = user._id;
}
//generate login tokens
var stampedToken = Accounts._generateStampedLoginToken();
var hashStampedToken = Accounts._hashStampedToken(stampedToken);
//console.log(stampedToken,hashStampedToken);
//send loggedin user's user id
return {
userId: userId
}
var enableClientAccountCreation = !!(mozdef.enableClientAccountCreation || false);
Accounts._options.enableClientAccountCreation = enableClientAccountCreation;
mozdefsettings.insert({
key: 'enableClientAccountCreation',
value: enableClientAccountCreation
});
// newer meteor uses a key of forbidClientAccountCreation, so
// we negate the enableClientAccountCreation mozdef setting
Accounts._options.forbidClientAccountCreation = !enableClientAccountCreation;
mozdefsettings.insert({
key: 'forbidClientAccountCreation',
value: !!!enableClientAccountCreation
});
registerLoginMethod();
//update veris if missing:
console.log("checking the veris framework reference enumeration");
console.log('tags: ' + veris.find().count());
if (veris.find().count()==0) {
if (veris.find().count() == 0) {
console.log("updating the veris collection");
var verisFile = Assets.getText("veris.dotformat.txt");
var verisObject = verisFile.split("\n");
verisObject.forEach(function(verisItem) {
veris.insert({tag: verisItem});
verisObject.forEach(function (verisItem) {
veris.insert({ tag: verisItem });
});
}
}); //end startup
}); //end startup
} //end is server
function registerLoginMethod() {
// Setup a single login method to allow
var authenticationType = mozdef.authenticationType;
console.log("Authentication Type: ", authenticationType);
switch (authenticationType) {
case 'meteor-password':
registerLoginViaPassword();
break;
case 'oidc':
registerLoginViaHeader();
break;
default:
// non-breaking default of header
// todo: evaluate defaulting to meteor-password
registerLoginViaHeader();
break;
}
}
function registerLoginViaPassword() {
console.log("Setting up Password Authentication");
// We need to attach the users email address to the user.profile
Accounts.onCreateUser(function (options, user) {
if (user.emails.count <= 0) {
console.log("No user emails")
return user;
}
var email = user.emails[0].address;
if (typeof (email) === "undefined") {
console.log("User Email address not defined.")
return user;
}
if (typeof (user.profile) === "undefined") {
// user has no profile - this is the usual case
user.profile = {
"email": email
};
} else {
// overwrite any email that may be attached
// to the current user profile. this should
// not happen as we are currently creating
// the user.
user.profile['email'] = email;
}
// set any other profile information here.
return user
});
}
function registerLoginViaHeader() {
Accounts.registerLoginHandler("headerLogin", function (loginRequest) {
//there are multiple login handlers in meteor.
//a login request go through all these handlers to find it's login hander
//so in our login handler, we only consider login requests which are via a header
var self = this;
var sessionData = self.connection || (self._session ? self._session.sessionData : self._sessionData);
// TODO: Figure out where the bug is (probably sessionData as it is never used)
// There is either a bug here, assigning the session, or a bug when assigning sessionData. One of these can not ever happen.
var session = Meteor.server.sessions[self.connection.id];
//ideally we would use a header unique to the installation like HTTP_OIDC_CLAIM_ID_TOKEN_EMAIL
//however sockJS whitelists only certain headers
// https://github.com/sockjs/sockjs-node/blob/8b03b3b1e7be14ee5746847f517029cb3ce30ca7/src/transport.coffee#L132
// choose one that is passed on and set it in your http server config:
var headerName = 'via';
// Logging httpHeaders is possibly leaking sensitive data
console.log('connection headers', this.connection.httpHeaders);
//grab the email from the header
var userEmail = this.connection.httpHeaders[headerName];
//our authentication logic
//check for user email header
if (userEmail == undefined) {
console.log('refused login request due to missing http header')
// throw
return new {
error: handleError("SSO Login failure: email not found")
};
}
console.log('target header:', userEmail);
console.log('handling login request', loginRequest);
//we create a user if needed, and get the userId
var userId = null;
var user = Meteor.users.findOne({ profile: { email: userEmail } });
if (user) {
userId = user._id;
} else {
console.log('creating user:', userEmail)
userId = Meteor.users.insert({
profile: { email: userEmail },
username: userEmail,
emails: [{ address: userEmail, "verified": true }],
createdAt: new Date()
});
}
//generate login tokens
var stampedToken = Accounts._generateStampedLoginToken();
var hashStampedToken = Accounts._hashStampedToken(stampedToken);
//console.log(stampedToken,hashStampedToken);
//send loggedin user's user id
return {
userId: userId
}
});
}
function handleError(msg) {
const error = new Meteor.Error(
403,
Accounts._options.ambiguousErrorMessages
? "Login failure. Please check your login credentials."
: msg
);
return error;
}