catflap 0.0.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rubocop.yml +21 -0
- data/.rubocop_todo.yml +7 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +20 -0
- data/README.md +134 -0
- data/Rakefile +6 -0
- data/bin/catflap +71 -64
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/catflap.gemspec +32 -0
- data/etc/config.yaml +30 -0
- data/etc/init.d/catflap +89 -0
- data/etc/passfile.yaml +7 -0
- data/lib/catflap.rb +108 -64
- data/lib/catflap/command.rb +102 -0
- data/lib/catflap/firewall.rb +56 -0
- data/lib/catflap/http.rb +288 -0
- data/lib/catflap/netfilter/writer.rb +127 -0
- data/lib/catflap/plugins/firewall/iptables.rb +104 -0
- data/lib/catflap/plugins/firewall/netfilter.rb +114 -0
- data/lib/catflap/plugins/firewall/plugin.rb +67 -0
- data/lib/catflap/version.rb +5 -0
- data/lib/netfilter/writer.rb +125 -0
- data/ui/css/catflap.css +44 -0
- data/ui/images/catflap.png +0 -0
- data/ui/index.rhtml +23 -0
- data/ui/js/catflap.js +85 -0
- data/ui/js/sha256.js +166 -0
- metadata +109 -11
- data/lib/catflap-http.rb +0 -111
Binary file
|
data/ui/index.rhtml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
|
6
|
+
<script src="/js/sha256.js"></script>
|
7
|
+
<script src="/js/catflap.js"></script>
|
8
|
+
<link rel="stylesheet" href="/css/catflap.css" />
|
9
|
+
<title>Catflap | No dogs allowed!</title>
|
10
|
+
</head>
|
11
|
+
<body id="home">
|
12
|
+
<div id="logo-wrapper"><img id="logo" class="centered" src="/images/catflap.png" /></div>
|
13
|
+
<div id="passphrase-form-wrapper" class="centered">
|
14
|
+
<input id="passphrase" name="passphrase" type="text" class="default-value" value="Enter your pass phrase here." />
|
15
|
+
<div id="message-wrapper" class="centered">
|
16
|
+
<div id="locked-message" class="message centered">The site you are trying to access is locked. You must enter a valid pass phrase
|
17
|
+
to enter the site.</div>
|
18
|
+
<div id="failed-message" class="message centered hidden">Sorry, but we don't recognize that pass phrase. Please try again and if you
|
19
|
+
are still having trouble email your contact for further instructions.</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
</body>
|
23
|
+
</html>
|
data/ui/js/catflap.js
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
(function($) { $(document).ready(function() {
|
2
|
+
|
3
|
+
// Hide default text
|
4
|
+
$('.default-value').each(function() {
|
5
|
+
var default_value = this.value;
|
6
|
+
$(this).focus(function() {
|
7
|
+
if(this.value === default_value) {
|
8
|
+
this.value = '';
|
9
|
+
}
|
10
|
+
});
|
11
|
+
$(this).blur(function() {
|
12
|
+
if(this.value === '') {
|
13
|
+
this.value = default_value;
|
14
|
+
}
|
15
|
+
});
|
16
|
+
});
|
17
|
+
|
18
|
+
// Bind token input field with 'enter' keypress.
|
19
|
+
$('input#passphrase').keypress(function(e) {
|
20
|
+
if(e.which == 13) {
|
21
|
+
var pass = $('input#passphrase').val();
|
22
|
+
|
23
|
+
// Get the first word to send as the key for the pass phrase.
|
24
|
+
// The first word is any part that comes before a special
|
25
|
+
// character (including spaces) other than the underscore.
|
26
|
+
var matches = pass.match(/^(\w+)\W+/);
|
27
|
+
// If there is nothing that looks like a key then don't make
|
28
|
+
// an authentication request.
|
29
|
+
if (matches === null) {
|
30
|
+
return;
|
31
|
+
}
|
32
|
+
|
33
|
+
// Handshake with the Catflap server by requesting a timestamp.
|
34
|
+
ts = 0;
|
35
|
+
$.ajax({
|
36
|
+
url: '/catflap/sync',
|
37
|
+
method: 'POST',
|
38
|
+
success: function(jsonData){
|
39
|
+
data = JSON.parse(jsonData);
|
40
|
+
ts = data.Timestamp;
|
41
|
+
|
42
|
+
// Construct our data packet to send to the server.
|
43
|
+
var data = {
|
44
|
+
"_key" : matches[1],
|
45
|
+
"ts" : ts
|
46
|
+
};
|
47
|
+
data.token = Sha256.hash(pass + ts);
|
48
|
+
|
49
|
+
$.ajax({
|
50
|
+
url: '/catflap/knock',
|
51
|
+
method: 'POST',
|
52
|
+
data: data,
|
53
|
+
success: function(jsonData){
|
54
|
+
data = JSON.parse(jsonData);
|
55
|
+
|
56
|
+
switch (data.StatusCode) {
|
57
|
+
case 200:
|
58
|
+
if (data.RedirectUrl == "reload") {
|
59
|
+
location.reload(true);
|
60
|
+
} else {
|
61
|
+
$(location).attr('href', data.RedirectUrl);
|
62
|
+
}
|
63
|
+
break;
|
64
|
+
default:
|
65
|
+
$('#passphrase').addClass('failed');
|
66
|
+
$('#locked-message').hide();
|
67
|
+
$('#failed-message').show();
|
68
|
+
break;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
})
|
72
|
+
.fail(function(jsonData){
|
73
|
+
console.log('Unable to authenticate with the server: ' + jsonData);
|
74
|
+
});
|
75
|
+
}
|
76
|
+
})
|
77
|
+
.fail(function(jsonData){
|
78
|
+
console.log('Sync handshake failed: ' + jsonData);
|
79
|
+
return;
|
80
|
+
});
|
81
|
+
|
82
|
+
}
|
83
|
+
});
|
84
|
+
});
|
85
|
+
})(jQuery);
|
data/ui/js/sha256.js
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
2
|
+
/* SHA-256 implementation in JavaScript (c) Chris Veness 2002-2014 / MIT Licence */
|
3
|
+
/* */
|
4
|
+
/* - see http://csrc.nist.gov/groups/ST/toolkit/secure_hashing.html */
|
5
|
+
/* http://csrc.nist.gov/groups/ST/toolkit/examples.html */
|
6
|
+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
7
|
+
|
8
|
+
/* jshint node:true *//* global define, escape, unescape */
|
9
|
+
'use strict';
|
10
|
+
|
11
|
+
|
12
|
+
/**
|
13
|
+
* SHA-256 hash function reference implementation.
|
14
|
+
*
|
15
|
+
* @namespace
|
16
|
+
*/
|
17
|
+
var Sha256 = {};
|
18
|
+
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Generates SHA-256 hash of string.
|
22
|
+
*
|
23
|
+
* @param {string} msg - String to be hashed
|
24
|
+
* @returns {string} Hash of msg as hex character string
|
25
|
+
*/
|
26
|
+
Sha256.hash = function(msg) {
|
27
|
+
// convert string to UTF-8, as SHA only deals with byte-streams
|
28
|
+
msg = msg.utf8Encode();
|
29
|
+
|
30
|
+
// constants [§4.2.2]
|
31
|
+
var K = [
|
32
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
33
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
34
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
35
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
36
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
37
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
38
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
39
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ];
|
40
|
+
// initial hash value [§5.3.1]
|
41
|
+
var H = [
|
42
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ];
|
43
|
+
|
44
|
+
// PREPROCESSING
|
45
|
+
|
46
|
+
msg += String.fromCharCode(0x80); // add trailing '1' bit (+ 0's padding) to string [§5.1.1]
|
47
|
+
|
48
|
+
// convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1]
|
49
|
+
var l = msg.length/4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length
|
50
|
+
var N = Math.ceil(l/16); // number of 16-integer-blocks required to hold 'l' ints
|
51
|
+
var M = new Array(N);
|
52
|
+
|
53
|
+
for (var i=0; i<N; i++) {
|
54
|
+
M[i] = new Array(16);
|
55
|
+
for (var j=0; j<16; j++) { // encode 4 chars per integer, big-endian encoding
|
56
|
+
M[i][j] = (msg.charCodeAt(i*64+j*4)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16) |
|
57
|
+
(msg.charCodeAt(i*64+j*4+2)<<8) | (msg.charCodeAt(i*64+j*4+3));
|
58
|
+
} // note running off the end of msg is ok 'cos bitwise ops on NaN return 0
|
59
|
+
}
|
60
|
+
// add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1]
|
61
|
+
// note: most significant word would be (len-1)*8 >>> 32, but since JS converts
|
62
|
+
// bitwise-op args to 32 bits, we need to simulate this by arithmetic operators
|
63
|
+
M[N-1][14] = ((msg.length-1)*8) / Math.pow(2, 32); M[N-1][14] = Math.floor(M[N-1][14]);
|
64
|
+
M[N-1][15] = ((msg.length-1)*8) & 0xffffffff;
|
65
|
+
|
66
|
+
|
67
|
+
// HASH COMPUTATION [§6.1.2]
|
68
|
+
|
69
|
+
var W = new Array(64); var a, b, c, d, e, f, g, h;
|
70
|
+
for (var i=0; i<N; i++) {
|
71
|
+
|
72
|
+
// 1 - prepare message schedule 'W'
|
73
|
+
for (var t=0; t<16; t++) W[t] = M[i][t];
|
74
|
+
for (var t=16; t<64; t++) W[t] = (Sha256.σ1(W[t-2]) + W[t-7] + Sha256.σ0(W[t-15]) + W[t-16]) & 0xffffffff;
|
75
|
+
|
76
|
+
// 2 - initialise working variables a, b, c, d, e, f, g, h with previous hash value
|
77
|
+
a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7];
|
78
|
+
|
79
|
+
// 3 - main loop (note 'addition modulo 2^32')
|
80
|
+
for (var t=0; t<64; t++) {
|
81
|
+
var T1 = h + Sha256.Σ1(e) + Sha256.Ch(e, f, g) + K[t] + W[t];
|
82
|
+
var T2 = Sha256.Σ0(a) + Sha256.Maj(a, b, c);
|
83
|
+
h = g;
|
84
|
+
g = f;
|
85
|
+
f = e;
|
86
|
+
e = (d + T1) & 0xffffffff;
|
87
|
+
d = c;
|
88
|
+
c = b;
|
89
|
+
b = a;
|
90
|
+
a = (T1 + T2) & 0xffffffff;
|
91
|
+
}
|
92
|
+
// 4 - compute the new intermediate hash value (note 'addition modulo 2^32')
|
93
|
+
H[0] = (H[0]+a) & 0xffffffff;
|
94
|
+
H[1] = (H[1]+b) & 0xffffffff;
|
95
|
+
H[2] = (H[2]+c) & 0xffffffff;
|
96
|
+
H[3] = (H[3]+d) & 0xffffffff;
|
97
|
+
H[4] = (H[4]+e) & 0xffffffff;
|
98
|
+
H[5] = (H[5]+f) & 0xffffffff;
|
99
|
+
H[6] = (H[6]+g) & 0xffffffff;
|
100
|
+
H[7] = (H[7]+h) & 0xffffffff;
|
101
|
+
}
|
102
|
+
|
103
|
+
return Sha256.toHexStr(H[0]) + Sha256.toHexStr(H[1]) + Sha256.toHexStr(H[2]) + Sha256.toHexStr(H[3]) +
|
104
|
+
Sha256.toHexStr(H[4]) + Sha256.toHexStr(H[5]) + Sha256.toHexStr(H[6]) + Sha256.toHexStr(H[7]);
|
105
|
+
};
|
106
|
+
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Rotates right (circular right shift) value x by n positions [§3.2.4].
|
110
|
+
* @private
|
111
|
+
*/
|
112
|
+
Sha256.ROTR = function(n, x) {
|
113
|
+
return (x >>> n) | (x << (32-n));
|
114
|
+
};
|
115
|
+
|
116
|
+
/**
|
117
|
+
* Logical functions [§4.1.2].
|
118
|
+
* @private
|
119
|
+
*/
|
120
|
+
Sha256.Σ0 = function(x) { return Sha256.ROTR(2, x) ^ Sha256.ROTR(13, x) ^ Sha256.ROTR(22, x); };
|
121
|
+
Sha256.Σ1 = function(x) { return Sha256.ROTR(6, x) ^ Sha256.ROTR(11, x) ^ Sha256.ROTR(25, x); };
|
122
|
+
Sha256.σ0 = function(x) { return Sha256.ROTR(7, x) ^ Sha256.ROTR(18, x) ^ (x>>>3); };
|
123
|
+
Sha256.σ1 = function(x) { return Sha256.ROTR(17, x) ^ Sha256.ROTR(19, x) ^ (x>>>10); };
|
124
|
+
Sha256.Ch = function(x, y, z) { return (x & y) ^ (~x & z); };
|
125
|
+
Sha256.Maj = function(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); };
|
126
|
+
|
127
|
+
|
128
|
+
/**
|
129
|
+
* Hexadecimal representation of a number.
|
130
|
+
* @private
|
131
|
+
*/
|
132
|
+
Sha256.toHexStr = function(n) {
|
133
|
+
// note can't use toString(16) as it is implementation-dependant,
|
134
|
+
// and in IE returns signed numbers when used on full words
|
135
|
+
var s="", v;
|
136
|
+
for (var i=7; i>=0; i--) { v = (n>>>(i*4)) & 0xf; s += v.toString(16); }
|
137
|
+
return s;
|
138
|
+
};
|
139
|
+
|
140
|
+
|
141
|
+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
142
|
+
|
143
|
+
|
144
|
+
/** Extend String object with method to encode multi-byte string to utf8
|
145
|
+
* - monsur.hossa.in/2012/07/20/utf-8-in-javascript.html */
|
146
|
+
if (typeof String.prototype.utf8Encode == 'undefined') {
|
147
|
+
String.prototype.utf8Encode = function() {
|
148
|
+
return unescape( encodeURIComponent( this ) );
|
149
|
+
};
|
150
|
+
}
|
151
|
+
|
152
|
+
/** Extend String object with method to decode utf8 string to multi-byte */
|
153
|
+
if (typeof String.prototype.utf8Decode == 'undefined') {
|
154
|
+
String.prototype.utf8Decode = function() {
|
155
|
+
try {
|
156
|
+
return decodeURIComponent( escape( this ) );
|
157
|
+
} catch (e) {
|
158
|
+
return this; // invalid UTF-8? return as-is
|
159
|
+
}
|
160
|
+
};
|
161
|
+
}
|
162
|
+
|
163
|
+
|
164
|
+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
165
|
+
if (typeof module != 'undefined' && module.exports) module.exports = Sha256; // CommonJs export
|
166
|
+
if (typeof define == 'function' && define.amd) define([], function() { return Sha256; }); // AMD
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: catflap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,19 +9,116 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
date: 2016-03-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.8.3
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.8.3
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.11'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.11'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '10.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.0'
|
78
|
+
description: ! "A simple solution to provide on-demand service access (e.g.\n port
|
79
|
+
80 on webserver), where a more robust and secure VPN solution is not\n available.
|
80
|
+
Essentially, it is a more user-friendly form of \"port knocking\".\n The original
|
81
|
+
proof-of-concept implementation was run for almost three years\n by Demotix, to
|
82
|
+
protect development and staging servers from search engine\n crawlers and other
|
83
|
+
unwanted traffic."
|
84
|
+
email: nykcowham@gmail.com
|
17
85
|
executables:
|
18
86
|
- catflap
|
19
87
|
extensions: []
|
20
88
|
extra_rdoc_files: []
|
21
89
|
files:
|
22
|
-
-
|
23
|
-
-
|
90
|
+
- .gitignore
|
91
|
+
- .rspec
|
92
|
+
- .rubocop.yml
|
93
|
+
- .rubocop_todo.yml
|
94
|
+
- CODE_OF_CONDUCT.md
|
95
|
+
- Gemfile
|
96
|
+
- Gemfile.lock
|
97
|
+
- LICENSE
|
98
|
+
- README.md
|
99
|
+
- Rakefile
|
24
100
|
- bin/catflap
|
101
|
+
- bin/console
|
102
|
+
- bin/setup
|
103
|
+
- catflap.gemspec
|
104
|
+
- etc/config.yaml
|
105
|
+
- etc/init.d/catflap
|
106
|
+
- etc/passfile.yaml
|
107
|
+
- lib/catflap.rb
|
108
|
+
- lib/catflap/command.rb
|
109
|
+
- lib/catflap/firewall.rb
|
110
|
+
- lib/catflap/http.rb
|
111
|
+
- lib/catflap/netfilter/writer.rb
|
112
|
+
- lib/catflap/plugins/firewall/iptables.rb
|
113
|
+
- lib/catflap/plugins/firewall/netfilter.rb
|
114
|
+
- lib/catflap/plugins/firewall/plugin.rb
|
115
|
+
- lib/catflap/version.rb
|
116
|
+
- lib/netfilter/writer.rb
|
117
|
+
- ui/css/catflap.css
|
118
|
+
- ui/images/catflap.png
|
119
|
+
- ui/index.rhtml
|
120
|
+
- ui/js/catflap.js
|
121
|
+
- ui/js/sha256.js
|
25
122
|
homepage: https://github.com/nyk/catflap
|
26
123
|
licenses:
|
27
124
|
- MIT
|
@@ -44,9 +141,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
44
141
|
requirements:
|
45
142
|
- NetFilters (iptables) installed and working.
|
46
143
|
rubyforge_project:
|
47
|
-
rubygems_version: 1.8.
|
144
|
+
rubygems_version: 1.8.23
|
48
145
|
signing_key:
|
49
146
|
specification_version: 3
|
50
|
-
summary: Manage NetFilter-based rules to grant port access on-demand
|
51
|
-
|
147
|
+
summary: Manage NetFilter-based rules to grant port access on-demand commandline or
|
148
|
+
REST API requests.
|
52
149
|
test_files: []
|
150
|
+
has_rdoc:
|
data/lib/catflap-http.rb
DELETED
@@ -1,111 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'catflap'
|
4
|
-
require 'webrick'
|
5
|
-
include WEBrick
|
6
|
-
|
7
|
-
module CatflapWebserver
|
8
|
-
|
9
|
-
def self.generate_server port
|
10
|
-
config = {:Port => port}
|
11
|
-
server = HTTPServer.new(config)
|
12
|
-
yield server if block_given?
|
13
|
-
['INT', 'TERM'].each {|signal|
|
14
|
-
trap(signal) {server.shutdown}
|
15
|
-
}
|
16
|
-
server.start
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.start_server cf
|
20
|
-
generate_server cf.port do |server|
|
21
|
-
server.mount '/', Servlet, cf
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Servlet < HTTPServlet::AbstractServlet
|
26
|
-
|
27
|
-
def initialize server, cf
|
28
|
-
super server
|
29
|
-
@cf = cf
|
30
|
-
end
|
31
|
-
|
32
|
-
def do_GET req, resp
|
33
|
-
# Split the path into piece
|
34
|
-
path = req.path[1..-1].split('/')
|
35
|
-
raise HTTPStatus::OK if path[0] == 'favicon.ico'
|
36
|
-
response_class = CatflapRestService.const_get 'Service'
|
37
|
-
|
38
|
-
if response_class and response_class.is_a? Class
|
39
|
-
# There was a method given
|
40
|
-
if path[0]
|
41
|
-
response_method = path[0].to_sym
|
42
|
-
# Make sure the method exists in the class
|
43
|
-
raise HTTPStatus::NotFound if !response_class.respond_to? response_method
|
44
|
-
|
45
|
-
if path[0] == "enter"
|
46
|
-
url = response_class.send response_method, req, resp, @cf
|
47
|
-
resp.set_redirect HTTPStatus::Redirect, url
|
48
|
-
end
|
49
|
-
|
50
|
-
# Remaining path segments get passed in as arguments to the method
|
51
|
-
if path.length > 1
|
52
|
-
resp.body = response_class.send response_method, req, resp, @cf, path[1..-1]
|
53
|
-
else
|
54
|
-
resp.body = response_class.send response_method, req, resp, @cf
|
55
|
-
end
|
56
|
-
raise HTTPStatus::OK
|
57
|
-
|
58
|
-
# No method was given, so check for an "index" method instead
|
59
|
-
else
|
60
|
-
raise HTTPStatus::NotFound if !response_class.respond_to? :index
|
61
|
-
resp.body = response_class.send :index
|
62
|
-
raise HTTPStatus::OK
|
63
|
-
end
|
64
|
-
else
|
65
|
-
raise HTTPStatus::NotFound
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
module CatflapRestService
|
72
|
-
class Service
|
73
|
-
|
74
|
-
def self.index
|
75
|
-
return "hello world"
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.enter req, resp, cf
|
79
|
-
ip = req.peeraddr.pop
|
80
|
-
host = req.addr[2]
|
81
|
-
cf.add_address! ip unless cf.check_address ip
|
82
|
-
return "http://" << host << ":80"
|
83
|
-
end
|
84
|
-
|
85
|
-
def self.add req, resp, cf, args
|
86
|
-
ip = args[0]
|
87
|
-
unless cf.check_address ip
|
88
|
-
cf.add_address! ip
|
89
|
-
return "#{ip} has been granted access"
|
90
|
-
else
|
91
|
-
return "#{ip} already has access"
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def self.remove req, resp, cf, args
|
96
|
-
ip = args[0]
|
97
|
-
cf.delete_address! ip
|
98
|
-
return "Access granted to #{ip} has been removed"
|
99
|
-
end
|
100
|
-
|
101
|
-
def self.check req, resp, cf, args
|
102
|
-
ip = args[0]
|
103
|
-
|
104
|
-
if cf.check_address ip
|
105
|
-
return "#{ip} has access to ports: #{cf.dports}"
|
106
|
-
else
|
107
|
-
return "#{ip} does not have access to ports: #{cf.dports}"
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|