Browse Source

Control Panel fixes & whole domain blocks

* Fix config files and init script
 * Help text and error coloring in WUI template.
 * EOL white space. :-/
 * Add whole domain blocking
master
Jon Foster 2 years ago
parent
commit
bb0278f2a5
8 changed files with 157 additions and 63 deletions
  1. +12
    -0
      controlpanel/TODO.md
  2. +1
    -0
      controlpanel/data.h
  3. +1
    -0
      controlpanel/default
  4. +18
    -11
      controlpanel/init
  5. +3
    -3
      controlpanel/lighttpd.conf
  6. +13
    -3
      controlpanel/mainskin.tmpl
  7. +2
    -2
      controlpanel/sample.js
  8. +107
    -44
      controlpanel/trafficctrl.cpp

+ 12
- 0
controlpanel/TODO.md View File

@@ -0,0 +1,12 @@

* Add comments to "dns" decision table
* "dns.decided" field should default to the last hit in "connections".
* format table centered with the title.
* Extend DNS mechanism to incorporate anonymous (no DNS) connections.
* Daemonization mechanism to allow us to run as non-root.
* C++CMS creates socket as original user:group (typically root:root). Yet the
PID file is written as the requested user:group. This means that permissions
on the socket have to be handled outside of C++CMS or it needs some patchery.
* Tool to read JSON conf and supply values to "init" script.
* Some list filtering tools in the WUI to target actions on specific subsets
of DNS names

+ 1
- 0
controlpanel/data.h View File

@@ -28,6 +28,7 @@ struct Domain {
struct DomainList :public cppcms::base_content { struct DomainList :public cppcms::base_content {
std::vector<Domain> list; std::vector<Domain> list;
std::string filter; // Which filter was used to show list std::string filter; // Which filter was used to show list
std::string error;
int page, pages, page_size, count; int page, pages, page_size, count;
}; };




+ 1
- 0
controlpanel/default View File

@@ -2,6 +2,7 @@
# defaults. # defaults.


# Configuration file for the TrafficCtrl server # Configuration file for the TrafficCtrl server
# NOTE: service won't start until this is set
#CONF=/etc/poorman-ids/sample.js #CONF=/etc/poorman-ids/sample.js
# Where "run" files are placed. This is the Debian+ default: # Where "run" files are placed. This is the Debian+ default:
#RUN=/run #RUN=/run


+ 18
- 11
controlpanel/init View File

@@ -16,7 +16,7 @@
NAME="trafficctrl" NAME="trafficctrl"
DAEMON="/usr/sbin/$NAME" DAEMON="/usr/sbin/$NAME"
RUN=/run RUN=/run
CONF=/etc/poorman-ids/sample.js
CONF=""
PID="" PID=""
GROUP="" GROUP=""


@@ -49,11 +49,16 @@ CTRL() {


do_start() { do_start() {
echo -n "Starting Traffic Control: " echo -n "Starting Traffic Control: "
if [ -z "$CONF" ]; then
echo "NOT CONFIGURED"
return 0
fi
if CTRL --start --oknodo --umask 007 $GROUP -- -c "$CONF"; then if CTRL --start --oknodo --umask 007 $GROUP -- -c "$CONF"; then
echo "OK" echo "OK"
return 0 #JIC
else else
echo "FAIL" echo "FAIL"
exit 1
return 1
fi fi
} }


@@ -63,9 +68,10 @@ do_stop() {
echo -n "Stoping Traffic Control: " echo -n "Stoping Traffic Control: "
if CTRL --stop --remove-pidfile; then if CTRL --stop --remove-pidfile; then
echo "OK" echo "OK"
return 0 #JIC
else else
echo "FAIL" echo "FAIL"
exit 1
return 1
fi fi
} }


@@ -75,36 +81,37 @@ do_status() {
echo -n "Traffic Control is: " echo -n "Traffic Control is: "
if CTRL --status; then if CTRL --status; then
echo "Up" echo "Up"
return 0 #JIC
else else
echo "Down" echo "Down"
exit 1
return 1
fi fi
} }






### Main()
### Main()


case "$1" in case "$1" in
start) start)
do_start do_start
;; ;;
stop) stop)
do_stop do_stop
;; ;;
restart) restart)
do_status && do_stop do_status && do_stop
do_start do_start
;; ;;
status) status)
do_status do_status
;; ;;
*) *)
echo "$0 {start | stop | restart | status}" echo "$0 {start | stop | restart | status}"
;; ;;
esac
esac

+ 3
- 3
controlpanel/lighttpd.conf View File

@@ -5,11 +5,11 @@
#fastcgi.debug = 1 #fastcgi.debug = 1
fastcgi.server = ( fastcgi.server = (
"/webmonitor" => "/webmonitor" =>
( "trafficctrl" =>
( "trafficctrl" =>
( "socket" => "/run/poorman-ids/trafficctrl.fcgi", ( "socket" => "/run/poorman-ids/trafficctrl.fcgi",
"check-local" => "disable", "check-local" => "disable",
# "fix-root-scriptname" => "enable", # "fix-root-scriptname" => "enable",
#"docroot" => "/" # remote server may use
#"docroot" => "/" # remote server may use
# its own docroot # its own docroot


) )
@@ -20,7 +20,7 @@ fastcgi.server = (


auth.backend = "htpasswd" auth.backend = "htpasswd"
auth.backend.htpasswd.userfile = "/etc/lighttpd/lighttpd.users" auth.backend.htpasswd.userfile = "/etc/lighttpd/lighttpd.users"
auth.require += ( "/webmonitor" => (
auth.require += ( "/webmonitor" => (
"method" => "basic", "method" => "basic",
"realm" => "Web Monitor", "realm" => "Web Monitor",
#"require" => "user=root" #"require" => "user=root"


+ 13
- 3
controlpanel/mainskin.tmpl View File

@@ -52,21 +52,31 @@
border-top: 2px solid black; border-top: 2px solid black;
border-bottom: 2px solid black; border-bottom: 2px solid black;
padding: 0.25em; padding: 0.25em;
}
}
div.pager { div.pager {
font-weight: bold; font-weight: bold;
} }
</style> </style>
</head><body> </head><body>
<div class="menu"><% include menu() %></div> <div class="menu"><% include menu() %></div>
<h1><% include title() %></h1> <h1><% include title() %></h1>
<div id="content"> <div id="content">
<% foreach domain rowid r from 1 in list %> <% foreach domain rowid r from 1 in list %>
<% include pager(page, pages) %> <% include pager(page, pages) %>
<% if not empty error %>
<p style="color: red"><i><b><%= error %></b></i></p>
<% end %>
<form method="POST"> <form method="POST">
Whole Domain: <input name="domain" size=50> Whole Domain: <input name="domain" size=50>
<p><i><b>NOTE:</b> the root domain name listed here will match any records in
this list with the same suffix and record the decision as chosen below.
<% if ( content.filter == "undecided" ) %>
Whole domains can be blocked by prefixing them with "*.". This means that
even if the exact host name or subdomain is not listed here it will get
blocked.
<% end %>
<table> <table>
<tr><th><select name="op" value="0"> <tr><th><select name="op" value="0">
<option value="0">Undecided</option> <option value="0">Undecided</option>


+ 2
- 2
controlpanel/sample.js View File

@@ -34,7 +34,7 @@
// // or "socket": "path..." // // or "socket": "path..."
// }, // },
// "http" : { "script": "" }, // "http" : { "script": "" },
// This is a FastCGI example, which could be used to provide HTTPS & passwords // This is a FastCGI example, which could be used to provide HTTPS & passwords
// via the host service: // via the host service:
"service": { "service": {
@@ -42,7 +42,7 @@
// To serve on a TCP socket: "ip": "0.0.0.0", "port": 8080 // To serve on a TCP socket: "ip": "0.0.0.0", "port": 8080
// or a socket. preferred if lesser privlieged users have access // or a socket. preferred if lesser privlieged users have access
// to the server: // to the server:
"socket": "/run/poorman-ids/traffcctl.fcgi
"socket": "/run/poorman-ids/traffcctl.fcgi"
}, },


// In context of restricted access environment this is probably OK. // In context of restricted access environment this is probably OK.


+ 107
- 44
controlpanel/trafficctrl.cpp View File

@@ -5,8 +5,10 @@
// Copyright JF Possibilities, Inc. All rights reserved. // Copyright JF Possibilities, Inc. All rights reserved.
// //
// Provide a control panel to manage which domains we want to watch, // Provide a control panel to manage which domains we want to watch,
// ignore anc block.
// ignore and block.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// TODO: put the note fields to use (dns,dns_wild)
// TODO: Any purpose in wild-card selection "accepted" host names?
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
@@ -36,6 +38,21 @@




////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// DNS wild card matcher
//////////////////////////////////////////////////////////////////////

bool dns_wild_match(const StringList &wilds, const std::string &host) {
for(StringList::const_iterator i=wilds.begin(); i!=wilds.end(); i++) {
if(host.length()<i->length()) continue;
if(host==*i) return true;
if(host.substr(host.length()-i->length()+1)=="."+*i) return true;
}
return false;
}



//////////////////////////////////////////////////////////////////////
// C++CMS App to manage the state of all known domain names. // C++CMS App to manage the state of all known domain names.
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////


@@ -48,7 +65,7 @@ struct app: public cppcms::application {
int items_per_page; int items_per_page;




std::string numlist(const std::string &field) { std::string numlist(const std::string &field) {
std::string r; std::string r;
const cppcms::http::request::form_type &POST = request().post(); const cppcms::http::request::form_type &POST = request().post();
@@ -64,19 +81,66 @@ struct app: public cppcms::application {
} }
return r; return r;
} }
void list(Domain::STATUS sid) {
int i;



/// This looks at new log entries and creates missing DNS entries.
/// This should be run periodically. Right now we run just prior
/// displaying a "list" page. This could be used in a CRON job...

void catchup() {
std::string s; std::string s;
StringList list; StringList list;
DomainList c;
StringList wild;

/// Read list of wild-card blocks ///

cppdb::result r = *sql
<< "SELECT name FROM dns_wild WHERE status=?"
<< Domain::blocked;
while(r.next()) {
r >> s;
wild.push_back(s);
}


/// Auto-add unknown domains to DNS list ///

// Find all connections not recorded in the DNS table
r = *sql <<
"SELECT c.them_name "
"FROM connections c LEFT OUTER JOIN dns ON c.them_name=dns.name "
"WHERE c.them_name<>'' AND dns.name IS NULL "
"GROUP BY c.them_name";
while(r.next()) {
r >> s;
list.push_back(s);
}

// add them
if(!list.empty()) {
cppdb::statement q = *sql << "INSERT INTO dns (name, status) VALUES (?,?)";
for(StringList::iterator i=list.begin(); i!=list.end(); i++) {
q.reset();
// If blocked by wild card add it to the blocked list otherwise its
// undecided.
q << *i << (Domain::blocked*dns_wild_match(wild, *i)) << cppdb::exec;
}
}
}



void list(Domain::STATUS sid) {
int i;
std::string s;
DomainList c;
cppdb::result r;

// TODO: put this someplace else? // TODO: put this someplace else?
/// Form processing /// /// Form processing ///
if(request().request_method()=="POST") { if(request().request_method()=="POST") {
std::string op = request().post("op"); std::string op = request().post("op");
// nothing to do without a valid "op" // nothing to do without a valid "op"
@@ -86,37 +150,36 @@ struct app: public cppcms::application {
*sql << "UPDATE dns SET status="+op+" WHERE name IN ("+s+")" *sql << "UPDATE dns SET status="+op+" WHERE name IN ("+s+")"
<< cppdb::exec; << cppdb::exec;
} }
if((s=request().post("domain"))!="") {
if(s=="*" || s=="*.") {
c.error = "'*' and '*.' are not acceptable.";
} else if((s=request().post("domain"))!="") {
// wild card block handling
if(s.substr(0,2)=="*.") {
s = s.substr(2);
if(op=="2")
*sql << "INSERT INTO dns_wild (name,status) VALUES (?,?)"
<< s << op
<< cppdb::exec;
else
c.error = "Wild cards can only be used to <b>block</b> domains. "
"This has been treated as regular domain prefix "
"search.";
}
// regardless move all existing matches to the specified status.
*sql << "UPDATE dns SET status=? WHERE name=? OR name LIKE ?" *sql << "UPDATE dns SET status=? WHERE name=? OR name LIKE ?"
<< op << s << ("%."+s) << op << s << ("%."+s)
<< cppdb::exec; << cppdb::exec;
} }
} }
} }
/// Auto add unknown domains to DNS list ///


cppdb::result r = *sql <<
"SELECT c.them_name "
"FROM connections c LEFT OUTER JOIN dns ON c.them_name=dns.name "
"WHERE c.them_name<>'' AND dns.name IS NULL "
"GROUP BY c.them_name";
while(r.next()) {
r >> s;
list.push_back(s);
}
if(!list.empty()) {
cppdb::statement q = *sql << "INSERT INTO dns (name) VALUES (?)";
for(StringList::iterator i=list.begin(); i!=list.end(); i++) {
q.reset();
q << *i << cppdb::exec;
}
}
/// Produce list of unknowns ///
c.filter = filter_titles[sid];
/// Update DB with new log data ///

catchup();

/// Produce list of names of the desired STATUS ///

c.filter = filter_titles[sid];
s = request().get("pg"); s = request().get("pg");
if(s=="") if(s=="")
c.page = 1; c.page = 1;
@@ -136,22 +199,22 @@ struct app: public cppcms::application {
c.pages = (c.count+items_per_page-1)/items_per_page; c.pages = (c.count+items_per_page-1)/items_per_page;
render("mainskin", "domain_list", c); render("mainskin", "domain_list", c);
} }
void undecided() { list(Domain::undecided); } void undecided() { list(Domain::undecided); }
void accepted() { list(Domain::accepted ); } void accepted() { list(Domain::accepted ); }
void blocked() { list(Domain::blocked ); } void blocked() { list(Domain::blocked ); }
app(cppcms::service &s): cppcms::application(s), items_per_page(50) { app(cppcms::service &s): cppcms::application(s), items_per_page(50) {
#ifdef DEBUGGIN #ifdef DEBUGGIN
std::cerr << "spawning app object" << std::endl; std::cerr << "spawning app object" << std::endl;
#endif #endif
sql.reset(new cppdb::session()); sql.reset(new cppdb::session());
sql->open(settings().get<std::string>("trafficctrl.db")); sql->open(settings().get<std::string>("trafficctrl.db"));
mapper().root(root_uri); mapper().root(root_uri);
mapper().assign("blocked", "/blocked"); mapper().assign("blocked", "/blocked");
dispatcher().assign("/blocked/?", &app::blocked, this); dispatcher().assign("/blocked/?", &app::blocked, this);
@@ -161,7 +224,7 @@ std::cerr << "spawning app object" << std::endl;
dispatcher().assign("/?", &app::undecided, this); dispatcher().assign("/?", &app::undecided, this);


} }
// logging // logging
void main(const std::string url) { void main(const std::string url) {
#ifdef DEBUGGIN #ifdef DEBUGGIN
@@ -172,7 +235,7 @@ std::cerr << "request: " << url << '\n'
#endif #endif
cppcms::application::main(url); cppcms::application::main(url);
} }
}; };






Loading…
Cancel
Save