Looking to edit rules.ml of my mirage-firewall VM but since I cannot run shell, IDK what to do

101 views
Skip to first unread message

Sphere

unread,
Apr 9, 2019, 10:12:25 PM4/9/19
to qubes-users
So I have now also boarded the mirage-firewall VM hype to replace sys-firewall in order to take advantage of the very nice small memory consumption of just 32 MB

After searching around I literally failed to find anything that could help me know how I'm gonna edit rules.ml in the mirage-firewall VM

The VM as it is right now is running on fedora-29 and trying to launch gnome-terminal/xterm in the VM using qvm-run returns with the error code that I usually get when it doesn't recognize the command/command does not exist in the VM at all

May I ask for any leads in getting through this?

unman

unread,
Apr 10, 2019, 6:44:56 AM4/10/19
to qubes-users
Unless I've missed something in the announcements, you have to set the rules in
rules.ml and then recompile.
Also, the firewall does *NOT* yet respond to changes set at the qube
level.

Happy to be corrected on this.

Thomas Leonard

unread,
Apr 10, 2019, 2:42:04 PM4/10/19
to qubes-users

mirage-firewall doesn't use a hard disk, so being based on the fedora-29 template doesn't make any difference (it won't load anything from it). It's not Linux. There's no terminal, no graphics system, and no bash. Not even /bin/sh. In fact, there's no filesystem, so no "/" at all.

Commands are handled here: https://github.com/mirage/qubes-mirage-firewall/blob/master/command.ml - as you can (maybe) see, it only supports SetDateTime and WaitForSession commands by default.

To change the rules, you edit rules.ml, rebuild and redeploy (this should only take a couple of seconds after the first build).

Rules.from_client is an OCaml function that takes one argument giving information about an IP packet as input, and returns an action saying what to do about it. The type of this argument is specified in https://github.com/mirage/qubes-mirage-firewall/blob/master/packet.ml

To get a quick overview of OCaml syntax, try https://try.ocamlpro.com/
For lots of details: http://dev.realworldocaml.org/

I believe some people are planning to add support for reading the rules from QubesDB so you can instead configure the firewall interactively with the dom0 GUI, like you can for sys-firewall. I'm not sure what the status of that work is, though.

799

unread,
Apr 10, 2019, 3:16:32 PM4/10/19
to Thomas Leonard, qubes-users
Hello,

Thomas Leonard <tal...@gmail.com> schrieb am Mi., 10. Apr. 2019, 20:42:
(...)

To change the rules, you edit rules.ml, rebuild and redeploy (this should only take a couple of seconds after the first build).
(...)

Can you or someone from the mirage fw for Qubes team give some examples how to write rules for mirage?

Examples:

1) <AppVM1> can access <AppVM2> via ssh
2) <AppVM3> can reach <InternetHost1> using <Port1> via TCP
3) Block access from <AppVM4> to <InternetHost2> 

I think some example rules will make it easier to understand how to write rules.

Regarding rebuilding and redployment:
Maybe we can write a small script that will do the following:

- launch mirage build VM
- apply changes to rules.ml
- rebuild
- copy new kernel files back to dom0
- shutdown mirage build VM
- restart mirage firewall proxyVM

The easiest procedure would be to keep the rules.ml in dom0, edit it there and then qvm-copy or qvm-run --pass-io cat ... it to the mirage build VM.

-O/799

Sphere

unread,
Apr 10, 2019, 11:16:17 PM4/10/19
to qubes-users
@unman Thanks for the clarification. I suppose I misunderstood it wrong since I thought you have to set it directly using some sort of text editor and be done with it. So I'll have to recompile it I see, welp guess I have no choice but go through with that haha
I second this idea. I'm having a hard time myself trying to absorb the very raw instructions of making rules in the rules.ml

While the added convenience expands the surface of attack by a bit, I think this can be very useful in environments where you have to frequently interact with firewall rules.

Also got questions about makings rules in rules.ml

let from_client = function
| { 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"

Does `NAT_to (`NetVM, 53) mean that NAT will be applied to the outgoing packet then NetVM itself will process the DNS Query within its own VM context? If this is right, then configuring a wrong DNS server within NetVM would essentially mean DNS resolutions will fail right?

Or is this because the rule { dst = `Client_gateway; proto = `UDP { dport = 53 } } -> `NAT_to (`NetVM, 53) is intended for internal DNS resolutions? (From my own understanding, that seems to be the case but I'd like to be corrected if this rule really is for internet DNS resolutions)

Moving forward, if I have no lapses in understanding the guidelines in making rules, then this must be the ruleset for allowing only outgoing traffic towards port 25, 110, and 143:

let from_client = function
| { dst = (`External _ | `NetVM); proto = `TCP { dport = 25, 110, 143 } } -> `NAT
| { dst = (`Client_gateway | `Firewall_uplink) } -> `Drop "packet addressed to firewall itself"
| { dst = `Client _ } -> `Drop "prevent communication between client VMs"

I also want to know why there is an underscore in front of `External and `Client

Thomas Leonard

unread,
Apr 11, 2019, 8:02:33 AM4/11/19
to qubes-users
On Thursday, April 11, 2019 at 4:16:17 AM UTC+1, Sphere wrote:
> @unman Thanks for the clarification. I suppose I misunderstood it wrong since I thought you have to set it directly using some sort of text editor and be done with it. So I'll have to recompile it I see, welp guess I have no choice but go through with that haha
>
> On Thursday, April 11, 2019 at 3:16:32 AM UTC+8, 799 wrote:
> > Hello,
> >
> >
> >
> > Thomas Leonard <tal...@gmail.com> schrieb am Mi., 10. Apr. 2019, 20:42:
> > (...)
> >
> > To change the rules, you edit rules.ml, rebuild and redeploy (this should only take a couple of seconds after the first build).
> >
> >
> > (...)
> >
> >
> >
> > Can you or someone from the mirage fw for Qubes team give some examples how to write rules for mirage?
> >
> >
> > Examples:
> >
> >
> > 1) <AppVM1> can access <AppVM2> via ssh
> > 2) <AppVM3> can reach <InternetHost1> using <Port1> via TCP
> > 3) Block access from <AppVM4> to <InternetHost2> 
> >
> > I think some example rules will make it easier to understand how to write rules.

I've added some examples at https://github.com/mirage/qubes-mirage-firewall/pull/54 (see the changes to rules.ml).

Actually, matching on individual machines was a bit ugly, so I also made some changes to let you name all the machines you want to refer at the start of the rules file. You'll need those changes too for the new examples to work.

> > Regarding rebuilding and redployment:
> > Maybe we can write a small script that will do the following:
> >
> >
> > - launch mirage build VM
> > - apply changes to rules.ml
> > - rebuild
> > - copy new kernel files back to dom0
> > - shutdown mirage build VM
> > - restart mirage firewall proxyVM

See: https://github.com/mirage/qubes-mirage-firewall/#easy-deployment-for-developers

e.g. I build and deploy the firewall from my dev VM with:

[dev]$ make && test-mirage qubes_firewall.xen mirage-firewall

It does what you describe, and also tails the log file so you can see it from the build VM. The process is triggered from the build VM rather than from dom0 because working in dom0 is risky. There is a policy so that only the builder VM can push the kernel, and only the mirage-firewall kernel can be updated.

Note that the instructions for test-mirage show how to set up a "mirage-test" unikernel. You'll need to use "mirage-firewall" as the name instead.

> I second this idea. I'm having a hard time myself trying to absorb the very raw instructions of making rules in the rules.ml
>
> While the added convenience expands the surface of attack by a bit, I think this can be very useful in environments where you have to frequently interact with firewall rules.
>
> Also got questions about makings rules in rules.ml
>
> let from_client = function
> | { 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"
>
> Does `NAT_to (`NetVM, 53) mean that NAT will be applied to the outgoing packet then NetVM itself will process the DNS Query within its own VM context? If this is right, then configuring a wrong DNS server within NetVM would essentially mean DNS resolutions will fail right?

Yes. Client AppVMs are by default configured to use the firewall as their DNS (check your /etc/resolv.conf). The firewall then just forwards these requests to sys-net.

> Or is this because the rule { dst = `Client_gateway; proto = `UDP { dport = 53 } } -> `NAT_to (`NetVM, 53) is intended for internal DNS resolutions? (From my own understanding, that seems to be the case but I'd like to be corrected if this rule really is for internet DNS resolutions)
>
> Moving forward, if I have no lapses in understanding the guidelines in making rules, then this must be the ruleset for allowing only outgoing traffic towards port 25, 110, and 143:
>
> let from_client = function
> | { dst = (`External _ | `NetVM); proto = `TCP { dport = 25, 110, 143 } } -> `NAT

Nearly: dport = (25 | 110 | 143)

> | { dst = (`Client_gateway | `Firewall_uplink) } -> `Drop "packet addressed to firewall itself"
> | { dst = `Client _ } -> `Drop "prevent communication between client VMs"
>
> I also want to know why there is an underscore in front of `External and `Client

That space contains information about which client or external machine it is. "_" matches anything.

799

unread,
Apr 12, 2019, 7:24:47 PM4/12/19
to Thomas Leonard, qubes-users
Hello Thomas.

On Thu, 11 Apr 2019 at 14:02, Thomas Leonard <tal...@gmail.com> wrote:
[...]

I've added some examples at https://github.com/mirage/qubes-mirage-firewall/pull/54 (see the changes to rules.ml).

Thanks a lot for your excellent work and adding more information to mirage-firewall, greatly appreciated.
Regarding the example rules:

| { src = `Client `Dev; dst = `Client `Untrusted; proto = `TCP { dport = 22 } } -> `Accept
| { dst = `External `GoogleDNS } -> `Drop "block Google DNS"

these two rules are easy to understand and will help me setting up rules between the AppVms


| { src = `Client _; dst = `Client _; proto = `TCP _; packet }
when not (is_tcp_start packet) -> `Accept

Can you add more details about this rules, its an any-to-any rule, but what is "when not (is_tcp_start packet) ?

- O/799

Thomas Leonard

unread,
Apr 13, 2019, 5:02:04 AM4/13/19
to qubes-users

The Dev->Untrusted:ssh rule allows TCP packets from Dev to Untrusted:ssh, but you also need to allow packets in the other direction. There are various ways to do that; this example rule allows any TCP packet that isn't trying to start a new TCP connection to go between AppVMs, which should do the trick.

You don't need this for external sites because the `NAT action also adds a rule that translates the responses, and that automatically allows the responses through. But `Accept just lets the packet through without remembering anything about the new connection.

is_tcp_start is defined in packet.ml. It means the same as Packet.is_tcp_start, but we can leave off the module name because of the "open Packet" at the top of rules.ml.

(* 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

People who know more than me about TCP can probably suggest better ways to do this.

Sphere

unread,
Apr 22, 2019, 1:14:07 AM4/22/19
to qubes-users
Thank you very much for the sweet details! Sorry if I only got to reply now as I was on vacation
Reply all
Reply to author
Forward
0 new messages