Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Contribution] qvm-expose-port #4028

Open
niccokunzmann opened this issue Jun 22, 2018 · 22 comments
Open

[Contribution] qvm-expose-port #4028

niccokunzmann opened this issue Jun 22, 2018 · 22 comments
Labels
C: contrib package C: networking community dev This is being developed by a member of the community rather than a core Qubes developer. P: default Priority: default. Default priority for new issues, to be replaced given sufficient information. S: needs review Status: needs review. Core devs must review contributed code for potential inclusion in Qubes OS. T: enhancement Type: enhancement. A new feature that does not yet exist or improvement of existing functionality.

Comments

@niccokunzmann
Copy link
Contributor

niccokunzmann commented Jun 22, 2018

Community Dev: @niccokunzmann
PoC: https://github.com/niccokunzmann/qvm-expose-port


Qubes OS version:

R3.2

Affected component(s):

dom0


Steps to reproduce the behavior:

Expected behavior:

I would like to expose a port from a VM to the outside.

Actual behavior:

I can read a long documentation and may fail.

General notes:

I implemented a script qvm-expose-port to do the work for me and
published it on GitHub.

I am offering it to you and would like to know if it is useful to add this to Qubes.
I am using it because I develop servers I would like to share.


Related issues:

@andrewdavidwong
Copy link
Member

andrewdavidwong commented Jun 22, 2018

Thanks for your contribution! Please see the Package Contributions page for the package contribution procedure.

@andrewdavidwong andrewdavidwong changed the title command to expose VM port to outside networks [Contribution] qvm-expose-port Jun 22, 2018
@andrewdavidwong andrewdavidwong added T: enhancement Type: enhancement. A new feature that does not yet exist or improvement of existing functionality. C: other labels Jun 22, 2018
@andrewdavidwong andrewdavidwong added this to the Far in the future milestone Jun 22, 2018
niccokunzmann added a commit to niccokunzmann/qubes-doc that referenced this issue Jun 24, 2018
QubesOS/qubes-issues#4028

The purpose of adding this is to get more attention about
adding a command and possible contributors.
@tasket
Copy link

tasket commented Jun 24, 2018

Not sure if its complementary, but @jpouellet also has a port-forwarding script:

"Forwards a specified port to a specified VM, auto-detecting its NetVM chain. (Qubes OS)"
https://gist.github.com/jpouellet/d8cd0eb8589a5b9bf0c53a28fc530369

@niccokunzmann
Copy link
Contributor Author

niccokunzmann commented Jun 24, 2018

@tasket Thanks! Posting this clarifies that I should use qvm-run -u root as pointed out in niccokunzmann/qvm-expose-port#1

Also, I like to point out that they know

  • how to recurse
  • how to get the IP without grep

@jpouellet
Copy link
Contributor

jpouellet commented Jun 25, 2018

If people actually want this, then IMO we should integrate it properly rather than as a fragile script in an optional package, and make it portable across different potential NetVMs/ProxyVMs (e.g. not make assumptions about the use or even availability of iptables).

I believe the correct way to implement this would be via a new "forward" action type for firewall rules. We already have infrastructure to handle reading/writing these rules, and the rules' intent are already provided to guests in a mostly-guest-OS-agnostic way via qubesdb, and we already have infrastructure in the linux agent for detecting changes to this exposed information and triggering idempotent iptables updates from them.

Some open questions are then:

  • how to handle the case where multiple VMs both have rules requesting the same port be forwarded to them? One possible solution may be for adding a forwarding rule for a VM to remove all rules for the same port on other VMs with the same NetVM, but it's not clear to me that this is the best way.
  • how to express mappings from one port to a different one (e.g. 80->8080)? The firewall rules at the moment only have a single port range field (internally referred to as dstports). Should we overload the semantics of a port range to mean lower bound is the port in the ingress NetVM side and the upper bound is the port on the VM providing the service? Should we add another field instead?

@jpouellet
Copy link
Contributor

jpouellet commented Jun 25, 2018

There is also the issue of direct recursive forwarding up the NetVM chain not being appropriate for all VMs. For example, if some VM is behind a ProxyVM which e.g. transparently tunnels all traffic at layer 2, then a port forward on that ProxyVM's NetVM does not make sense since the ports to be forwarded only exist on the overlay network (inside the proxy tunnel). Some way of denoting that forwarding should stop is required. It seems the introduction of a qvm-features flag would be a good way to express this. ProxyVMs which only act as firewalls (e.g. sys-firewall) could set this flag to mean "establish further forwarding firewall rules in NetVMs upstream of me", or not set it to not do so.

I believe the least-surprising-to-users way to introduce this would be a positive feature saying "do forward upstream" rather than a negative feature saying "do not foward upstream", and setting the feature by default on sys-firewall. This way people with existing ProxyVMs do not suddenly get ports forwarded from their NetVM to their ProxyVM as a surprise, but must be aware of this feature and manually opt in on the ProxyVMs for which it makes sense for them.

Following from this, creating a forwarding rule in a particular VM could automatically create corresponding forwarding rules in upstream NetVMs so long as they have the "auto-port-forward" (or "propagate-port-mappings", or whatever) flag set. This would eliminate the need for a separate port-forwarding tool altogether, and allow easy integration into firewall-management GUIs such as qubes-manager without duplicating netvm-recursion logic, and keeping all semantics contained in the core firewall handling logic.

@jpouellet
Copy link
Contributor

jpouellet commented Jun 25, 2018

OTOH, I suspect such a mechanism would be R4+ only since in R3.2 the firewall rules were not provided in a guest-agnostic format (although I suppose they could be shoved into the provided iptables rules anyway) and IIRC qube feature flags did not exist. So, I see nothing wrong with wanting to package up a short script without forward-compatibility guarantees for R3.2 users.

@jpouellet
Copy link
Contributor

Shoutout to @Joeviocoe since (although I have not reviewed/audited it) his script is probably a better starting point than mine.

@niccokunzmann
Copy link
Contributor Author

@jpouellet Since I am new to the code, I would appreciate more links to the source and implementation you are writing about. I can I agree because I do not know what you are talking about but I am never sure even if I found code. Can you create links to these?

Answers:

how to handle the case where multiple VMs both have rules requesting the same port be forwarded to them?

I would expect an error with no change being done, if I request to forward a port which is already forwarded.

how to express mappings from one port to a different one (e.g. 80->8080)?

This is not an important use case for now in my opinion. If one desires to re-map, one can get a proxy. The basic use-case is to allow people to expose ports in general. Once there are more people who have port conflicts, they can join in creating this more advanced case. However, if we create database entries and such, we can already keep in mind that the incoming port is not the outgoing port.

@jpouellet
Copy link
Contributor

jpouellet commented Jun 25, 2018

for qubesdb, see:

It's basically a hierarchical key-value store with roughly equivalent semantics to xenstore. If you're not familiar with it, it's pretty simple. Only thing that may be non-obvious is that updates are passed as messages, so you can observe writes even if they are the same value as was there previously. This behavior can be used for generic notification of events without polling at some interval or relying on another out-of-band mechanism, for example to notify that some other set of non-atomic changes has been completed and state is now consistent and can be acted on, for example in the case of firewall rules spread over multiple key/value pairs in the store.

Other functionality is built on top of this, for example getting the firewall rules from entries under /qubesdb-firewall/${vm_ip}/... in R4 (or /qubes-iptables-domainrules/${domain} in R3, still present for backwards compat in R4 but deprecated) and being notified that the rules have been updated and that the vm should install handle them somehow (e.g. install them into the kernel via iptables) via watching for empty writes to /qubesdb-firewall/${vm_ip} (or write of reload to /qubesdb-iptables in R3).


as for infrastructure for handling firewall rules, it's nothing special. I'm just referring to the fact that we already have a qvm-firewall tool, rules accessible under qubesadmin.Qubes().domains[somevm].firewall.rules, stored in /var/lib/qubes/appvms/somevm/firewall.xml (for now, maybe to be merged into qubes.xml at some point), modifyable via the qubes-manager gui (albeit with some limitations), etc.


Useful entry-points for reading the relevant code would be:

Thanks for being interested :)

@tlaurion
Copy link
Contributor

tlaurion commented Oct 18, 2018

I think the qvm-expose-port command should permit from which netvm (-s) on we want that port and protocol to be available.

The use case I have in mind is for the exposition of services only from a defined VPN netvm or sys-whonix for hidden tor services access, not opening everything up from the firewall-vm up to the destination qube.

example:
qvm-expose-port -a work 8000 -s sys-whonix

@jpouellet @niccokunzmann @tasket @Joeviocoe @daktak: which project aims to go mainstream and be packaged?

@niccokunzmann
Copy link
Contributor Author

niccokunzmann commented Oct 18, 2018 via email

@zby
Copy link

zby commented Nov 8, 2018

I am not a bash programmer - so I am ready to remove this comment - but I think there is a major problem with this script.

There is an error function that is called whenever there is a serious error and it exits the script, but then the functions are called in backticks - so that exit is executed in a subshell (I think - I am not an expert here - but see below) and the main script does not exit on errors but goes on executing the rest possibly doing a lot of harm.

A simplified example:

#!/bin/bash

set -e

function error() {
  echo "ERROR: $@" >&2
  exit 1
  echo "AFTER EXIT"
}

function get_vm_ip() {
  error "SOME ERROR"
}

function expose_port() {
  local TARGET_VM_IP="`get_vm_ip`"
  echo "AFTER ERROR"
}

expose_port

exit 0

The effect of running this is:

user@personal:~/Downloads$ sh ~/QubesIncoming/dom0/aaa.sh 
ERROR: SOME ERROR
AFTER ERROR

Update: added 'set -e' line - but it did not change the output - as explained by marmarek below.

@marmarek
Copy link
Member

marmarek commented Nov 8, 2018

In theory there is set -e, which should also terminate main script if any command (including backticks) fails, but usage of local bypass this effect.
So, right now it relies on exit_if_empty after each such assignment. One need to be very careful to not miss any such case...

@rustybird
Copy link

rustybird commented Nov 9, 2018

BTW this can be avoided by decoupling a local variable's declaration from its definition when the definition uses command substitution:

local foo
foo=`bar`

The same goes for export or any other command.

@andrewdavidwong andrewdavidwong added community dev This is being developed by a member of the community rather than a core Qubes developer. P: default Priority: default. Default priority for new issues, to be replaced given sufficient information. S: needs review Status: needs review. Core devs must review contributed code for potential inclusion in Qubes OS. labels Jun 9, 2019
@andrewdavidwong andrewdavidwong modified the milestones: Far in the future, Release 4.1 Jun 9, 2019
@Osndok
Copy link

Osndok commented Sep 5, 2020

If anyone is interested, I have started collecting & building upon the work presented here.

Of particular note:

  • The exposed port can be made permanent with a "--perm" flag (it modifies the rc.local files in a clean-ish way)
  • It's up-to-date (as presented, the other scripts did not work for me due to minor usage drift over time)

https://github.com/Osndok/qvm-expose-port/blob/master/qvm-expose-port

@fepitre
Copy link
Member

fepitre commented Sep 6, 2020

Maybe we should do some common one with #5693. @marmarek

@Osndok
Copy link

Osndok commented Sep 6, 2020

Thanks @fepitre I did not see that. I've added those gists to an alt branch to track them, but I'm not a huge fan of putting a glob of unreadable iptables rules into the rc.local... it's one of those things where accidentally deleting a stray character will break things in ways that are nearly-unnoticable and/or hard to diagnose.

@tlaurion
Copy link
Contributor

tlaurion commented Sep 6, 2020

I would love to bring back to everybody's attention the work done under Qubesos Network server : https://github.com/Rudd-O/qubes-network-server

Having Qubesos firewall frontend to have rules -from, and cascading rules application from netvm to appvm would be totally awesome. Look at the issues there.

This project needs collaboration, which would ease user experience.

@fepitre
Copy link
Member

fepitre commented Sep 7, 2020

@tlaurion I think the scope of those scripts is mostly for straightforward/temp. VM exposure. IMHO, using https://github.com/Rudd-O/qubes-network-server is much more for advanced/recurrent setup than simply exposing one port. I've encountered the case where I needed to easily forward external to multiples AppVMs and calling the qvm-portfwd-iptables tool was the easiest solution for me. Depending on available features and project status, it's certainly worth to add qubes-network-server to QubesOS-contrib.

@andrewdavidwong
Copy link
Member

Please note that https://github.com/Rudd-O/qubes-network-server already has its own open issue: #5088

@Tehvan
Copy link

Tehvan commented May 25, 2023

I'm using the 'in.sh' script by unman on R4.1.2.
https://github.com/unman/stuff/

I tried to use the script by fepitre (https://gist.github.com/fepitre/941d7161ae1150d90e15f778027e3248) but I couldn't get it working.

Update: Fepitre's script (with Istador's fix) is working fine and allows persistance. I just had to install net-tools in all the relevant qubes since ip is now the default package instead of net-tools.

@andrewdavidwong andrewdavidwong modified the milestones: Release 4.2, Release TBD Jun 26, 2023
@andrewdavidwong andrewdavidwong removed this from the Release TBD milestone Aug 13, 2023
@ben-grande
Copy link

ben-grande commented Jan 19, 2024

R4.2 with nftables script based on Fepitre and previous contributors: qvm-port-forward, provided as is.

It could be improved... such as sanitizing and validating data coming from DomU and deal with disposable sys-net combined with disposable sys-firewall, which is not dealt with currently.

@marmarek marmarek removed their assignment Mar 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C: contrib package C: networking community dev This is being developed by a member of the community rather than a core Qubes developer. P: default Priority: default. Default priority for new issues, to be replaced given sufficient information. S: needs review Status: needs review. Core devs must review contributed code for potential inclusion in Qubes OS. T: enhancement Type: enhancement. A new feature that does not yet exist or improvement of existing functionality.
Projects
None yet
Development

No branches or pull requests

13 participants