Skip to content

Commit b4f3749

Browse files
committed
Correct 1.1 release with missing new files.
1 parent d93392e commit b4f3749

File tree

32 files changed

+6015
-1
lines changed

32 files changed

+6015
-1
lines changed

.gitmodules

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[submodule "src/uPNA"]
2+
path = src/uPNA
3+
url = https://github.com/pcrowley/PNA.git
4+
branch = uPNA

images/docker/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ RUN dpkg -i ona-service_UbuntuPrecise_amd64.deb
1010

1111
# Switch to the unprivileged user, set some local configuration, and start.
1212

13-
COPY images/docker/run.sh /opt/obsrvbl-ona/run.sh
13+
COPY run.sh /opt/obsrvbl-ona/run.sh
1414
RUN chmod +x /opt/obsrvbl-ona/run.sh
1515

1616
USER obsrvbl_ona

images/docker/run.sh

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
# This file will provide default values for the environment, unless explicitly
3+
# overrode by the runtime environment. It also copies in all OBSRVBL_*
4+
# environment variables.
5+
local_config=/opt/obsrvbl-ona/config.local
6+
7+
if [ -z $OBSRVBL_SERVICE_KEY ] ; then
8+
echo "Missing required OBSRVBL_SERVICE_KEY in environment"
9+
exit 1
10+
fi
11+
12+
echo "" > $local_config
13+
echo "OBSRVBL_MANAGE_MODE='${OBSRVBL_MANAGE_MODE:-manual}'" >> $local_config
14+
echo "OBSRVBL_NETWORKS='${OBSRVBL_NETWORKS:-10.0.0.0/8 172.16.0.0/12 192.168.0.0/16}'" >> $local_config
15+
echo "OBSRVBL_LOG_WATCHER='${OBSRVBL_LOG_WATCHER:-false}'" >> $local_config
16+
echo "OBSRVBL_SYSLOG_ENABLED='${OBSRVBL_SYSLOG_ENABLED:-true}'" >> $local_config
17+
echo "OBSRVBL_SYSLOG_FACILITY='${OBSRVBL_SYSLOG_FACILITY:-user}'" >> $local_config
18+
echo "OBSRVBL_SYSLOG_SERVER='${OBSRVBL_SYSLOG_SERVER:-127.0.0.1}'" >> $local_config
19+
echo "OBSRVBL_SYSLOG_SERVER_PORT='${OBSRVBL_SYSLOG_SERVER_PORT:-514}'" >> $local_config
20+
21+
for var in ${!OBSRVBL_*} ; do
22+
# Check if we've already written var to the config
23+
ret=$(grep $var $local_config)
24+
if [ ! -z $ret ] ; then
25+
continue
26+
fi
27+
28+
# write the var to the config
29+
echo "$var='${!var}'" >> $local_config
30+
done
31+
32+
exec /opt/obsrvbl-ona/system/supervisord/ona-supervisord.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/bin/sh
2+
3+
. /etc/rc.subr
4+
5+
name=obsrvbl-ona
6+
rcvar=obsrvbl_ona_enable
7+
command="/opt/obsrvbl-ona/system/supervisord/ona-supervisord.sh"
8+
daemon_pid="/tmp/obsrvbl-ona.pid"
9+
start_cmd="/usr/sbin/daemon -f -P ${daemon_pid} -u obsrvbl_ona -r ${command}"
10+
stop_cmd="kill `cat ${daemon_pid}` || true"
11+
status_cmd="test -e ${daemon_pid} && echo '${name} is running' || echo '${name} is not running'"
12+
13+
load_rc_config $name
14+
: ${obsrvbl_ona_enable="YES"}
15+
16+
run_rc_command "$1"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright 2015 Observable Networks
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from __future__ import division, print_function, unicode_literals
15+
16+
# python builtins
17+
from os import getenv
18+
from os.path import join
19+
from tempfile import gettempdir
20+
21+
# local
22+
from tcpdump_capturer import TcpdumpCapturer
23+
24+
25+
DATA_TYPE = 'iec61850'
26+
PROTO = ['0x88b8', '0x88b9', '0x88ba']
27+
BPF_FILTER = " or ".join(["ether proto {}".format(p) for p in PROTO])
28+
29+
30+
class Iec61850Capturer(TcpdumpCapturer):
31+
def __init__(self, *args, **kwargs):
32+
init_kwargs = {
33+
'bpf_filter': BPF_FILTER,
34+
'data_type': DATA_TYPE,
35+
'capture_iface': getenv('OBSRVBL_IEC61850_CAPTURE_IFACE', 'any'),
36+
'capture_seconds': int(
37+
getenv('OBSRVBL_IEC61850_CAPTURE_SECONDS', '600')
38+
),
39+
'pcap_dir': join(
40+
gettempdir(), getenv('OBSRVBL_IEC61850_PCAP_DIR',
41+
'obsrvbl_iec61850')
42+
),
43+
'poll_seconds': 600,
44+
'pps_limit': int(getenv('OBSRVBL_IEC61850_PPS_LIMIT', '100')),
45+
}
46+
kwargs.update(init_kwargs)
47+
super(Iec61850Capturer, self).__init__(*args, **kwargs)
48+
49+
50+
if __name__ == '__main__':
51+
capturer = Iec61850Capturer()
52+
capturer.run()

src/scripts/ona_service/nmapper.py

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Copyright 2015 Observable Networks
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# python builtins
16+
import json
17+
import logging
18+
from tempfile import NamedTemporaryFile
19+
from vendor.libnmap.process import NmapProcess
20+
from vendor.libnmap.parser import NmapParser, NmapParserException
21+
22+
# local
23+
from service import Service
24+
from utils import utc
25+
26+
OBSERVATION_TYPE = 'scan_observation_v1'
27+
SCAN_TARGETS = 'scan-config'
28+
SCAN_INTERVAL_SECONDS = 60 * 60 # Hourly.
29+
DEFAULT_NMAP_ARGS = ['nmap', '-oG', '-']
30+
MAX_SIMULTANEOUS_TARGETS = 10
31+
32+
33+
def _run_scan(ips, now):
34+
nmap = NmapProcess(ips)
35+
rc = nmap.run()
36+
if rc != 0:
37+
logging.error("nmap failed with error {}".format(rc))
38+
return
39+
40+
try:
41+
report = NmapParser.parse(nmap.stdout)
42+
except NmapParserException as e:
43+
logging.error("nmap parsing error? " + str(e))
44+
return
45+
46+
for host in report.hosts:
47+
ports = ['{}/{}'.format(s.port, s.state)
48+
for s in host.services]
49+
yield {
50+
'observation_type': OBSERVATION_TYPE,
51+
'time': now.isoformat(),
52+
'source': host.address,
53+
'ports': ', '.join(ports),
54+
'info_type': 'services',
55+
'result': '',
56+
}
57+
58+
59+
class NmapperService(Service):
60+
def __init__(self, *args, **kwargs):
61+
kwargs.update({
62+
'poll_seconds': SCAN_INTERVAL_SECONDS,
63+
})
64+
super(NmapperService, self).__init__(*args, **kwargs)
65+
66+
def _get_target_ips(self):
67+
json_resp = self.api.get_data(SCAN_TARGETS).json()
68+
objects = json_resp.get('objects', [])
69+
if not objects:
70+
return []
71+
72+
is_enabled = objects[0].get('is_enabled', True)
73+
if not is_enabled:
74+
return []
75+
76+
target_ips = objects[0].get('scan_targets', [])
77+
return target_ips
78+
79+
def _upload_results(self, filename, now):
80+
path = self.api.send_file('logs', filename, now, suffix='nmap')
81+
data = {
82+
'path': path,
83+
'log_type': 'observations',
84+
}
85+
self.api.send_signal('logs', data)
86+
87+
def execute(self, now=None):
88+
logging.info('getting target ips')
89+
target_ips = self._get_target_ips()
90+
logging.info('got {} target ips'.format(len(target_ips)))
91+
92+
# timezoneify now
93+
if now:
94+
now = now.replace(tzinfo=utc)
95+
96+
while target_ips:
97+
ips = target_ips[0:MAX_SIMULTANEOUS_TARGETS]
98+
target_ips = target_ips[MAX_SIMULTANEOUS_TARGETS:]
99+
100+
results = _run_scan(ips, now)
101+
with NamedTemporaryFile() as f:
102+
if not results:
103+
continue
104+
for r in results:
105+
f.write(json.dumps(r) + '\n')
106+
107+
f.seek(0)
108+
self._upload_results(f.name, now)
109+
110+
111+
if __name__ == '__main__':
112+
scanner = NmapperService()
113+
scanner.run()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
This code is licensed under Creative Common "Attribution" license (CC-BY: http://creativecommons.org/licenses/by/3.0/).
2+
3+
You are free to:
4+
- Share: to copy, distribute and transmit the work
5+
- Remix: to adapt the work
6+
- and make commercial use of the work
7+
8+
Under the following conditions:
9+
- Attribution: You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
10+
11+
With the understanding that:
12+
Waiver: Any of the above conditions can be waived if you get permission from the copyright holder.
13+
Public Domain: Where the work or any of its elements is in the public domain under applicable law, that status is in no way affected by the license.
14+
Other Rights: In no way are any of the following rights affected by the license:
15+
Your fair dealing or fair use rights, or other applicable copyright exceptions and limitations;
16+
The author's moral rights;
17+
Rights other persons may have either in the work itself or in how the work is used, such as publicity or privacy rights.
18+
19+
The full text is available here: http://creativecommons.org/licenses/by/3.0/legalcode
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# -*- coding: utf-8 -*-
2+
3+
__author__ = 'Ronald Bister, Mike Boutillier'
4+
__credits__ = ['Ronald Bister', 'Mike Boutillier']
5+
__maintainer__ = 'Ronald Bister'
6+
__email__ = '[email protected]'
7+
__license__ = 'CC-BY'
8+
__version__ = '0.6.1'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# -*- coding: utf-8 -*-
2+
3+
4+
class DictDiffer(object):
5+
"""
6+
Calculate the difference between two dictionaries as:
7+
(1) items added
8+
(2) items removed
9+
(3) keys same in both but changed values
10+
(4) keys same in both and unchanged values
11+
"""
12+
def __init__(self, current_dict, past_dict):
13+
self.current_dict = current_dict
14+
self.past_dict = past_dict
15+
self.set_current = set(current_dict.keys())
16+
self.set_past = set(past_dict.keys())
17+
self.intersect = self.set_current.intersection(self.set_past)
18+
19+
def added(self):
20+
return self.set_current - self.intersect
21+
22+
def removed(self):
23+
return self.set_past - self.intersect
24+
25+
def changed(self):
26+
return (set(o for o in self.intersect
27+
if self.past_dict[o] != self.current_dict[o]))
28+
29+
def unchanged(self):
30+
return (set(o for o in self.intersect
31+
if self.past_dict[o] == self.current_dict[o]))
32+
33+
34+
class NmapDiff(DictDiffer):
35+
"""
36+
NmapDiff compares two objects of same type to enable the user to check:
37+
38+
- what has changed
39+
- what has been added
40+
- what has been removed
41+
- what was kept unchanged
42+
43+
NmapDiff inherit from DictDiffer which makes the actual comparaison.
44+
The different methods from DictDiffer used by NmapDiff are the
45+
following:
46+
47+
- NmapDiff.changed()
48+
- NmapDiff.added()
49+
- NmapDiff.removed()
50+
- NmapDiff.unchanged()
51+
52+
Each of the returns a python set() of key which have changed in the
53+
compared objects. To check the different keys that could be returned,
54+
refer to the get_dict() method of the objects you which to
55+
compare (i.e: libnmap.objects.NmapHost, NmapService,...).
56+
"""
57+
def __init__(self, nmap_obj1, nmap_obj2):
58+
"""
59+
Constructor of NmapDiff:
60+
61+
- Checks if the two objects are of the same class
62+
- Checks if the objects are "comparable" via a call to id() (dirty)
63+
- Inherits from DictDiffer and
64+
"""
65+
if(nmap_obj1.__class__ != nmap_obj2.__class__ or
66+
nmap_obj1.id != nmap_obj2.id):
67+
raise NmapDiffException("Comparing objects with non-matching id")
68+
69+
self.object1 = nmap_obj1.get_dict()
70+
self.object2 = nmap_obj2.get_dict()
71+
72+
DictDiffer.__init__(self, self.object1, self.object2)
73+
74+
def __repr__(self):
75+
return ("added: [{0}] -- changed: [{1}] -- "
76+
"unchanged: [{2}] -- removed [{3}]".format(self.added(),
77+
self.changed(),
78+
self.unchanged(),
79+
self.removed()))
80+
81+
82+
class NmapDiffException(Exception):
83+
def __init__(self, msg):
84+
self.msg = msg
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from .report import NmapReport
4+
from .host import NmapHost
5+
from .service import NmapService
6+
7+
__all__ = ['NmapReport', 'NmapHost', 'NmapService']

0 commit comments

Comments
 (0)