зеркало из https://github.com/mozilla/MozDef.git
Merge branch 'master' into add_docker_compose
This commit is contained in:
Коммит
58081ac415
|
@ -3,10 +3,11 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2014 Mozilla Corporation
|
||||
# Copyright (c) 2017 Mozilla Corporation
|
||||
#
|
||||
# Contributors:
|
||||
# Jeff Bryner jbryner@mozilla.com
|
||||
# Brandon Myers bmyers@mozilla.com
|
||||
|
||||
from lib.alerttask import AlertTask
|
||||
from query_models import SearchQuery, TermMatch, PhraseMatch, WildcardMatch
|
||||
|
@ -19,19 +20,18 @@ class AlertAccountCreations(AlertTask):
|
|||
search_query.add_must([
|
||||
TermMatch('_type', 'event'),
|
||||
TermMatch('tags', 'firefoxaccounts'),
|
||||
PhraseMatch('details.path', '/v1/account/create')
|
||||
PhraseMatch('details.action', 'accountCreate')
|
||||
])
|
||||
|
||||
# ignore test accounts and attempts to create accounts that already exist.
|
||||
search_query.add_must_not([
|
||||
WildcardMatch('details.email', '*restmail.net'),
|
||||
TermMatch('details.code', '429')
|
||||
])
|
||||
|
||||
self.filtersManual(search_query)
|
||||
|
||||
# Search aggregations on field 'sourceipv4address', keep X samples of events at most
|
||||
self.searchEventsAggregated('details.sourceipv4address', samplesLimit=10)
|
||||
# Search aggregations on field 'ip', keep X samples of events at most
|
||||
self.searchEventsAggregated('details.ip', samplesLimit=10)
|
||||
# alert when >= X matching events in an aggregation
|
||||
self.walkAggregations(threshold=10)
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[options]
|
||||
smtpserver = <add_smtpserver>
|
||||
sender = <add_sender_email>
|
||||
recipients = <add_recipient_email>,<add_recipient2_email>
|
|
@ -1,73 +0,0 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2017 Mozilla Corporation
|
||||
#
|
||||
# Contributors:
|
||||
# Alicia Smith <asmith@mozilla.com>
|
||||
# Michal Purzynski <mpurzynski@mozilla.com>
|
||||
# Brandon Myers <bmyers@mozilla.com>
|
||||
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from configlib import getConfig, OptionParser
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from email.Utils import formatdate
|
||||
from time import mktime
|
||||
|
||||
|
||||
class message(object):
|
||||
def __init__(self):
|
||||
'''
|
||||
takes an incoming alert
|
||||
and uses it to trigger an email sent to
|
||||
the releng signing server team
|
||||
'''
|
||||
|
||||
self.registration = ['access']
|
||||
self.priority = 2
|
||||
|
||||
# set my own conf file
|
||||
# relative path to the alerts alertWorker.py file
|
||||
self.configfile = './plugins/ssh_access_signreleng.conf'
|
||||
self.options = None
|
||||
if os.path.exists(self.configfile):
|
||||
sys.stdout.write('found conf file {0}\n'.format(self.configfile))
|
||||
self.initConfiguration()
|
||||
|
||||
def initConfiguration(self):
|
||||
myparser = OptionParser()
|
||||
# setup self.options by sending empty list [] to parse_args
|
||||
(self.options, args) = myparser.parse_args([])
|
||||
|
||||
# email settings
|
||||
self.options.smtpserver = getConfig('smtpserver', 'localhost', self.configfile)
|
||||
self.options.sender = getConfig('sender', 'donotreply@localhost.com', self.configfile)
|
||||
recipients_str = getConfig('recipients', 'noone@localhost.com', self.configfile)
|
||||
self.options.recipients = recipients_str.split(',')
|
||||
|
||||
def onMessage(self, message):
|
||||
# here is where you do something with the incoming alert message
|
||||
|
||||
emailMessage = MIMEText(message['summary'] + ' on ' + message['events'][0]['documentsource']['utctimestamp'])
|
||||
emailMessage['Subject'] = 'MozDef Alert: Releng Restricted Servers Successful SSH Access'
|
||||
emailMessage['From'] = self.options.sender
|
||||
emailMessage['To'] = ','.join(self.options.recipients)
|
||||
nowtuple = mktime(datetime.utcnow().timetuple())
|
||||
# The Date field needs to be in a specific format, and we must
|
||||
# define it or gmail struggles to parse it.
|
||||
emailMessage['Date'] = formatdate(nowtuple)
|
||||
smtpObj = smtplib.SMTP(self.options.smtpserver, 25)
|
||||
try:
|
||||
smtpObj.sendmail(self.options.sender, self.options.recipients, emailMessage.as_string())
|
||||
smtpObj.quit()
|
||||
except smtplib.SMTPException as e:
|
||||
sys.stderr.write('Error: failed to send email {0}\n'.format(e))
|
||||
|
||||
# you can modify the message if needed
|
||||
# plugins registered with lower (>2) priority
|
||||
# will receive the message and can also act on it
|
||||
# but even if not modified, you must return it
|
||||
return message
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# beaver-syslog
|
||||
|
||||
This configuration for [beaver](https://github.com/josegonzalez/beaver) ships syslog logs stored in `/var/log/syslog/systems/myhost.example.com/*.log` to mozdef.
|
||||
|
||||
To run it:
|
||||
|
||||
```
|
||||
beaver -c config.ini -t stdout
|
||||
```
|
Двоичные данные
examples/beaver-syslog/config.ini
Двоичные данные
examples/beaver-syslog/config.ini
Двоичный файл не отображается.
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -33,13 +33,14 @@ class message(object):
|
|||
'fxaoauthwebserver',
|
||||
'fxabrowseridwebserver',
|
||||
'fxaprofilewebserver',
|
||||
'fxa-auth-server'
|
||||
'fxa-auth-server',
|
||||
'fxa-customsmozsvc'
|
||||
]
|
||||
self.priority = 10
|
||||
|
||||
def onMessage(self, message, metadata):
|
||||
#drop non-relevant messages
|
||||
if message['eventsource'] in ('FxaContentWebserver', 'FxaAuthWebserver', 'FxaOauthWebserver', 'FxaAuth', 'fxa-auth-server'):
|
||||
if message['eventsource'] in ('Fxa-customsMozSvc', 'FxaContentWebserver', 'FxaAuthWebserver', 'FxaOauthWebserver', 'FxaAuth', 'fxa-auth-server'):
|
||||
if 'details' in message.keys():
|
||||
if 'status' in message['details']:
|
||||
if message['details']['status'] == 200:
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[options]
|
||||
region=<add_region>
|
||||
aws_access_key_id=<add_aws_access_key_id>
|
||||
aws_secret_access_key=<add_aws_secret_access_key>
|
||||
aws_queue_name=<add_aws_queue_name>
|
|
@ -1,173 +0,0 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
# Copyright (c) 2014 Mozilla Corporation
|
||||
#
|
||||
# Contributors:
|
||||
# Jeff Bryner jbryner@mozilla.com
|
||||
|
||||
import os
|
||||
import sys
|
||||
from configlib import getConfig, OptionParser
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
import netaddr
|
||||
import boto.sqs
|
||||
from boto.sqs.message import RawMessage
|
||||
|
||||
def isIPv4(ip):
|
||||
try:
|
||||
# netaddr on it's own considers 1 and 0 to be valid_ipv4
|
||||
# so a little sanity check prior to netaddr.
|
||||
# Use IPNetwork instead of valid_ipv4 to allow CIDR
|
||||
if '.' in ip and len(ip.split('.'))==4:
|
||||
# some ips are quoted
|
||||
netaddr.IPNetwork(ip.strip("'").strip('"'))
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def isIPv6(ip):
|
||||
try:
|
||||
return netaddr.valid_ipv6(ip)
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
class message(object):
|
||||
def __init__(self):
|
||||
'''register our criteria for being passed a message
|
||||
as a list of lower case strings to match with an rest endpoint
|
||||
(i.e. blockip matches /blockip)
|
||||
set the priority if you have a preference for order of plugins
|
||||
0 goes first, 100 is assumed/default if not sent
|
||||
|
||||
Plugins will register in Meteor with attributes:
|
||||
name: (as below)
|
||||
description: (as below)
|
||||
priority: (as below)
|
||||
file: "plugins.filename" where filename.py is the plugin code.
|
||||
|
||||
Plugin gets sent main rest options as:
|
||||
self.restoptions
|
||||
self.restoptions['configfile'] will be the .conf file
|
||||
used by the restapi's index.py file.
|
||||
|
||||
'''
|
||||
|
||||
self.registration = ['blockip']
|
||||
self.priority = 10
|
||||
self.name = "fxaCustomsServer"
|
||||
self.description = "Firefox Accounts SQS Queue"
|
||||
|
||||
# set my own conf file
|
||||
# relative path to the rest index.py file
|
||||
self.configfile = './plugins/fxaCustomsServer.conf'
|
||||
self.options = None
|
||||
if os.path.exists(self.configfile):
|
||||
sys.stdout.write('found conf file {0}\n'.format(self.configfile))
|
||||
self.initConfiguration()
|
||||
|
||||
|
||||
def initConfiguration(self):
|
||||
myparser = OptionParser()
|
||||
# setup self.options by sending empty list [] to parse_args
|
||||
(self.options, args) = myparser.parse_args([])
|
||||
|
||||
# fill self.options with plugin-specific options
|
||||
|
||||
# boto options
|
||||
self.options.region = getConfig('region',
|
||||
'us-west-2',
|
||||
self.configfile)
|
||||
self.options.aws_access_key_id=getConfig('aws_access_key_id',
|
||||
'',
|
||||
self.configfile)
|
||||
self.options.aws_secret_access_key=getConfig('aws_secret_access_key',
|
||||
'',
|
||||
self.configfile)
|
||||
self.options.aws_queue_name=getConfig('aws_queue_name',
|
||||
'',
|
||||
self.configfile)
|
||||
|
||||
|
||||
def sendToCustomsServer(self,
|
||||
ipaddress=None):
|
||||
try:
|
||||
if ipaddress is not None and self.options is not None:
|
||||
# connect and send a message like:
|
||||
# '{"Message": {"ban": {"ip": "192.168.0.2"}}}'
|
||||
# encoded like this:
|
||||
# {"Message":"{\"ban\":{\"ip\":\"192.168.0.2\"}}"}
|
||||
|
||||
conn = boto.sqs.connect_to_region(self.options.region,
|
||||
aws_access_key_id=self.options.aws_access_key_id,
|
||||
aws_secret_access_key=self.options.aws_secret_access_key)
|
||||
queue = conn.get_queue(self.options.aws_queue_name)
|
||||
|
||||
banMessage = dict(Message=json.dumps(dict(ban=dict(ip=ipaddress))))
|
||||
m = RawMessage()
|
||||
m.set_body(json.dumps(banMessage))
|
||||
queue.write(m)
|
||||
sys.stdout.write('Sent {0} to customs server\n'.format(ipaddress))
|
||||
|
||||
except Exception as e:
|
||||
sys.stderr.write('Error while sending to customs server %s: %r\n' % (ipaddress, e))
|
||||
|
||||
|
||||
def onMessage(self, request, response):
|
||||
'''
|
||||
request: http://bottlepy.org/docs/dev/api.html#the-request-object
|
||||
response: http://bottlepy.org/docs/dev/api.html#the-response-object
|
||||
|
||||
'''
|
||||
# format/validate request.json:
|
||||
ipaddress = None
|
||||
CIDR = None
|
||||
comment = None
|
||||
duration = None
|
||||
referenceID = None
|
||||
userid = None
|
||||
sendToCustomsServer = False
|
||||
|
||||
# loop through the fields of the form
|
||||
# and fill in our values
|
||||
try:
|
||||
for i in request.json:
|
||||
# were we checked?
|
||||
if self.name in i.keys():
|
||||
sendToCustomsServer = i.values()[0]
|
||||
if 'ipaddress' in i.keys():
|
||||
ipaddress = i.values()[0]
|
||||
if 'duration' in i.keys():
|
||||
duration = i.values()[0]
|
||||
if 'comment' in i.keys():
|
||||
comment = i.values()[0]
|
||||
if 'referenceID' in i.keys():
|
||||
referenceID = i.values()[0]
|
||||
if 'userid' in i.keys():
|
||||
userid = i.values()[0]
|
||||
|
||||
# are we configured?
|
||||
if self.options is None:
|
||||
sys.stderr.write("Customs server blockip requested but not configured\n")
|
||||
sendToCustomsServer = False
|
||||
|
||||
if sendToCustomsServer and ipaddress is not None:
|
||||
#figure out the CIDR mask
|
||||
if isIPv4(ipaddress) or isIPv6(ipaddress):
|
||||
ipcidr=netaddr.IPNetwork(ipaddress)
|
||||
if not ipcidr.ip.is_loopback() \
|
||||
and not ipcidr.ip.is_private() \
|
||||
and not ipcidr.ip.is_reserved():
|
||||
#split the ip vs cidr mask
|
||||
ipaddress, CIDR = str(ipcidr.cidr).split('/')
|
||||
self.sendToCustomsServer(ipaddress)
|
||||
sys.stdout.write ('Sent {0}/{1} to customs server\n'.format(ipaddress, CIDR))
|
||||
except Exception as e:
|
||||
sys.stderr.write('Error handling request.json %r \n'% (e))
|
||||
|
||||
return (request, response)
|
Загрузка…
Ссылка в новой задаче