Commit 2faea2f3 authored by Leigh Stoller's avatar Leigh Stoller

Server side of firewall support for XEN containers.

This differs from the current firewall support, which assumes a single
firewall for an entire experiment, hosted on a dedicated physical
node. At some point, it would be better to host the dedicated firewall
inside a XEN container, but that is a project for another day (year).

Instead, I added two sets of firewall rules to the default_firewall_rules
table, one for dom0 and another for domU. These follow the current
style setup of open,basic,closed, while elabinelab is ignored since it
does not make sense for this yet.

These two rules sets are independent, the dom0 rules can be applied to
the physical host, and domU rules can be applied to specific
containers.

My goal is that all shared nodes will get the dom0 closed rules (ssh
from local boss only) to avoid the ssh attacks that all of the racks
are seeing.

DomU rules can be applied on a per-container (node) basis. As
mentioned above this is quite different, and needed minor additions to
the virt_nodes table to allow it.
parent dd2cb49c
......@@ -507,7 +507,7 @@ CREATE TABLE `datapository_databases` (
DROP TABLE IF EXISTS `default_firewall_rules`;
CREATE TABLE `default_firewall_rules` (
`type` enum('ipfw','ipfw2','iptables','ipfw2-vlan','iptables-vlan') NOT NULL default 'ipfw',
`type` enum('ipfw','ipfw2','iptables','ipfw2-vlan','iptables-vlan','iptables-dom0','iptables-domU') NOT NULL default 'ipfw',
`style` enum('open','closed','basic','emulab') NOT NULL default 'basic',
`enabled` tinyint(4) NOT NULL default '0',
`ruleno` int(10) unsigned NOT NULL default '0',
......@@ -4957,6 +4957,8 @@ CREATE TABLE `virt_nodes` (
`numeric_id` int(11) default NULL,
`sharing_mode` varchar(32) default NULL,
`role` enum('node','bridge') NOT NULL default 'node',
`firewall_style` tinytext,
`firewall_log` tinytext,
PRIMARY KEY (`exptidx`,`vname`),
UNIQUE KEY `pideid` (`pid`,`eid`,`vname`),
KEY `pid` (`pid`,`eid`,`vname`)
......
......@@ -1109,6 +1109,8 @@ REPLACE INTO table_regex VALUES ('virt_firewalls','fwname','text','redirect','vi
REPLACE INTO table_regex VALUES ('virt_firewalls','type','text','regex','^(ipfw|ipfw2|iptables|ipfw2-vlan|iptables-vlan)$',0,0,NULL);
REPLACE INTO table_regex VALUES ('virt_firewalls','style','text','regex','^(open|closed|basic|emulab)$',0,0,NULL);
REPLACE INTO table_regex VALUES ('virt_nodes','firewall_style','text','regex','^(open|closed|basic|emulab)$',0,0,NULL);
REPLACE INTO table_regex VALUES ('mailman_lists','pid_idx','text','redirect','projects:pid_idx',0,0,NULL);
REPLACE INTO table_regex VALUES ('mailman_lists','password1','text','redirect','default:tinytext',0,0,NULL);
REPLACE INTO table_regex VALUES ('mailman_lists','password2','text','redirect','default:tinytext',0,0,NULL);
......
#
# New firewall type.
#
use strict;
use libdb;
sub DoUpdate($$$)
{
my ($dbhandle, $dbname, $version) = @_;
DBQueryFatal("alter table default_firewall_rules change `type` ".
" `type` enum('ipfw','ipfw2','iptables','ipfw2-vlan',".
" 'iptables-vlan','iptables-dom0', ".
" 'iptables-domU') ".
" NOT NULL default 'ipfw'");
if (!DBSlotExists("virt_nodes", "firewall_style")) {
DBQueryFatal("alter table virt_nodes add ".
" `firewall_style` tinytext");
}
DBQueryFatal("REPLACE INTO table_regex VALUES ".
"('virt_nodes','firewall_style','text','regex',".
"'^(open|closed|basic|emulab)\$',0,0,NULL)");
if (!DBSlotExists("virt_nodes", "firewall_log")) {
DBQueryFatal("alter table virt_nodes add ".
" `firewall_log` tinytext");
}
}
# Local Variables:
# mode:perl
# End:
# -*- tcl -*-
#
# Copyright (c) 2000-2013 University of Utah and the Flux Group.
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -146,6 +146,9 @@ Node instproc init {s} {
# This is a blockstore thing.
$self set bstore_agent 0
# Per node firewall thing.
$self set fw_style ""
}
Bridge instproc init {s} {
......@@ -229,6 +232,7 @@ Node instproc updatedb {DB} {
$self instvar simulated
$self instvar sharing_mode
$self instvar topo
$self instvar fw_style
$self instvar X_
$self instvar Y_
$self instvar orientation_
......@@ -402,6 +406,11 @@ Node instproc updatedb {DB} {
lappend values $parent_osid
}
if { $fw_style != "" } {
lappend fields "firewall_style"
lappend values $fw_style
}
$sim spitxml_data "virt_nodes" $fields $values
if {$topo != "" && ($type == "robot" || $hwtype_class($type) == "robot")} {
......
......@@ -73,6 +73,7 @@ proc tb-set-endnodeshaping {link onoff} {}
proc tb-set-noshaping {link onoff} {}
proc tb-set-useveth {link onoff} {}
proc tb-set-link-encap {link style} {}
proc tb-set-fw-style {vnode style} {}
proc tb-set-allowcolocate {lanlink onoff} {}
proc tb-set-colocate-factor {factor} {}
proc tb-set-sync-server {node} {}
......
......@@ -2290,6 +2290,20 @@ proc tb-set-elabinelab-fw-type {type} {
set elabinelab_fw_type $type
}
#
# Set firewall style for an individual node. Really only makes sense
# for linux nodes with iptables. Might need to add an os_feature.
#
proc tb-set-fw-style {vnode style}
{
if ($style != "basic" && $style != "closed" &&
$style != "open" && $style != "elabinelab") {
perror "\[tb-set-fw-style] $style is not a valid type"
return
}
$vnode set fw_style $style
}
#
# Set numeric ID (this is a mote thing)
#
......
......@@ -246,6 +246,7 @@ typedef struct {
char sfshostid[TBDB_FLEN_SFSHOSTID];
char testdb[TBDB_FLEN_TINYTEXT];
char sharing_mode[TBDB_FLEN_TINYTEXT];
char erole[TBDB_FLEN_TINYTEXT];
char privkey[PRIVKEY_LEN+1];
char nodeuuid[TBDB_FLEN_UUID];
/* This key is a replacement for privkey, on protogeni resources */
......@@ -6795,7 +6796,8 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
" AS isdedicated_wa, "
" r.genisliver_idx,r.tmcd_redirect, "
" r.sharing_mode,e.geniflags,n.uuid, "
" n.nonfsmounts,e.nonfsmounts AS enonfs "
" n.nonfsmounts,e.nonfsmounts AS enonfs, "
" r.erole "
"FROM nodes AS n "
"LEFT JOIN reserved AS r ON "
" r.node_id=n.node_id "
......@@ -6824,7 +6826,7 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
" (SELECT node_id FROM widearea_nodeinfo "
" WHERE privkey='%s') "
" AND notmcdinfo_types.attrvalue IS NULL",
37, nodekey);
38, nodekey);
}
else if (reqp->isvnode) {
char clause[BUFSIZ];
......@@ -6860,7 +6862,8 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
" u.admin,null, "
" r.genisliver_idx,r.tmcd_redirect, "
" r.sharing_mode,e.geniflags,nv.uuid, "
" nv.nonfsmounts,e.nonfsmounts AS enonfs "
" nv.nonfsmounts,e.nonfsmounts AS enonfs, "
" r.erole "
"from nodes as nv "
"left join nodes as np on "
" np.node_id=nv.phys_nodeid "
......@@ -6881,7 +6884,7 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
"left join users as u on "
" u.uid_idx=e.swapper_idx "
"where nv.node_id='%s' and (%s)",
37, reqp->vnodeid, clause);
38, reqp->vnodeid, clause);
}
else {
char clause[BUFSIZ];
......@@ -6910,7 +6913,8 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
" as isdedicated_wa, "
" r.genisliver_idx,r.tmcd_redirect, "
" r.sharing_mode,e.geniflags,n.uuid, "
" n.nonfsmounts,e.nonfsmounts AS enonfs "
" n.nonfsmounts,e.nonfsmounts AS enonfs, "
" r.erole "
"from interfaces as i "
"left join nodes as n on n.node_id=i.node_id "
"left join reserved as r on "
......@@ -6938,7 +6942,7 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
" on n.type=dedicated_wa_types.type "
"where (%s) "
" and notmcdinfo_types.attrvalue is NULL",
37, clause);
38, clause);
}
if (!res) {
......@@ -7048,6 +7052,8 @@ iptonodeid(struct in_addr ipaddr, tmcdreq_t *reqp, char* nodekey)
reqp->geniflags = atoi(row[33]);
else
reqp->geniflags = 0;
if (row[37])
strcpy(reqp->erole, row[37]);
}
if (row[9])
......@@ -8156,34 +8162,12 @@ COMMAND_PROTOTYPE(doroutelist)
*/
COMMAND_PROTOTYPE(dorole)
{
MYSQL_RES *res;
MYSQL_ROW row;
char buf[MYBUFSIZE];
/*
* Get the erole from the reserved table
*/
res = mydb_query("select erole from reserved where node_id='%s'",
1, reqp->nodeid);
if (!res) {
error("ROLE: %s: DB Error getting the role of this node!\n",
reqp->nodeid);
return 1;
}
if ((int)mysql_num_rows(res) == 0) {
mysql_free_result(res);
return 0;
}
row = mysql_fetch_row(res);
if (! row[0] || !row[0][0]) {
mysql_free_result(res);
if (! reqp->allocated) {
return 0;
}
sprintf(buf, "%s\n", row[0]);
mysql_free_result(res);
sprintf(buf, "%s\n", reqp->erole);
client_writeback(sock, buf, strlen(buf), tcp);
if (verbose)
......@@ -8572,16 +8556,82 @@ COMMAND_PROTOTYPE(dofwinfo)
char fwname[TBDB_FLEN_VNAME+2];
char fwtype[TBDB_FLEN_VNAME+2];
char fwstyle[TBDB_FLEN_VNAME+2];
char fwlog[TBDB_FLEN_TINYTEXT+1];
int n, nrows;
char *vlan;
bzero(fwlog, sizeof(fwlog));
/*
* Containers and shared hosts can each have specific rules.
* The shared host can protect itself with iptables-dom0 rules,
* and containers themselves can be firewalled with iptables-domU
* rules (in the default_firewall_rules table). The main point
* though, is that this done on the virt host dom0, not with a
* separate firewall node.
*/
if (reqp->isvnode ||
strcmp(reqp->erole, "virthost") == 0 ||
strcmp(reqp->erole, "sharedhost") == 0) {
/*
* Since we implement the firewall code on the outside of
* the container (dom0), containers themselves know
* nothing about it, nor can containers act *as* firewall
* nodes for an entire experiment (although this is
* something we should implement some day). So just tell
* the container not to worry about it.
*/
if (reqp->asvnode) {
goto nofirewall;
}
res = mydb_query("select firewall_style,firewall_log "
"from virt_nodes "
"where exptidx='%d' and "
" vname='%s' and "
" firewall_style is not null ",
2, reqp->exptidx, reqp->nickname);
if (!res) {
error("FWINFO: %s: DB Error getting firewall info!\n",
reqp->nodeid);
return 1;
}
if ((int)mysql_num_rows(res) == 0) {
mysql_free_result(res);
goto nofirewall;
}
row = mysql_fetch_row(res);
/*
* Ignore the type, we decide.
*/
if (reqp->isvnode) {
strcpy(fwtype, "iptables-domU");
}
else {
strcpy(fwtype, "iptables-dom0");
}
strncpy(fwstyle, row[0], sizeof(fwstyle));
if (row[1] && row[1][0])
strncpy(fwlog, row[1], sizeof(fwlog));
mysql_free_result(res);
OUTPUT(buf, sizeof(buf),
"TYPE=%s STYLE=%s "
"IN_IF=na OUT_IF=na IN_VLAN=0 OUT_VLAN=0\n",
fwtype, fwstyle);
client_writeback(sock, buf, strlen(buf), tcp);
if (verbose)
info("FWINFO: %s", buf);
goto vnodefw;
}
else {
/*
* See if this node's experiment has an associated firewall
*
* XXX will only work if there is one firewall per experiment.
*/
res = mydb_query("select r.node_id,v.type,v.style,v.log,f.fwname,"
" i.IP,i.mac,f.vlan "
res = mydb_query("select r.node_id,v.type,v.style,v.log,"
" f.fwname,i.IP,i.mac,f.vlan "
"from firewalls as f "
"left join reserved as r on"
" f.pid=r.pid and f.eid=r.eid and f.fwname=r.vname "
......@@ -8597,12 +8647,14 @@ COMMAND_PROTOTYPE(dofwinfo)
reqp->nodeid);
return 1;
}
}
/*
* Common case, no firewall
*/
if ((int)mysql_num_rows(res) == 0) {
mysql_free_result(res);
nofirewall:
strncpy(buf, "TYPE=none\n", sizeof(buf));
client_writeback(sock, buf, strlen(buf), tcp);
return 0;
......@@ -8666,21 +8718,24 @@ COMMAND_PROTOTYPE(dofwinfo)
if (verbose)
info("FWINFO: %s", buf);
strncpy(fwtype, row[1], sizeof(fwtype));
strncpy(fwstyle, row[2], sizeof(fwstyle));
strncpy(fwname, row[4], sizeof(fwname));
if (row[3] && row[3][0])
strncpy(fwlog, row[3], sizeof(fwlog));
mysql_free_result(res);
vnodefw:
/*
* Put out info about firewall rule logging
*/
if (vers > 25 && row[3] && row[3][0]) {
OUTPUT(buf, sizeof(buf), "LOG=%s\n", row[3]);
if (vers > 25 && fwlog[0]) {
OUTPUT(buf, sizeof(buf), "LOG=%s\n", fwlog);
client_writeback(sock, buf, strlen(buf), tcp);
if (verbose)
info("FWINFO: %s", buf);
}
strncpy(fwtype, row[1], sizeof(fwtype));
strncpy(fwstyle, row[2], sizeof(fwstyle));
strncpy(fwname, row[4], sizeof(fwname));
mysql_free_result(res);
/*
* Return firewall variables
*/
......@@ -8729,13 +8784,41 @@ COMMAND_PROTOTYPE(dofwinfo)
info("FWINFO: %d variables\n", nrows);
}
if (reqp->isvnode) {
/*
* Write a var for the sshdport number.
*/
res = mydb_query("select n.sshdport from nodes as n "
"where n.node_id='%s'",
1, reqp->nodeid);
if (!res) {
error("FWINFO: %s: DB Error getting sshdport!\n",
reqp->nodeid);
return 1;
}
if ((int)mysql_num_rows(res)) {
row = mysql_fetch_row(res);
OUTPUT(buf, sizeof(buf), "VAR=%s VALUE=\"%s\"\n",
"SSHDPORT", row[0]);
client_writeback(sock, buf, strlen(buf), tcp);
}
mysql_free_result(res);
}
/*
* Get the user firewall rules from the DB and return them.
* Note difference for vnodes/vhosts; these can store rules
# for each vnode/vhost in the topo.
*/
res = mydb_query("select ruleno,rule from firewall_rules "
"where pid='%s' and eid='%s' and fwname='%s' "
"order by ruleno",
2, reqp->pid, reqp->eid, fwname);
2, reqp->pid, reqp->eid,
((reqp->isvnode || reqp->sharing_mode[0]) ?
reqp->nickname ? fwname));
if (!res) {
error("FWINFO: %s: DB Error getting firewall rules!\n",
reqp->nodeid);
......@@ -8749,7 +8832,6 @@ COMMAND_PROTOTYPE(dofwinfo)
row[0], row[1]);
client_writeback(sock, buf, strlen(buf), tcp);
}
mysql_free_result(res);
if (verbose)
info("FWINFO: %d user rules\n", nrows);
......@@ -10253,7 +10335,7 @@ COMMAND_PROTOTYPE(doarpinfo)
MYSQL_RES *res;
MYSQL_ROW row;
int nrows, xenvifrouting = 0;
char buf[MYBUFSIZE], erole[32], arptype[32];
char buf[MYBUFSIZE], arptype[32];
#ifdef GET_SERVERS_FROM_SITEVARS
struct serv {
char name[8];
......@@ -10331,25 +10413,6 @@ COMMAND_PROTOTYPE(doarpinfo)
}
mysql_free_result(res);
res = mydb_query("select erole from reserved where node_id='%s'",
1, reqp->nodeid);
if (!res) {
error("doarpinfo: %s: DB Error checking for reserved.erole\n",
reqp->nodeid);
return 1;
}
if (mysql_num_rows(res) == 0 || (row = mysql_fetch_row(res)) == NULL ||
row[0] == NULL) {
error("doarpinfo: %s: Could not deterimine erole\n",
reqp->nodeid);
mysql_free_result(res);
return 1;
}
strncpy(erole, row[0], sizeof(erole));
mysql_free_result(res);
/*
* Get the GW and primary server (boss, ops, fs) info.
*/
......@@ -10571,7 +10634,7 @@ COMMAND_PROTOTYPE(doarpinfo)
/*
* Subbosses get info for all nodes they provide a service for.
*/
if (strcmp(erole, "subboss") == 0) {
if (strcmp(reqp->erole, "subboss") == 0) {
res = mydb_query("select distinct i.node_id,i.IP,i.mac from "
"interfaces as i,subbosses as s where "
"s.node_id=i.node_id and "
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment