Merge pull request from talex5/rule-examples

Allow naming hosts and add examples to rules.ml
This commit is contained in:
Thomas Leonard 2019-05-07 10:03:42 +01:00 committed by GitHub
commit a93bb954d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 305 additions and 47 deletions

2
.gitignore vendored

@ -1,4 +1,4 @@
Makefile
/Makefile
_build/
log
key_gen.ml

@ -86,6 +86,29 @@ qvm-prefs --set my-app-vm netvm mirage-firewall
Alternatively, you can configure `mirage-firewall` to be your default firewall VM.
### Components
This diagram show the main components (each box corresponds to a source `.ml` file with the same name):
<p align='center'>
<img src="./diagrams/components.svg"/>
</p>
Ethernet frames arrives from client qubes (such as `work` or `personal`) or from `sys-net`.
Internet (IP) packets are sent to `firewall`, which consults `rules` to decide what to do with the packet.
If it should be sent on, it uses `router` to send it to the chosen destination.
`client_net` watches the XenStore database provided by dom0
to find out when clients need to be added or removed.
The boot process:
- `config.ml` describes the libraries used and static configuration settings (NAT table size).
The `mirage` tool uses this to generate `main.ml`.
- `main.ml` initialises the drivers selected by `config.ml`
and calls the `start` function in `unikernel.ml`.
- `unikernel.ml` connects the Qubes agents, sets up the networking components,
and then waits for a shutdown request.
### Easy deployment for developers
For development, use the [test-mirage][] scripts to deploy the unikernel (`qubes_firewall.xen`) from your development AppVM.

@ -5,5 +5,5 @@ docker build -t qubes-mirage-firewall .
echo Building Firewall...
docker run --rm -i -v `pwd`:/home/opam/qubes-mirage-firewall qubes-mirage-firewall
echo "SHA2 of build: $(sha256sum qubes_firewall.xen)"
echo "SHA2 last known: dbf7460fa628bea5d132a96fe7ba2cd832e3d9da7005ae74f6a124957f4848ea"
echo "SHA2 last known: 888cfd66e54c14da75be2bc4272efdb74c2ec8f9f144979f508a09410121482e"
echo "(hashes should match for released versions)"

@ -56,7 +56,7 @@ let input_arp ~fixed_arp ~iface request =
iface#writev `ARP (fun b -> Arp_packet.encode_into response b; Arp_packet.size)
(** Handle an IPv4 packet from the client. *)
let input_ipv4 ~client_ip ~router packet =
let input_ipv4 ~iface ~router packet =
match Nat_packet.of_ipv4_packet packet with
| Error e ->
Log.warn (fun f -> f "Ignored unknown IPv4 message: %a" Nat_packet.pp_error e);
@ -64,10 +64,10 @@ let input_ipv4 ~client_ip ~router packet =
| Ok packet ->
let `IPv4 (ip, _) = packet in
let src = ip.Ipv4_packet.src in
if src = client_ip then Firewall.ipv4_from_client router packet
if src = iface#other_ip then Firewall.ipv4_from_client router ~src:iface packet
else (
Log.warn (fun f -> f "Incorrect source IP %a in IP packet from %a (dropping)"
Ipaddr.V4.pp src Ipaddr.V4.pp client_ip);
Ipaddr.V4.pp src Ipaddr.V4.pp iface#other_ip);
return ()
)
@ -94,7 +94,7 @@ let add_vif { Dao.ClientVif.domid; device_id } ~client_ip ~router ~cleanup_tasks
| Ok (eth, payload) ->
match eth.Ethernet_packet.ethertype with
| `ARP -> input_arp ~fixed_arp ~iface payload
| `IPv4 -> input_ipv4 ~client_ip ~router payload
| `IPv4 -> input_ipv4 ~iface ~router payload
| `IPv6 -> return () (* TODO: oh no! *)
)
>|= or_raise "Listen on client interface" Netback.pp_error

6
diagrams/Makefile Normal file

@ -0,0 +1,6 @@
# Requires https://github.com/blampe/goat
all: components.svg
%.svg: %.txt
goat $^ > $@

149
diagrams/components.svg Normal file

@ -0,0 +1,149 @@
<svg class='diagram' xmlns='http://www.w3.org/2000/svg' version='1.1' height='329' width='600'>
<g transform='translate(8,16)'>
<path d='M 272,0 L 360,0' style='fill:none;stroke:#000;'></path>
<path d='M 272,32 L 360,32' style='fill:none;stroke:#000;'></path>
<path d='M 120,96 L 224,96' style='fill:none;stroke:#000;'></path>
<path d='M 272,96 L 320,96' style='fill:none;stroke:#000;'></path>
<path d='M 320,96 L 360,96' style='fill:none;stroke:#000;'></path>
<path d='M 72,112 L 112,112' style='fill:none;stroke:#000;'></path>
<path d='M 224,112 L 264,112' style='fill:none;stroke:#000;'></path>
<path d='M 368,112 L 440,112' style='fill:none;stroke:#000;'></path>
<path d='M 272,128 L 320,128' style='fill:none;stroke:#000;'></path>
<path d='M 320,128 L 360,128' style='fill:none;stroke:#000;'></path>
<path d='M 400,144 L 440,144' style='fill:none;stroke:#000;'></path>
<path d='M 440,144 L 472,144' style='fill:none;stroke:#000;'></path>
<path d='M 72,160 L 112,160' style='fill:none;stroke:#000;'></path>
<path d='M 480,160 L 520,160' style='fill:none;stroke:#000;'></path>
<path d='M 400,176 L 472,176' style='fill:none;stroke:#000;'></path>
<path d='M 272,192 L 360,192' style='fill:none;stroke:#000;'></path>
<path d='M 72,208 L 112,208' style='fill:none;stroke:#000;'></path>
<path d='M 232,208 L 272,208' style='fill:none;stroke:#000;'></path>
<path d='M 360,208 L 440,208' style='fill:none;stroke:#000;'></path>
<path d='M 120,224 L 176,224' style='fill:none;stroke:#000;'></path>
<path d='M 176,224 L 224,224' style='fill:none;stroke:#000;'></path>
<path d='M 272,224 L 360,224' style='fill:none;stroke:#000;'></path>
<path d='M 120,96 L 120,224' style='fill:none;stroke:#000;'></path>
<path d='M 176,224 L 176,272' style='fill:none;stroke:#000;'></path>
<path d='M 224,96 L 224,112' style='fill:none;stroke:#000;'></path>
<path d='M 224,112 L 224,224' style='fill:none;stroke:#000;'></path>
<path d='M 272,0 L 272,32' style='fill:none;stroke:#000;'></path>
<path d='M 272,96 L 272,128' style='fill:none;stroke:#000;'></path>
<path d='M 272,192 L 272,208' style='fill:none;stroke:#000;'></path>
<path d='M 272,208 L 272,224' style='fill:none;stroke:#000;'></path>
<path d='M 320,48 L 320,96' style='fill:none;stroke:#000;'></path>
<path d='M 320,128 L 320,176' style='fill:none;stroke:#000;'></path>
<path d='M 360,0 L 360,32' style='fill:none;stroke:#000;'></path>
<path d='M 360,96 L 360,128' style='fill:none;stroke:#000;'></path>
<path d='M 360,192 L 360,208' style='fill:none;stroke:#000;'></path>
<path d='M 360,208 L 360,224' style='fill:none;stroke:#000;'></path>
<path d='M 400,144 L 400,176' style='fill:none;stroke:#000;'></path>
<path d='M 440,112 L 440,144' style='fill:none;stroke:#000;'></path>
<path d='M 440,192 L 440,208' style='fill:none;stroke:#000;'></path>
<path d='M 472,144 L 472,176' style='fill:none;stroke:#000;'></path>
<polygon points='80.000000,112.000000 68.000000,106.400002 68.000000,117.599998' style='fill:#000' transform='rotate(180.000000, 72.000000, 112.000000)'></polygon>
<polygon points='80.000000,160.000000 68.000000,154.399994 68.000000,165.600006' style='fill:#000' transform='rotate(180.000000, 72.000000, 160.000000)'></polygon>
<polygon points='80.000000,208.000000 68.000000,202.399994 68.000000,213.600006' style='fill:#000' transform='rotate(180.000000, 72.000000, 208.000000)'></polygon>
<polygon points='120.000000,112.000000 108.000000,106.400002 108.000000,117.599998' style='fill:#000' transform='rotate(0.000000, 112.000000, 112.000000)'></polygon>
<polygon points='120.000000,160.000000 108.000000,154.399994 108.000000,165.600006' style='fill:#000' transform='rotate(0.000000, 112.000000, 160.000000)'></polygon>
<polygon points='120.000000,208.000000 108.000000,202.399994 108.000000,213.600006' style='fill:#000' transform='rotate(0.000000, 112.000000, 208.000000)'></polygon>
<polygon points='184.000000,272.000000 172.000000,266.399994 172.000000,277.600006' style='fill:#000' transform='rotate(90.000000, 176.000000, 272.000000)'></polygon>
<polygon points='240.000000,208.000000 228.000000,202.399994 228.000000,213.600006' style='fill:#000' transform='rotate(180.000000, 232.000000, 208.000000)'></polygon>
<polygon points='272.000000,112.000000 260.000000,106.400002 260.000000,117.599998' style='fill:#000' transform='rotate(0.000000, 264.000000, 112.000000)'></polygon>
<path d='M 320,40 L 320,48' style='fill:none;stroke:#000;'></path>
<polygon points='336.000000,48.000000 324.000000,42.400002 324.000000,53.599998' style='fill:#000' transform='rotate(270.000000, 320.000000, 48.000000)'></polygon>
<path d='M 320,176 L 320,184' style='fill:none;stroke:#000;'></path>
<polygon points='336.000000,176.000000 324.000000,170.399994 324.000000,181.600006' style='fill:#000' transform='rotate(90.000000, 320.000000, 176.000000)'></polygon>
<polygon points='376.000000,112.000000 364.000000,106.400002 364.000000,117.599998' style='fill:#000' transform='rotate(180.000000, 368.000000, 112.000000)'></polygon>
<path d='M 440,184 L 440,192' style='fill:none;stroke:#000;'></path>
<polygon points='456.000000,192.000000 444.000000,186.399994 444.000000,197.600006' style='fill:#000' transform='rotate(270.000000, 440.000000, 192.000000)'></polygon>
<polygon points='488.000000,160.000000 476.000000,154.399994 476.000000,165.600006' style='fill:#000' transform='rotate(180.000000, 480.000000, 160.000000)'></polygon>
<polygon points='528.000000,160.000000 516.000000,154.399994 516.000000,165.600006' style='fill:#000' transform='rotate(0.000000, 520.000000, 160.000000)'></polygon>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='144' y='164' style='fill:#000;font-size:1em'>l</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='544' y='164' style='fill:#000;font-size:1em'>y</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='552' y='164' style='fill:#000;font-size:1em'>s</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='216' y='260' style='fill:#000;font-size:1em'>t</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='456' y='164' style='fill:#000;font-size:1em'>k</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='16' y='212' style='fill:#000;font-size:1em'>r</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='312' y='212' style='fill:#000;font-size:1em'>u</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='312' y='20' style='fill:#000;font-size:1em'>l</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='168' y='164' style='fill:#000;font-size:1em'>n</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='184' y='164' style='fill:#000;font-size:1em'>_</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='296' y='20' style='fill:#000;font-size:1em'>r</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='152' y='164' style='fill:#000;font-size:1em'>i</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='8' y='212' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='56' y='212' style='fill:#000;font-size:1em'>l</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='224' y='260' style='fill:#000;font-size:1em'>o</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='168' y='292' style='fill:#000;font-size:1em'>n</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='56' y='116' style='fill:#000;font-size:1em'>k</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='568' y='164' style='fill:#000;font-size:1em'>n</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='32' y='212' style='fill:#000;font-size:1em'>o</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='40' y='116' style='fill:#000;font-size:1em'>o</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='576' y='164' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='328' y='212' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='160' y='292' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='432' y='164' style='fill:#000;font-size:1em'>l</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='536' y='164' style='fill:#000;font-size:1em'>s</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='184' y='292' style='fill:#000;font-size:1em'>t</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='160' y='308' style='fill:#000;font-size:1em'>(</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='288' y='116' style='fill:#000;font-size:1em'>f</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='424' y='164' style='fill:#000;font-size:1em'>p</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='440' y='164' style='fill:#000;font-size:1em'>i</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='208' y='260' style='fill:#000;font-size:1em'>i</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='192' y='260' style='fill:#000;font-size:1em'>o</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='320' y='116' style='fill:#000;font-size:1em'>w</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='176' y='164' style='fill:#000;font-size:1em'>t</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='416' y='164' style='fill:#000;font-size:1em'>u</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='448' y='164' style='fill:#000;font-size:1em'>n</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='560' y='164' style='fill:#000;font-size:1em'>-</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='48' y='212' style='fill:#000;font-size:1em'>a</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='304' y='212' style='fill:#000;font-size:1em'>o</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='152' y='292' style='fill:#000;font-size:1em'>X</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='176' y='292' style='fill:#000;font-size:1em'>S</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='200' y='292' style='fill:#000;font-size:1em'>r</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='184' y='308' style='fill:#000;font-size:1em'>m</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='304' y='20' style='fill:#000;font-size:1em'>u</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='352' y='68' style='fill:#000;font-size:1em'>c</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='48' y='116' style='fill:#000;font-size:1em'>r</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='56' y='164' style='fill:#000;font-size:1em'>]</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='200' y='164' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='296' y='212' style='fill:#000;font-size:1em'>r</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='296' y='116' style='fill:#000;font-size:1em'>i</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='200' y='260' style='fill:#000;font-size:1em'>n</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='240' y='260' style='fill:#000;font-size:1em'>s</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='584' y='164' style='fill:#000;font-size:1em'>t</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='344' y='68' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='360' y='68' style='fill:#000;font-size:1em'>k</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='368' y='68' style='fill:#000;font-size:1em'>s</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='32' y='116' style='fill:#000;font-size:1em'>w</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='312' y='116' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='32' y='164' style='fill:#000;font-size:1em'>.</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='192' y='164' style='fill:#000;font-size:1em'>n</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='208' y='292' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='336' y='116' style='fill:#000;font-size:1em'>l</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='232' y='260' style='fill:#000;font-size:1em'>r</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='24' y='212' style='fill:#000;font-size:1em'>s</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='320' y='20' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='328' y='20' style='fill:#000;font-size:1em'>s</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='304' y='116' style='fill:#000;font-size:1em'>r</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='344' y='116' style='fill:#000;font-size:1em'>l</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='24' y='164' style='fill:#000;font-size:1em'>[</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='48' y='164' style='fill:#000;font-size:1em'>.</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='0' y='212' style='fill:#000;font-size:1em'>p</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='40' y='212' style='fill:#000;font-size:1em'>n</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='320' y='212' style='fill:#000;font-size:1em'>t</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='192' y='292' style='fill:#000;font-size:1em'>o</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='176' y='308' style='fill:#000;font-size:1em'>o</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='328' y='68' style='fill:#000;font-size:1em'>c</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='336' y='68' style='fill:#000;font-size:1em'>h</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='40' y='164' style='fill:#000;font-size:1em'>.</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='136' y='164' style='fill:#000;font-size:1em'>c</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='208' y='164' style='fill:#000;font-size:1em'>t</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='184' y='260' style='fill:#000;font-size:1em'>m</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='328' y='116' style='fill:#000;font-size:1em'>a</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='160' y='164' style='fill:#000;font-size:1em'>e</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='336' y='212' style='fill:#000;font-size:1em'>r</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='168' y='308' style='fill:#000;font-size:1em'>d</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='192' y='308' style='fill:#000;font-size:1em'>0</text>
<text text-anchor='middle' font-family='Menlo,Lucida Console,monospace' x='200' y='308' style='fill:#000;font-size:1em'>)</text>
</g>
</svg>

After

(image error) Size: 16 KiB

20
diagrams/components.txt Normal file

@ -0,0 +1,20 @@
+----------+
| rules |
+----------+
^
|checks
|
+------------+ +-----+----+
work <---->| +---->| firewall |<--------.
| | +-----+----+ |
| | | +----+---+
[...] <---->| client_net | | | uplink |<----> sys-net
| | v +--------+
| | +----------+ ^
personal <---->| |<----+ router +---------'
+------+-----+ +----------+
|
|monitors
v
XenStore
(dom0)

@ -48,8 +48,21 @@ let forward_ipv4 t packet =
(* Packet classification *)
let classify t packet =
let `IPv4 (ip, transport) = packet in
let parse_ips ips = List.map (fun (ip_str, id) -> (Ipaddr.of_string_exn ip_str, id)) ips
let clients = parse_ips Rules.clients
let externals = parse_ips Rules.externals
let resolve_client client =
`Client (try List.assoc (Ipaddr.V4 client#other_ip) clients with Not_found -> `Unknown)
let resolve_host = function
| `Client c -> resolve_client c
| `External ip -> `External (try List.assoc ip externals with Not_found -> `Unknown)
| (`Client_gateway | `Firewall_uplink | `NetVM) as x -> x
let classify ~src ~dst packet =
let `IPv4 (_ip, transport) = packet in
let proto =
match transport with
| `TCP ({Tcp.Tcp_packet.src_port; dst_port; _}, _) -> `TCP {sport = src_port; dport = dst_port}
@ -58,8 +71,8 @@ let classify t packet =
in
Some {
packet;
src = Router.classify t (Ipaddr.V4 ip.Ipv4_packet.src);
dst = Router.classify t (Ipaddr.V4 ip.Ipv4_packet.dst);
src;
dst;
proto;
}
@ -80,7 +93,10 @@ let pp_proto fmt = function
| `ICMP -> Format.pp_print_string fmt "ICMP"
| `Unknown -> Format.pp_print_string fmt "UnknownProtocol"
let pp_packet fmt {src; dst; proto; packet = _} =
let pp_packet t fmt {src = _; dst = _; proto; packet} =
let `IPv4 (ip, _transport) = packet in
let src = Router.classify t (Ipaddr.V4 ip.Ipv4_packet.src) in
let dst = Router.classify t (Ipaddr.V4 ip.Ipv4_packet.dst) in
Format.fprintf fmt "[src=%a dst=%a proto=%a]"
pp_host src
pp_host dst
@ -125,18 +141,18 @@ let nat_to t ~host ~port packet =
(* Handle incoming packets *)
let apply_rules t rules info =
let apply_rules t rules ~dst info =
let packet = info.packet in
match rules info, info.dst with
match rules info, dst with
| `Accept, `Client client_link -> transmit_ipv4 packet client_link
| `Accept, (`External _ | `NetVM) -> transmit_ipv4 packet t.Router.uplink
| `Accept, (`Firewall_uplink | `Client_gateway) ->
Log.warn (fun f -> f "Bad rule: firewall can't accept packets %a" pp_packet info);
Log.warn (fun f -> f "Bad rule: firewall can't accept packets %a" (pp_packet t) info);
return ()
| `NAT, _ -> add_nat_and_forward_ipv4 t packet
| `NAT_to (host, port), _ -> nat_to t packet ~host ~port
| `Drop reason, _ ->
Log.info (fun f -> f "Dropped packet (%s) %a" reason pp_packet info);
Log.info (fun f -> f "Dropped packet (%s) %a" reason (pp_packet t) info);
return ()
let handle_low_memory t =
@ -147,7 +163,7 @@ let handle_low_memory t =
`Memory_critical
| `Ok -> Lwt.return `Ok
let ipv4_from_client t packet =
let ipv4_from_client t ~src packet =
handle_low_memory t >>= function
| `Memory_critical -> return ()
| `Ok ->
@ -156,23 +172,28 @@ let ipv4_from_client t packet =
| Some frame -> forward_ipv4 t frame (* Some existing connection or redirect *)
| None ->
(* No existing NAT entry. Check the firewall rules. *)
match classify t packet with
let `IPv4 (ip, _transport) = packet in
let dst = Router.classify t (Ipaddr.V4 ip.Ipv4_packet.dst) in
match classify ~src:(resolve_client src) ~dst:(resolve_host dst) packet with
| None -> return ()
| Some info -> apply_rules t Rules.from_client info
| Some info -> apply_rules t Rules.from_client ~dst info
let ipv4_from_netvm t packet =
handle_low_memory t >>= function
| `Memory_critical -> return ()
| `Ok ->
match classify t packet with
let `IPv4 (ip, _transport) = packet in
let src = Router.classify t (Ipaddr.V4 ip.Ipv4_packet.src) in
let dst = Router.classify t (Ipaddr.V4 ip.Ipv4_packet.dst) in
match classify ~src ~dst:(resolve_host dst) packet with
| None -> return ()
| Some info ->
match info.src with
match src with
| `Client _ | `Firewall_uplink | `Client_gateway ->
Log.warn (fun f -> f "Frame from NetVM has internal source IP address! %a" pp_packet info);
Log.warn (fun f -> f "Frame from NetVM has internal source IP address! %a" (pp_packet t) info);
return ()
| `External _ | `NetVM ->
| `External _ | `NetVM as src ->
translate t packet >>= function
| Some frame -> forward_ipv4 t frame
| None ->
apply_rules t Rules.from_netvm info
apply_rules t Rules.from_netvm ~dst { info with src }

@ -6,6 +6,6 @@
val ipv4_from_netvm : Router.t -> Nat_packet.t -> unit Lwt.t
(** Handle a packet from the outside world (this module will validate the source IP). *)
val ipv4_from_client : Router.t -> Nat_packet.t -> unit Lwt.t
val ipv4_from_client : Router.t -> src:Fw_utils.client_link -> Nat_packet.t -> unit Lwt.t
(** Handle a packet from a client. Caller must check the source IP matches the client's
before calling this. *)

@ -13,9 +13,25 @@ type ports = {
type host =
[ `Client of client_link | `Client_gateway | `Firewall_uplink | `NetVM | `External of Ipaddr.t ]
type info = {
type ('src, 'dst) info = {
packet : Nat_packet.t;
src : host;
dst : host;
src : 'src;
dst : 'dst;
proto : [ `UDP of ports | `TCP of ports | `ICMP | `Unknown ];
}
(* The first message in a TCP connection has SYN set and ACK clear. *)
let is_tcp_start = function
| `IPv4 (_ip, `TCP (hdr, _body)) -> Tcp.Tcp_packet.(hdr.syn && not hdr.ack)
| _ -> false
(* The possible actions we can take for a packet: *)
type action = [
| `Accept (* Send the packet to its destination. *)
| `NAT (* Rewrite the packet's source field so packet appears to
have come from the firewall, via an unused port.
Also, add NAT rules so related packets will be translated accordingly. *)
| `NAT_to of host * port (* As for [`NAT], but also rewrite the packet's
destination fields so it will be sent to [host:port]. *)
| `Drop of string (* Drop the packet and log the given reason. *)
]

@ -1,39 +1,62 @@
(* Copyright (C) 2015, Thomas Leonard <thomas.leonard@unikernel.com>
See the README file for details. *)
(** Put your firewall rules here. *)
(** Put your firewall rules in this file. *)
open Packet
open Packet (* Allow us to use definitions in packet.ml *)
(* List your AppVM IP addresses here if you want to match on them in the rules below.
Any client not listed here will appear as [`Client `Unknown]. *)
let clients = [
(*
"10.137.0.12", `Dev;
"10.137.0.14", `Untrusted;
*)
]
(* List your external (non-AppVM) IP addresses here if you want to match on them in the rules below.
Any external machine not listed here will appear as [`External `Unknown]. *)
let externals = [
(*
"8.8.8.8", `GoogleDNS;
*)
]
(* OCaml normally warns if you don't match all fields, but that's OK here. *)
[@@@ocaml.warning "-9"]
(** {2 Actions}
(** This function decides what to do with a packet from a client VM.
The possible actions are:
It takes as input an argument [info] (of type [Packet.info]) describing the
packet, and returns an action (of type [Packet.action]) to perform.
- [`Accept] : Send the packet to its destination.
See packet.ml for the definitions of [info] and [action].
- [`NAT] : Rewrite the packet's source field so packet appears to
have come from the firewall, via an unused port.
Also, add NAT rules so related packets will be translated accordingly.
- [`NAT_to (host, port)] :
As for [`NAT], but also rewrite the packet's destination fields so it
will be sent to [host:port].
- [`Drop reason] drop the packet and log the reason.
*)
(** Decide what to do with a packet from a client VM.
Note: If the packet matched an existing NAT rule then this isn't called. *)
let from_client = function
let from_client (info : ([`Client of _], _) Packet.info) : Packet.action =
match info with
(* Examples (add your own rules here):
1. Allows Dev to send SSH packets to Untrusted.
Note: responses are not covered by this!
2. Allows Untrusted to reply to Dev.
3. Blocks an external site.
In all cases, make sure you've added the VM name to [clients] or [externals] above, or it won't
match anything! *)
(*
| { src = `Client `Dev; dst = `Client `Untrusted; proto = `TCP { dport = 22 } } -> `Accept
| { src = `Client `Untrusted; dst = `Client `Dev; proto = `TCP _; packet }
when not (is_tcp_start packet) -> `Accept
| { dst = `External `GoogleDNS } -> `Drop "block Google DNS"
*)
| { dst = (`External _ | `NetVM) } -> `NAT
| { dst = `Client_gateway; proto = `UDP { dport = 53 } } -> `NAT_to (`NetVM, 53)
| { dst = (`Client_gateway | `Firewall_uplink) } -> `Drop "packet addressed to firewall itself"
| { dst = `Client _ } -> `Drop "prevent communication between client VMs"
| { dst = `Client _ } -> `Drop "prevent communication between client VMs by default"
(** Decide what to do with a packet received from the outside world.
Note: If the packet matched an existing NAT rule then this isn't called. *)
let from_netvm = function
let from_netvm (info : ([`NetVM | `External of _], _) Packet.info) : Packet.action =
match info with
| _ -> `Drop "drop by default"