Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • Pavel.Valach/warden
1 result
Show changes
Showing
with 742 additions and 97 deletions
/*
*
* -*- coding: utf-8 -*-
*
* warden-map.css
*
* Copyright (C) 2016 Cesnet z.s.p.o
* Use of this source is governed by a 3-clause BSD-style license, see LICENSE file.
*
*/
body {
font-family: 'Oswald', sans-serif;
background: #00253D;
border: 0px;
padding: 0px;
margin: 0px;
}
h2 {
color: #0062a2;
}
.hoverinfo {
font-family: 'Oswald', sans-serif;
}
#country {
color: #0062a2; /* Cesnet blue */
font-weight: bold;
}
table {
text-align: left;
margin: 0;
padding: 0;
font-size: 12px;
}
table th {
color: #0062a2; /* Cesnet blue */
padding: 0;
}
table td {
color: #4b4d4a; /* Greenish gray */
padding: 0;
}
#container {
overflow: hidden;
/* border: 2px solid #0062a2;
border: 0px;
padding: 0px;
margin: 0px;
border-radius: 5px;*/
position: relative;
/* width: 1280px;
height: 720px;*/
max-width: 100%;
max-height: 100%
width: 100%;
height: 100vh;*/
}
.zoom-button {
width: 40px;
height: 40px;
border-radius: 5px;
border: none;
background: #dcdcda;
font-size: 23px;
font-weight: bold;
color: white;
cursor: pointer;
}
.zoom-button:hover {
background-color: #0062a2;
}
#zoom-info {
display: inline-block;
padding: 10px;
color: #0062a2;
}
#warden-logo {
position: absolute;
top: 30px;
left: 30px;
background: white;
padding: 10px;
border-radius: 10px;
width: 240px;
height: 92px;
text-align: center;
}
#cesnet-logo {
position: absolute;
top: 30px;
right: 30px;
background: white;
padding: 10px;
border-radius: 10px;
width: 240px;
height: 92px;
text-align: center;
}
#legend-box {
position: absolute;
bottom: 30px;
left: 30px;
background-color: rgba(0,0,0,0.3);
color: white;
padding: 10px;
border-radius: 10px;
/*width: 240px;
height: 92px;
text-align: center;*/
}
#heading {
position: absolute;
top: 30px;
left: 50%;
width: 40em;
height: 92px;
margin-left: -20em;
font-size: xx-large;
color: white;
text-align: center;
vertical-align: middle;
line-height: 92px;
}
/*
*
* -*- coding: utf-8 -*-
*
* warden-map.js
*
* Copyright (C) 2016 Cesnet z.s.p.o
* Use of this source is governed by a 3-clause BSD-style license, see LICENSE file.
*
*/
// NOTE: Change path in a function d3.json() if you separate backend and frontend!
// Zooming functionality is based on WunderBart's implementation
// Please see following links:
// https://github.com/wunderbart
// https://jsfiddle.net/wunderbart/Lom3b0gb/
function Zoom(args) {
$.extend(this, {
$buttons: $(".zoom-button"),
$info: $("#zoom-info"),
scale: { max: 50, currentShift: 0 },
$container: args.$container,
datamap: args.datamap
});
this.init();
}
Zoom.prototype.init = function() {
var paths = this.datamap.svg.selectAll("path"),
subunits = this.datamap.svg.selectAll(".datamaps-subunit");
// preserve stroke thickness
paths.style("vector-effect", "non-scaling-stroke");
// disable click on drag end
subunits.call(
d3.behavior.drag().on("dragend", function() {
d3.event.sourceEvent.stopPropagation();
})
);
this.scale.set = this._getScalesArray();
this.d3Zoom = d3.behavior.zoom().scaleExtent([ 1, this.scale.max ]);
this._displayPercentage(1);
this.listen();
};
Zoom.prototype.listen = function() {
this.$buttons.off("click").on("click", this._handleClick.bind(this));
this.datamap.svg
.call(this.d3Zoom.on("zoom", this._handleScroll.bind(this)))
.on("dblclick.zoom", null); // disable zoom on double-click
};
Zoom.prototype.reset = function() {
this._shift("reset");
};
Zoom.prototype._handleScroll = function() {
var translate = d3.event.translate,
scale = d3.event.scale,
limited = this._bound(translate, scale);
this.scrolled = true;
this._update(limited.translate, limited.scale);
};
Zoom.prototype._handleClick = function(event) {
var direction = $(event.target).data("zoom");
this._shift(direction);
};
Zoom.prototype._shift = function(direction) {
var center = [ this.$container.width() / 2, this.$container.height() / 2 ],
translate = this.d3Zoom.translate(), translate0 = [], l = [],
view = {
x: translate[0],
y: translate[1],
k: this.d3Zoom.scale()
}, bounded;
translate0 = [
(center[0] - view.x) / view.k,
(center[1] - view.y) / view.k
];
if (direction == "reset") {
view.k = 1;
this.scrolled = true;
} else {
view.k = this._getNextScale(direction);
}
l = [ translate0[0] * view.k + view.x, translate0[1] * view.k + view.y ];
view.x += center[0] - l[0];
view.y += center[1] - l[1];
bounded = this._bound([ view.x, view.y ], view.k);
this._animate(bounded.translate, bounded.scale);
};
Zoom.prototype._bound = function(translate, scale) {
var width = this.$container.width(),
height = this.$container.height();
translate[0] = Math.min(
(width / height) * (scale - 1),
Math.max( width * (1 - scale), translate[0] )
);
translate[1] = Math.min(0, Math.max(height * (1 - scale), translate[1]));
return { translate: translate, scale: scale };
};
Zoom.prototype._update = function(translate, scale) {
this.d3Zoom
.translate(translate)
.scale(scale);
this.datamap.svg.selectAll("g")
.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
this._displayPercentage(scale);
};
Zoom.prototype._animate = function(translate, scale) {
var _this = this,
d3Zoom = this.d3Zoom;
d3.transition().duration(350).tween("zoom", function() {
var iTranslate = d3.interpolate(d3Zoom.translate(), translate),
iScale = d3.interpolate(d3Zoom.scale(), scale);
return function(t) {
_this._update(iTranslate(t), iScale(t));
};
});
};
Zoom.prototype._displayPercentage = function(scale) {
var value;
value = Math.round(Math.log(scale) / Math.log(this.scale.max) * 100);
this.$info.text(value + "%");
};
Zoom.prototype._getScalesArray = function() {
var array = [],
scaleMaxLog = Math.log(this.scale.max);
for (var i = 0; i <= 10; i++) {
array.push(Math.pow(Math.E, 0.1 * i * scaleMaxLog));
}
return array;
};
Zoom.prototype._getNextScale = function(direction) {
var scaleSet = this.scale.set,
currentScale = this.d3Zoom.scale(),
lastShift = scaleSet.length - 1,
shift, temp = [];
if (this.scrolled) {
for (shift = 0; shift <= lastShift; shift++) {
temp.push(Math.abs(scaleSet[shift] - currentScale));
}
shift = temp.indexOf(Math.min.apply(null, temp));
if (currentScale >= scaleSet[shift] && shift < lastShift) {
shift++;
}
if (direction == "out" && shift > 0) {
shift--;
}
this.scrolled = false;
} else {
shift = this.scale.currentShift;
if (direction == "out") {
shift > 0 && shift--;
} else {
shift < lastShift && shift++;
}
}
this.scale.currentShift = shift;
return scaleSet[shift];
};
function defaults(obj) {
Array.prototype.slice.call(arguments, 1).forEach(function(source) {
if (source) {
for (var prop in source) {
// Deep copy if property not set
if (obj[prop] == null) {
if (typeof source[prop] == 'function') {
obj[prop] = source[prop];
}
else {
obj[prop] = JSON.parse(JSON.stringify(source[prop]));
}
}
}
}
});
return obj;
}
function val( datumValue, optionsValue, context ) {
if ( typeof context === 'undefined' ) {
context = optionsValue;
optionsValues = undefined;
}
var value = typeof datumValue !== 'undefined' ? datumValue : optionsValue;
if (typeof value === 'undefined') {
return null;
}
if ( typeof value === 'function' ) {
var fnContext = [context];
if ( context.geography ) {
fnContext = [context.geography, context.data];
}
return value.apply(null, fnContext);
}
else {
return value;
}
}
var cat_color = {
"Abusive": "MediumPurple",
"Malware": "Red",
"Recon": "LightSlateGray",
"Attempt": "GhostWhite",
"Intrusion": "DarkTurquoise",
"Availability": "HotPink",
"Information": "PaleTurquoise",
"Fraud": "Yellow",
"Vulnerable": "DarkGoldenRod",
"Anomaly": "Brown",
"Other": "Green"
}
var cat_desc = {
"Abusive": "spam",
"Malware": "virus, worm, trojan, malware",
"Recon": "scanning, sniffing",
"Attempt": "bruteforce, exploitation attempt",
"Intrusion": "botnet, successful exploit",
"Availability": "(D)DOS",
"Information": "wiretapping, spoofing, hijacking",
"Fraud": "phishing, scam",
"Vulnerable": "open for abuse",
"Anomaly": "unusual traffic",
"Other": "unknown/unidentified"
}
function handleArcs (layer, data, options) {
var self = this,
svg = this.svg;
if ( !data || (data && !data.slice) ) {
throw "Datamaps Error - arcs must be an array";
}
// For some reason arc options were put in an `options` object instead of the parent arc
// I don't like this, so to match bubbles and other plugins I'm moving it
// This is to keep backwards compatability
for ( var i = 0; i < data.length; i++ ) {
data[i] = defaults(data[i], data[i].options);
delete data[i].options;
}
if ( typeof options === "undefined" ) {
options = defaultOptions.arcConfig;
}
var arcs = layer.selectAll('path.datamaps-arc').data( data, JSON.stringify );
var path = d3.geo.path()
.projection(self.projection);
arcs
.enter()
.append('svg:path')
.attr('class', 'datamaps-arc')
.style('stroke-linecap', 'round')
.style('stroke', function(datum) {
/* return val(datum.strokeColor, options.strokeColor, datum);*/
for (cat in cat_color) {
if (datum.event.startsWith(cat)) {
return cat_color[cat];
}
}
return "Green";
})
.style('fill', 'none')
.style('stroke-width', function(datum) {
return val(datum.strokeWidth, options.strokeWidth, datum);
})
.attr('d', function(datum) {
var originXY, destXY;
originXY = self.latLngToXY(val(datum.origin.latitude, datum), val(datum.origin.longitude, datum))
destXY = self.latLngToXY(val(datum.destination.latitude, datum), val(datum.destination.longitude, datum));
var midXY = [ (originXY[0] + destXY[0]) / 2, (originXY[1] + destXY[1]) / 2];
if (options.greatArc) {
// TODO: Move this to inside `if` clause when setting attr `d`
var greatArc = d3.geo.greatArc()
.source(function(d) { return [val(d.origin.longitude, d), val(d.origin.latitude, d)]; })
.target(function(d) { return [val(d.destination.longitude, d), val(d.destination.latitude, d)]; });
return path(greatArc(datum))
}
var sharpness = val(datum.arcSharpness, options.arcSharpness, datum);
return "M" + originXY[0] + ',' + originXY[1] + "S" + (midXY[0] + (50 * sharpness)) + "," + (midXY[1] - (75 * sharpness)) + "," + destXY[0] + "," + destXY[1];
})
.attr('data-info', function(datum) {
return JSON.stringify(datum);
})
.on('mouseover', function ( datum ) {
var $this = d3.select(this);
if (options.popupOnHover) {
self.updatePopup($this, datum, options, svg);
}
})
.on('mouseout', function ( datum ) {
var $this = d3.select(this);
d3.selectAll('.datamaps-hoverover').style('display', 'none');
})
.transition()
.style('fill', function(datum, i) {
/*
Thank you Jake Archibald, this is awesome.
Source: http://jakearchibald.com/2013/animated-line-drawing-svg/
*/
var length = this.getTotalLength();
this.style.transition = this.style.WebkitTransition = 'none';
this.style.strokeDasharray = length + ' ' + length;
this.style.strokeDashoffset = length;
this.getBoundingClientRect();
this.style.transition = this.style.WebkitTransition = 'stroke-dashoffset ' + val(datum.animationSpeed, options.animationSpeed, datum) + 'ms ' + datum.delay*1000 + 'ms ease-out';
this.style.strokeDashoffset = '0';
return 'none';
});
arcs.exit()
.transition()
.duration(1000)
.style('opacity', 0)
.remove();
}
var main_data = [];
var prev_data = 0;
// Configuration of datamap canvas
// Futher reading can be found at https://datamaps.github.io/
function Datamap() {
this.$container = $("#container");
instance = this.instance = new Datamaps({
scope: 'world',
element: this.$container.get(0),
done: this._handleMapReady.bind(this),
projection: 'mercator',
fills: {
/*defaultFill: '#454545'*/
defaultFill: 'black'
},
geographyConfig: {
hideAntarctica: true,
borderColor: '#0062a2',
highlightFillColor: '#4b4d4a',
highlightBorderColor: '#fdfdfd',
popupOnHover: true,
popupTemplate: function(geography, data) {
return '<div class="hoverinfo" id="country">' + geography.properties.name + '</div>';
},
},
ph_arcConfig: {
strokeColor: '#0062a2',
strokeWidth: 2,
arcSharpness: 2, /* 5 */
animationSpeed: 3000, // Milliseconds
popupOnHover: true,
// Case with latitude and longitude
popupTemplate: function(geography, data) {
if ( ( data.origin && data.destination ) && data.origin.latitude && data.origin.longitude && data.destination.latitude && data.destination.longitude ) {
// Content of info table
str = '<div class="hoverinfo"><table id="event"><tr><th>Warden Event</th></tr><tr><td>Type</td><td>'+ JSON.stringify(data.event) +'</td></tr><tr><td>Detect Time</td><td>'+ JSON.stringify(data.time) +'</td></tr><tr><th>Event origin</th></tr><tr><td>IP</td><td>' + JSON.stringify(data.origin.ip) + '</td></tr><tr><td>City & Country</td><td>' + JSON.stringify(data.origin.city) + ',&nbsp;' + JSON.stringify(data.origin.country_name) + '</td></tr><tr><td>GPS</td><td>' + JSON.stringify(data.origin.latitude) + ',&nbsp;' + JSON.stringify(data.origin.longitude) + '</td></tr><tr><th>Event Destination</th></tr><tr><td>IP</td><td>' + JSON.stringify(data.destination.ip) + '</td></tr><tr><td>City & Country</td><td>' + JSON.stringify(data.destination.city) + ',&nbsp;' + JSON.stringify(data.destination.country_name) + '</td></tr><tr><td>GPS</td><td>' + JSON.stringify(data.destination.latitude) + ',&nbsp;' + JSON.stringify(data.destination.longitude) + '</td></tr></table></div>';
return str.replace(/&quot;/g,"");
}
// Missing information
else {
return '';
}
}
}
});
legend_data = d3.select("#legend")
.selectAll("li")
.data(Object.keys(cat_color).sort())
.enter()
.append("li")
.append("span")
.style("color", function(datum) { return cat_color[datum]})
.text(function(datum) { return datum; })
.append("span")
.text(function(datum) { return "" + cat_desc[datum]})
.style("color", "white");
instance.addPlugin('ph_arc', handleArcs);
setInterval(function(){
d3.json("./warden-map.json", function(error, data) {
if (data) {
var cur_data = data.pop()
var cur_time = new Date().getTime();
if (cur_data != prev_data) {
prev_data = cur_data;
for (var i=0; i<data.length; i++) {
data[i].arrivalTime = cur_time;
data[i].delay = i/data.length;
}
main_data = main_data.concat(data);
}
}
var trimmed_data = [];
for (var i=0; i<main_data.length; i++) {
if (main_data[i].arrivalTime + 3500 > cur_time) {
trimmed_data.push(main_data[i]);
}
}
main_data = trimmed_data;
trimmed_data = cur_time = cur_data = error = data = null;
instance.ph_arc(main_data);
});
}, 1000);
};
Datamap.prototype._handleMapReady = function(datamap) {
this.zoom = new Zoom({
$container: this.$container,
datamap: datamap
});
}
<!-- -->
<!-- -->
<!-- -*- coding: utf-8 -*- -->
<!-- -->
<!-- warden-map.html -->
<!-- -->
<!-- Copyright (C) 2016 Cesnet z.s.p.o -->
<!-- Use of this source is governed by a 3-clause BSD-style license, see LICENSE file. -->
<!-- -->
<!-- -->
<!DOCTYPE html>
<meta name="robots" content="noindex">
<meta charset="utf-8">
<link href='https://fonts.googleapis.com/css?family=Oswald&amp;subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="./css/warden-map.css"/>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="./js/datamaps.world.min.js"></script>
<script src="./js/warden-map.js"></script>
<!--
<h2>Warden Map</h2>
<div id="tools">
<button class="zoom-button" data-zoom="reset">&#x2302</button>
<button class="zoom-button" data-zoom="out">-</button>
<button class="zoom-button" data-zoom="in">+</button>
<div id="zoom-info"></div>
</div>
-->
<div id="container"></div>
<div id="heading">Attacks, detected in CESNET network<br/>
SABU - Sharing and Analysis of Security Events
</div>
<div id="legend-box">
<p><b>Reported to Warden right <i>now</i>.</b></p>
<ul id="legend"></ul>
</div>
<!-- Draw datamap into id="container" -->
<script>new Datamap();</script>
</body>
</html>
{
"Log": {
"filename": "/var/log/warden_ra.log",
"level": "info"
},
"Registry": {
"type": "EjbcaRegistry",
"url": "https://ejbca.example.org/ejbca/ejbcaws/ejbcaws?wsdl",
"cert": "warden_ra.cert.pem",
"key": "warden_ra.key.pem",
"ca_name": "Example CA",
"certificate_profile_name": "Example",
"end_entity_profile_name": "Example EE",
"subject_dn_template": "DC=cz,DC=example-ca,DC=warden,CN=%s",
"username_suffix": "@warden"
}
}
+---------------------------------+ +---------------------------------+
| Warden Client Library 3.0-beta2 | | Warden Client Library 3.0-beta3 |
+---------------------------------+ +---------------------------------+
Content Content
...@@ -15,7 +15,7 @@ A. Introduction ...@@ -15,7 +15,7 @@ A. Introduction
The main goal of Warden 3 is to address the shortcomings, which emerged The main goal of Warden 3 is to address the shortcomings, which emerged
during several years of Warden 2.X operation. Warden 3 uses flexible and during several years of Warden 2.X operation. Warden 3 uses flexible and
descriptive event format, based on JSON. Warden 3 protocol is based on plain descriptive event format, based on JSON. Warden 3 protocol is based on plain
HTTPS queries with help of JSON (Warden 2 SOAP is heavyweight, outdated and HTTPS queries with help of JSON (Warden 2 SOAP is heavyweight, outdated and
draws in many dependencies). Clients can be multilanguage, unlike SOAP/HTTPS, draws in many dependencies). Clients can be multilanguage, unlike SOAP/HTTPS,
plain HTTPS and JSON is mature in many mainstream programming languages. plain HTTPS and JSON is mature in many mainstream programming languages.
...@@ -36,7 +36,7 @@ B. Quick start (TL;DR) ...@@ -36,7 +36,7 @@ B. Quick start (TL;DR)
sandbox URL, etc. sandbox URL, etc.
If succesful, you will receive authentication secret. If succesful, you will receive authentication secret.
* Use warden_curl_test.sh to check you are able to talk to server. * Use warden_curl_test.sh to check you are able to talk to server.
* See warden_client_examples.py on how to integrate sending/recieving * See warden_client_examples.py on how to integrate sending/receiving
into your Python application. into your Python application.
* Alternatively, check 'contrib' directory in Warden GIT for various * Alternatively, check 'contrib' directory in Warden GIT for various
ready to use tools or recipes. You may find senders for various ready to use tools or recipes. You may find senders for various
...@@ -53,7 +53,7 @@ C.1. Event description format ...@@ -53,7 +53,7 @@ C.1. Event description format
IDEA - Intrusion Detection Extensible Alert, flexible extensible format IDEA - Intrusion Detection Extensible Alert, flexible extensible format
for security events, see: for security events, see:
https://csirt.cesnet.cz/IDEA https://idea.cesnet.cz/
C.2. Event serial ID C.2. Event serial ID
...@@ -65,7 +65,7 @@ C.3. Authentication ...@@ -65,7 +65,7 @@ C.3. Authentication
In Warden 2, clients get authenticated by server certificate, however In Warden 2, clients get authenticated by server certificate, however
server certificate is usually same for the whole machine, so individual server certificate is usually same for the whole machine, so individual
clients are differentiated only by telling its own name. However, client name clients are differentiated only by telling their own name. However, client name
is widely known, so this allows for client impersonation within one machine. is widely known, so this allows for client impersonation within one machine.
Warden 3 slightly improves this schema by replacing client name in Warden 3 slightly improves this schema by replacing client name in
authentication phase by "secret", random string, shared among particular authentication phase by "secret", random string, shared among particular
...@@ -134,7 +134,7 @@ sending events). The keys of the object, which may be available, are: ...@@ -134,7 +134,7 @@ sending events). The keys of the object, which may be available, are:
description. description.
Client errors (4xx) are considered permanent - client must not try to send Client errors (4xx) are considered permanent - client must not try to send
same event again as it will get always rejected - client administrator same event again as it will always get rejected - client administrator
will need to inspect logs and rectify the cause. will need to inspect logs and rectify the cause.
Server errors (5xx) may be considered by client as temporary and client is Server errors (5xx) may be considered by client as temporary and client is
...@@ -465,4 +465,4 @@ for e in res: ...@@ -465,4 +465,4 @@ for e in res:
debug_str() output increasingly more detailed info. debug_str() output increasingly more detailed info.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
Copyright (C) 2011-2015 Cesnet z.s.p.o Copyright (C) 2011-2022 Cesnet z.s.p.o
...@@ -13,7 +13,7 @@ from operator import itemgetter ...@@ -13,7 +13,7 @@ from operator import itemgetter
fix_logging_filename = str if version_info<(2, 7) else lambda x: x fix_logging_filename = str if version_info<(2, 7) else lambda x: x
if version_info > (3, 0): if version_info[0] >= 3:
import http.client as httplib import http.client as httplib
from urllib.parse import urlparse from urllib.parse import urlparse
from urllib.parse import urlencode from urllib.parse import urlencode
...@@ -25,7 +25,7 @@ else: ...@@ -25,7 +25,7 @@ else:
VERSION = "3.0-beta2" VERSION = "3.0-beta3"
DEFAULT_CA_STORES = [ DEFAULT_CA_STORES = [
"/etc/ssl/certs/ca-certificates.crt", # Deb "/etc/ssl/certs/ca-certificates.crt", # Deb
...@@ -42,9 +42,9 @@ class HTTPSConnection(httplib.HTTPSConnection): ...@@ -42,9 +42,9 @@ class HTTPSConnection(httplib.HTTPSConnection):
Used only if ssl.SSLContext is not available (Python version < 2.7.9) Used only if ssl.SSLContext is not available (Python version < 2.7.9)
''' '''
def __init__(self, host, **kwargs): def __init__(self, host, **kwargs):
self.ciphers = kwargs.pop('ciphers',None) self.ciphers = kwargs.pop('ciphers', None)
self.ca_certs = kwargs.pop('ca_certs',None) self.ca_certs = kwargs.pop('ca_certs', None)
self.ssl_version = kwargs.pop('ssl_version',ssl.PROTOCOL_SSLv23) self.ssl_version = kwargs.pop('ssl_version', getattr(ssl, "PROTOCOL_TLS", ssl.PROTOCOL_SSLv23))
httplib.HTTPSConnection.__init__(self,host,**kwargs) httplib.HTTPSConnection.__init__(self,host,**kwargs)
...@@ -269,8 +269,8 @@ class Client(object): ...@@ -269,8 +269,8 @@ class Client(object):
self.retry = int(retry) self.retry = int(retry)
self.pause = int(pause) self.pause = int(pause)
self.ciphers = 'TLS_RSA_WITH_AES_256_CBC_SHA' self.ciphers = None
self.sslversion = ssl.PROTOCOL_TLSv1 self.sslversion = getattr(ssl, "PROTOCOL_TLS", ssl.PROTOCOL_SSLv23)
# If Python is new enough to have SSLContext, use it for SSL settings, # If Python is new enough to have SSLContext, use it for SSL settings,
# otherwise our own class derived from httplib.HTTPSConnection is used # otherwise our own class derived from httplib.HTTPSConnection is used
...@@ -425,20 +425,23 @@ class Client(object): ...@@ -425,20 +425,23 @@ class Client(object):
else: else:
argurl = "" argurl = ""
try: self.headers = {"Accept": "application/json"}
if payload is None: data = None
data = ""
else: if payload is None:
method = "GET"
else:
method = "POST"
try:
data = json.dumps(payload) data = json.dumps(payload)
except: except:
return Error(message="Serialization to JSON failed", return Error(message="Serialization to JSON failed",
exc=exc_info(), method=func, payload=payload) exc=exc_info(), method=func, payload=payload)
self.headers = { self.headers.update({
"Content-Type": "application/json", "Content-Type": "application/json",
"Accept": "application/json", "Content-Length": str(len(data))
"Content-Length": str(len(data)) })
}
# HTTP(S)Connection is oneshot object (and we don't speak "pipelining") # HTTP(S)Connection is oneshot object (and we don't speak "pipelining")
conn = self.connect() conn = self.connect()
...@@ -447,7 +450,7 @@ class Client(object): ...@@ -447,7 +450,7 @@ class Client(object):
loc = '%s/%s%s' % (self.url.path, func, argurl) loc = '%s/%s%s' % (self.url.path, func, argurl)
try: try:
conn.request("POST", loc, data, self.headers) conn.request(method, loc, data, self.headers)
except: except:
conn.close() conn.close()
return Error(message="Sending of request to server failed", return Error(message="Sending of request to server failed",
...@@ -662,7 +665,6 @@ def format_time(year, month, day, hour, minute, second, microsec=0, utcoffset=No ...@@ -662,7 +665,6 @@ def format_time(year, month, day, hour, minute, second, microsec=0, utcoffset=No
def read_cfg(cfgfile): def read_cfg(cfgfile):
abspath = path.join(path.dirname(__file__), cfgfile) with open(cfgfile, "r") as f:
with open(abspath, "r") as f:
stripcomments = "\n".join((l for l in f if not l.lstrip().startswith(("#", "//")))) stripcomments = "\n".join((l for l in f if not l.lstrip().startswith(("#", "//"))))
return json.loads(stripcomments) return json.loads(stripcomments)
...@@ -23,7 +23,6 @@ curl \ ...@@ -23,7 +23,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/blefub?client=$client&secret=$secret" "$url/blefub?client=$client&secret=$secret"
echo echo
...@@ -33,7 +32,6 @@ curl \ ...@@ -33,7 +32,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/?client=$client&secret=$secret" "$url/?client=$client&secret=$secret"
echo echo
...@@ -43,7 +41,6 @@ curl \ ...@@ -43,7 +41,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=$client" "$url/getEvents?client=$client"
echo echo
...@@ -53,7 +50,6 @@ curl \ ...@@ -53,7 +50,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents" "$url/getEvents"
echo echo
...@@ -63,7 +59,6 @@ curl \ ...@@ -63,7 +59,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=asdf.blefub" "$url/getEvents?client=asdf.blefub"
echo echo
...@@ -73,7 +68,6 @@ curl \ ...@@ -73,7 +68,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=asdf.blefub&secret=$secret" "$url/getEvents?client=asdf.blefub&secret=$secret"
echo echo
...@@ -83,7 +77,6 @@ curl \ ...@@ -83,7 +77,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=$client&secret=ASDFblefub" "$url/getEvents?client=$client&secret=ASDFblefub"
echo echo
...@@ -93,7 +86,6 @@ curl \ ...@@ -93,7 +86,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?secret=$secret" "$url/getEvents?secret=$secret"
echo echo
...@@ -114,7 +106,6 @@ curl \ ...@@ -114,7 +106,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=$client&secret=$secret&cat=bflm" "$url/getEvents?client=$client&secret=$secret&cat=bflm"
echo echo
...@@ -124,7 +115,6 @@ curl \ ...@@ -124,7 +115,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=$client&secret=$secret&cat=Other&nocat=Test" "$url/getEvents?client=$client&secret=$secret&cat=Other&nocat=Test"
echo echo
...@@ -145,7 +135,6 @@ curl \ ...@@ -145,7 +135,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=$client&secret=$secret&self=test" "$url/getEvents?client=$client&secret=$secret&self=test"
echo echo
...@@ -155,7 +144,6 @@ curl \ ...@@ -155,7 +144,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=$client&secret=$secret&bad=guy" "$url/getEvents?client=$client&secret=$secret&bad=guy"
echo echo
...@@ -165,7 +153,6 @@ curl \ ...@@ -165,7 +153,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=$client&secret=$secret" "$url/getEvents?client=$client&secret=$secret"
echo echo
...@@ -175,7 +162,6 @@ curl \ ...@@ -175,7 +162,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getEvents?client=$client&secret=$secret&count=3&id=10" "$url/getEvents?client=$client&secret=$secret&count=3&id=10"
echo echo
...@@ -185,7 +171,6 @@ curl \ ...@@ -185,7 +171,6 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getDebug?client=$client&secret=$secret" "$url/getDebug?client=$client&secret=$secret"
echo echo
...@@ -195,6 +180,5 @@ curl \ ...@@ -195,6 +180,5 @@ curl \
--cert $certfile \ --cert $certfile \
--cacert $cafile \ --cacert $cafile \
--connect-timeout 3 \ --connect-timeout 3 \
--request POST \
"$url/getInfo?client=$client&secret=$secret" "$url/getInfo?client=$client&secret=$secret"
echo echo
File moved
+---------------------------------------+ +---------------------------------------+
| Warden Filer 3.0-beta2 for Warden 3.X | | Warden Filer 3.0-beta3 for Warden 3.X |
+---------------------------------------+ +---------------------------------------+
Content Content
...@@ -135,11 +135,11 @@ directories must obey simple protocols, which use atomic "rename" to avoid ...@@ -135,11 +135,11 @@ directories must obey simple protocols, which use atomic "rename" to avoid
locking issues. locking issues.
Also, your directory (and its structure) _must_ reside on the same Also, your directory (and its structure) _must_ reside on the same
filesystem to keep "rename" atomic. _Never_ try to mount some of the filesystem to keep "rename" atomic. _Never_ try to mount some of the
subdirectories ("temp", "incoming", "errors") from other filesystem. subdirectories ("tmp", "incoming", "errors") from other filesystem.
1. Inserting file 1. Inserting file
* The file you want to create _must_ be created in the "temp" subdirectory * The file you want to create _must_ be created in the "tmp" subdirectory
first, _not_ "incoming". Filename is arbitrary, but must be unique among first, _not_ "incoming". Filename is arbitrary, but must be unique among
all subdirectories. all subdirectories.
...@@ -148,7 +148,7 @@ subdirectories ("temp", "incoming", "errors") from other filesystem. ...@@ -148,7 +148,7 @@ subdirectories ("temp", "incoming", "errors") from other filesystem.
or complete. or complete.
For simple usage (bash scripts, etc.), just creating sufficiently random For simple usage (bash scripts, etc.), just creating sufficiently random
filename in "temp" and then moving into "incoming" may be enough. filename in "tmp" and then moving into "incoming" may be enough.
Concatenating $RANDOM couple of times will do. :) Concatenating $RANDOM couple of times will do. :)
For advanced or potentially concurrent usage inserting enough of unique For advanced or potentially concurrent usage inserting enough of unique
...@@ -159,7 +159,7 @@ subdirectories ("temp", "incoming", "errors") from other filesystem. ...@@ -159,7 +159,7 @@ subdirectories ("temp", "incoming", "errors") from other filesystem.
2. Picking up file 2. Picking up file
* Rename the file to work with into "temp" directory. * Rename the file to work with into "tmp" directory.
* Do whatever you want with contents, and when finished, rename file back * Do whatever you want with contents, and when finished, rename file back
into "incoming", or remove, or move somewhere else, or move into "errors" into "incoming", or remove, or move somewhere else, or move into "errors"
......
...@@ -18,9 +18,15 @@ import resource ...@@ -18,9 +18,15 @@ import resource
import atexit import atexit
import argparse import argparse
from os import path, mkdir from os import path, mkdir
from random import choice, randint; from random import choice, randint
VERSION = "3.0-beta2" # for py2/py3 compatibility
try:
basestring
except NameError:
basestring = str
VERSION = "3.0-beta3"
class NamedFile(object): class NamedFile(object):
""" Wrapper class for file objects, which allows and tracks filename """ Wrapper class for file objects, which allows and tracks filename
...@@ -65,9 +71,9 @@ class NamedFile(object): ...@@ -65,9 +71,9 @@ class NamedFile(object):
class SafeDir(object): class SafeDir(object):
""" Maildir like directory for safe file exchange. """ Maildir like directory for safe file exchange.
- Producers are expected to drop files into "temp" under globally unique - Producers are expected to drop files into "tmp" under globally unique
filename and rename it into "incoming" atomically (newfile method) filename and rename it into "incoming" atomically (newfile method)
- Workers pick files in "incoming", rename them into "temp", - Workers pick files in "incoming", rename them into "tmp",
do whatever they want, and either discard them or move into do whatever they want, and either discard them or move into
"errors" directory "errors" directory
""" """
...@@ -76,7 +82,7 @@ class SafeDir(object): ...@@ -76,7 +82,7 @@ class SafeDir(object):
self.path = self._ensure_path(p) self.path = self._ensure_path(p)
self.incoming = self._ensure_path(path.join(self.path, "incoming")) self.incoming = self._ensure_path(path.join(self.path, "incoming"))
self.errors = self._ensure_path(path.join(self.path, "errors")) self.errors = self._ensure_path(path.join(self.path, "errors"))
self.temp = self._ensure_path(path.join(self.path, "temp")) self.temp = self._ensure_path(path.join(self.path, "tmp"))
self.hostname = socket.gethostname() self.hostname = socket.gethostname()
self.pid = os.getpid() self.pid = os.getpid()
...@@ -116,7 +122,7 @@ class SafeDir(object): ...@@ -116,7 +122,7 @@ class SafeDir(object):
# which checked uniqueness among all directories by atomic # which checked uniqueness among all directories by atomic
# links. # links.
# First find and open name unique within temp # First find and open name unique within tmp
tmpname = None tmpname = None
while not tmpname: while not tmpname:
tmpname = self._get_new_name() tmpname = self._get_new_name()
...@@ -181,7 +187,7 @@ def receiver(config, wclient, sdir, oneshot): ...@@ -181,7 +187,7 @@ def receiver(config, wclient, sdir, oneshot):
nf = sdir.newfile() nf = sdir.newfile()
with nf.f as f: with nf.f as f:
data = json.dumps(event) data = json.dumps(event)
f.write(data) f.write(data.encode('utf-8'))
nf.moveto(sdir.incoming) nf.moveto(sdir.incoming)
count_ok += 1 count_ok += 1
except Exception as e: except Exception as e:
...@@ -291,7 +297,7 @@ def sender(config, wclient, sdir, oneshot): ...@@ -291,7 +297,7 @@ def sender(config, wclient, sdir, oneshot):
continue # Silently go to next filename, somebody else might have interfered continue # Silently go to next filename, somebody else might have interfered
try: try:
with nf.open("rb") as fd: with nf.open("rb") as fd:
data = fd.read() data = fd.read().decode('utf-8')
event = json.loads(data) event = json.loads(data)
if not match_event(event, **filt): if not match_event(event, **filt):
wclient.logger.debug("Unmatched event: %s" % data) wclient.logger.debug("Unmatched event: %s" % data)
...@@ -392,7 +398,7 @@ def daemonize( ...@@ -392,7 +398,7 @@ def daemonize(
# PID file # PID file
if pidfile is not None: if pidfile is not None:
pidd = os.open(pidfile, os.O_RDWR|os.O_CREAT|os.O_EXCL|os.O_TRUNC) pidd = os.open(pidfile, os.O_RDWR|os.O_CREAT|os.O_EXCL|os.O_TRUNC)
os.write(pidd, str(os.getpid())+"\n") os.write(pidd, (str(os.getpid())+"\n").encode())
os.close(pidd) os.close(pidd)
# Define and setup atexit closure # Define and setup atexit closure
@atexit.register @atexit.register
...@@ -464,6 +470,18 @@ def get_configs(): ...@@ -464,6 +470,18 @@ def get_configs():
def get_uid_gid(str_id, get_nam_func):
if str_id:
try:
id = int(str_id)
except ValueError:
id = get_nam_func(str_id)[2]
else:
id = None
return id
if __name__ == "__main__": if __name__ == "__main__":
args = get_args() args = get_args()
...@@ -476,12 +494,17 @@ if __name__ == "__main__": ...@@ -476,12 +494,17 @@ if __name__ == "__main__":
try: try:
if args.daemon: if args.daemon:
from pwd import getpwnam
from grp import getgrnam
uid = get_uid_gid(fconfig.get("uid"), getpwnam)
gid = get_uid_gid(fconfig.get("gid"), getgrnam)
daemonize( daemonize(
work_dir = fconfig.get("work_dir", "."), work_dir = fconfig.get("work_dir", "."),
chroot_dir = fconfig.get("chroot_dir"), chroot_dir = fconfig.get("chroot_dir"),
umask = fconfig.get("umask"), umask = fconfig.get("umask"),
uid = fconfig.get("uid"), uid = uid,
gid = fconfig.get("gid"), gid = gid,
pidfile = args.pid_file, pidfile = args.pid_file,
files_preserve = get_logger_files(wclient.logger), files_preserve = get_logger_files(wclient.logger),
signals = { signals = {
......
...@@ -27,6 +27,8 @@ done ...@@ -27,6 +27,8 @@ done
function log_daemon_msg () { echo -n "$@"; } function log_daemon_msg () { echo -n "$@"; }
function log_end_msg () { [ $1 -eq 0 ] && echo " OK" || echo " Failed"; } function log_end_msg () { [ $1 -eq 0 ] && echo " OK" || echo " Failed"; }
function status_of_proc () { [ -f "$PID" ] && ps u -p $(<"$PID") || echo "$PID not found."; } function status_of_proc () { [ -f "$PID" ] && ps u -p $(<"$PID") || echo "$PID not found."; }
function start_daemon () { shift; shift; $* ; }
function killproc () { kill $(cat $PID) ; }
[ -f /lib/lsb/init-functions ] && . /lib/lsb/init-functions [ -f /lib/lsb/init-functions ] && . /lib/lsb/init-functions
......
...@@ -27,6 +27,8 @@ done ...@@ -27,6 +27,8 @@ done
function log_daemon_msg () { echo -n "$@"; } function log_daemon_msg () { echo -n "$@"; }
function log_end_msg () { [ $1 -eq 0 ] && echo " OK" || echo " Failed"; } function log_end_msg () { [ $1 -eq 0 ] && echo " OK" || echo " Failed"; }
function status_of_proc () { [ -f "$PID" ] && ps u -p $(<"$PID") || echo "$PID not found."; } function status_of_proc () { [ -f "$PID" ] && ps u -p $(<"$PID") || echo "$PID not found."; }
function start_daemon () { shift; shift; $* ; }
function killproc () { kill $(cat $PID) ; }
[ -f /lib/lsb/init-functions ] && . /lib/lsb/init-functions [ -f /lib/lsb/init-functions ] && . /lib/lsb/init-functions
......
File moved
...@@ -36,7 +36,6 @@ Dependencies ...@@ -36,7 +36,6 @@ Dependencies
Python 2.7+ Python 2.7+
Apache 2.2+ Apache 2.2+
mod_wsgi 3.3+ mod_wsgi 3.3+
EJBCA_ 3.9+
Registration process Registration process
...@@ -57,7 +56,7 @@ to the new client administrator along with other setup information. ...@@ -57,7 +56,7 @@ to the new client administrator along with other setup information.
The client administrator runs the application script with application The client administrator runs the application script with application
password: password:
warden_apply.sh org.example.warden.client P4SSW0RD warden_apply.sh https://warden.example.org/warden-ra/ org.example.warden.client P4SSW0RD
The script creates new X509 key, CSR certificate request and makes call to The script creates new X509 key, CSR certificate request and makes call to
the Warden RA web service, where it obtains the new complete certificate. the Warden RA web service, where it obtains the new complete certificate.
...@@ -69,7 +68,7 @@ Prolonging existing client credentials ...@@ -69,7 +68,7 @@ Prolonging existing client credentials
The client administrator runs the application script with his existing valid The client administrator runs the application script with his existing valid
Warden credentials, which he needs to prolong: Warden credentials, which he needs to prolong:
warden_apply.sh org.example.warden.client cert.pem key.pem warden_apply.sh https://warden.example.org/warden-ra/ org.example.warden.client cert.pem key.pem
The script creates new X509 key, CSR certificate request and makes call to The script creates new X509 key, CSR certificate request and makes call to
the Warden RA web service, where it obtains the new complete certificate. the Warden RA web service, where it obtains the new complete certificate.
...@@ -78,10 +77,6 @@ the Warden RA web service, where it obtains the new complete certificate. ...@@ -78,10 +77,6 @@ the Warden RA web service, where it obtains the new complete certificate.
Installation Installation
------------ ------------
As for now, correctly configured and running EJBCA_ PKI is necessary. PKI part
of the RA is however pluggable, so simple openssl backend is also planned.
This depends heavily on your distribution and Apache configuration. This depends heavily on your distribution and Apache configuration.
Basically you need to create and include apache.conf: Basically you need to create and include apache.conf:
...@@ -103,6 +98,8 @@ Also, for warden_server.wsgi, you can use warden_server.wsgi.dist as ...@@ -103,6 +98,8 @@ Also, for warden_server.wsgi, you can use warden_server.wsgi.dist as
a template. You will possibly need to change at least configuration a template. You will possibly need to change at least configuration
file path. file path.
* Now install and/or configure RA backend (see README.openssl or README.ejbca)
* Configure Warden RA (see next chapter) * Configure Warden RA (see next chapter)
* Reload Apache * Reload Apache
...@@ -133,7 +130,7 @@ Sections and their "type" objects can be: ...@@ -133,7 +130,7 @@ Sections and their "type" objects can be:
Log: FileLogger, SysLogger Log: FileLogger, SysLogger
Auth: OptionalAuthenticator Auth: OptionalAuthenticator
Registry: EjbcaRegistry Registry: EjbcaRegistry, OpenSSLRegistry
Handler: CertHandler Handler: CertHandler
"type" keyword is not mandatory, if not specified, first implementation "type" keyword is not mandatory, if not specified, first implementation
...@@ -156,21 +153,14 @@ Object function and configuration keys are as follows: ...@@ -156,21 +153,14 @@ Object function and configuration keys are as follows:
CertHandler: the main certificate requestor implementation CertHandler: the main certificate requestor implementation
EjbcaRegistry: EJBCA connector configuration For OpenSSLRegistry or EJBCARegistry configuration please see
url: EJBCA API URL, for example "https://ejbca.example.org/ejbca/ejbcaws/ejbcaws?wsdl" README.openssl or README.ejbca respectively.
cert: certificate for authentication to EJBCA, defaults to "warden_ra.cert.pem"
key: key for authentication to EJBCA, defaults to "warden_ra.key.pem"
ca_name: name of the CA, dedicated for Warden, defaults to "Example CA"
certificate_profile_name: name of the EJBCA certificate profile, defaults to "Example"
end_entity_profile_name: name of the EJBCA entity profile, defaults to "Example EE"
subject_dn_template: template for the DN generation, defaults to "DC=cz,DC=example-ca,DC=warden,CN=%s"
username_suffix: suffix, which will be added to EJBCA entities, defaults to "@warden"
Command line Command line
------------ ------------
Whe run from the command line, RA allows for client and request management. When run from the command line, RA allows for client and request management.
warden_ra.py [--help] [-c CONFIG] [-v] warden_ra.py [--help] [-c CONFIG] [-v]
...@@ -226,7 +216,6 @@ Whe run from the command line, RA allows for client and request management. ...@@ -226,7 +216,6 @@ Whe run from the command line, RA allows for client and request management.
.. _Warden: https://warden.cesnet.cz/ .. _Warden: https://warden.cesnet.cz/
.. _EJBCA: https://www.ejbca.org/
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
......