-
Notifications
You must be signed in to change notification settings - Fork 326
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
Saml external browser #1217
base: master
Are you sure you want to change the base?
Saml external browser #1217
Conversation
start listening on port 8020 or parameter print a address to open in browser when id received proceed with the authemntication
as i've mentioned in #867, imo this is not a good idea. external programms such as https://github.com/gm-vm/openfortivpn-webview are a cleaner approach to handling this -- maby look into packaging it for your distro instead? |
expose auth-id option
Can you be more explicit this pr use a different method than openfortivpn-webview |
i know that, i've read your code. you are adding a bunch of code for functionality that openfortipvn-webview can already do, but better. if it gets packaged for distros that is actually a much nicer user experience than having to manually run an external browser. that is why i've said that you should look into packaging it instead. |
The advantage is that this solution could use the default browser that keep the session and if it is valid doesn't need to reautenticate. And i think it is easy to integrate in external gui or NetworkManager |
INADDR_ANY does not always work
I hope to be able to test this in the coming weeks. I'm a regular user of code from an unfinished MR in NetworkManager-fortisslvpn which uses the same approach as openfortipvn-webview, with the benefits of NetworkManager integration. While it's a life saver in my situation, I have to say that the approach taken in this PR would be preferable. The VPN is not very stable, and I am often on a flaky uplink, necessitating many reconnections. Typing the password and dealing with 2FA can be quite annoying. We can debate whether this functionality belongs into openfortivpn itself instead of an external tool. I think having it in openfortivpn has the benefit of making it available to all users. For comparison, having openfortivpn-webview available doesn't do much for those of us who need NetworkManager, and while the Gnome version of the NM user interface now has an unfinished reimplementation of what openfortivpn-webview offers, other interfaces (like KDE Plasma) don't have it. In other words, everybody would need to integrate a webview, which is not trivial. On the other hand, if openfortivpn can handle this by itself, adding SAML to NM interfaces is one new checkbox away. The way I see it, this is just another way for openfortivpn to get the cookie, complementing the stdin method. Authentication is delegated to an external tool (i.e., the default browser), same as always. |
Working well on Ubuntu. I do strongly prefer this method because it is a much simpler command that does not require additional packages, nor the awkward pipe into sudo, nor re-authentication because it uses my existing browser session. This is how convoluted my launch command needs to be with openfortivpn-webview: Vs this branch: It is better in every way: quicker to install, easier to use, and saves a good 40 secs by using existing session. |
Hi @LorenzISR
Expanding on my previous comment, the user experience is superior with this PR for the following reasons:
I think the last two items in particular are high value for user experience. I respectfully request you reconsider use cases where this PR adds real value. |
pipes are not "tricks", they are normal features of a shell, and they have always been. you will need sudo anyways, regardless if you use webview or not. i am not using that said, i am not a maintainer but just a random person on the internet who's worked on this problem before and this is just my opinion. |
I'll clarify. If
I thought I read on another discussion that it didn't. Apologies if I am wrong.
|
sudo does not read the password from stdin. i'd argue that re-using the sign-in from the browser might even be a security risk for some and not desired; having to sign in once for your VPN is fine imo and this is also how it works on windows. |
This seems to do what it is described here. That's neat, I don't remember having this option in my official client (and I no longer have access to the VPN to check it now), but this definitely removes the need of using a custom made browser. @LorenzISR this seems to be a feature of the gateway that lets clients use any browser for the authentication. It is different from launching the browser from within openfortivpn. |
@gm-vm that's correct. I use this option in the official mac client. |
If needed I can split this PR |
I am of the opinion this PR would vastly improve all linux user experience for everyone I work with. My default browser session requires me to SAML auth with a second factor each day. Currently I have to do this twice each morning since If the |
Presumably I could build openfortivpn from this filippor:saml-external-take2 branch ? |
or to retrieve the token you could use this project https://github.com/filippor/XdgOpenSaml/tree/main it also use the external browser a binary that not require javacompiled for linux-x86-64 is here |
Without knowing this even existed and driven only by the need of a specific feature I written a solution that kinda solves the same problem, there are few extra features like specifying routes, dns entries and domain prefixes in the config file to override the configuration coming from the server.
I really would not push a "home made browser" over a mainstream browser. and ....
|
Rather than trying to put the browser spawning and web server listening bits into the (run as root) openfortivpn process, why not use the privilege-separated interface already provided for openfortvpn-webview? Make a new process (run unprivileged) that does the SAML dance with the VPN appliance, by shelling out to the user's browser and listening for a response on port 8020, and then outputs the "SVPNCOOKIE=..." string for piping into "sudo openfortivpn --cookie-on-stdin ...". |
The implementation as external program already exist at this repo https://github.com/filippor/XdgOpenSaml. |
@@ -87,6 +87,8 @@ const struct vpn_config invalid_cfg = { | |||
.user_agent = NULL, | |||
.hostcheck = NULL, | |||
.check_virtual_desktop = NULL, | |||
.auth_id = NULL, | |||
.listen_port = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable names are very generic. Maybe, being more specific about what the listen_port is actually listen for might improve readability.
if (username[0] == '\0' && tunnel->config->password[0] == '\0') { | ||
if (tunnel->config->auth_id != NULL) { | ||
char url[256]; | ||
snprintf(url,sizeof(url), "/remote/saml/auth_id?id=%s", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Size check is missing. In case the auth ID gets too long, it will be cut off without notice.
|
||
static void print_url(struct vpn_config *cfg) { | ||
char url[512]; | ||
snprintf(url, sizeof(url), "https://%s:%d/remote/saml/start?redirect=1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Size check missing. For long server names, the string might be cut off.
|
||
static void print_url(struct vpn_config *cfg) { | ||
char url[512]; | ||
snprintf(url, sizeof(url), "https://%s:%d/remote/saml/start?redirect=1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Size check missing. For long server names, the string might be cut off.
cfg->gateway_host, cfg->gateway_port); | ||
|
||
if (cfg->realm[0] != '\0') { | ||
strncat(url, "&realm=", sizeof(url) - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To my understanding the 3rd argument is how may bytes from the src
argument it should copy, but not the total size of both strings combined.
Also it has to be checked if there is actual space in url
to fit the additional string. According th the man page, behavior is undefined.
if (optarg != NULL) { | ||
port = strtol(optarg, NULL, 0); | ||
if (port < 1 || port > 65535) { | ||
log_error("Specify a valid listen port or omit for parameter ext-browser-saml\n"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion:
Specify a valid listen port for parameter ext-browser-saml or omit it to use the default.
} | ||
if (strcmp(long_options[option_index].name, "auth-id") == 0) { | ||
free(cli_cfg.auth_id); | ||
cli_cfg.auth_id = strdup(optarg); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it handy to have at least a zero length check to catch shell mistakes like empty variables when calling as early as possible.
if(cfg.auth_id){ | ||
log_warn("auth-id will be ignored conflict with --ext-browser-saml"); | ||
} | ||
log_debug("Will listen on port \"%d\" for authentication id \n", cfg.listen_port); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's about the same message in idlistener.c:listen_for_id
free(cfg.auth_id); | ||
cfg.auth_id = listen_for_id(&cfg); | ||
} | ||
|
||
if (geteuid() != 0) { | ||
log_error("This process was not spawned with root privileges, which are required.\n"); | ||
ret = EXIT_FAILURE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Sorry, for putting it here, but I didn't find out how to comment on files that there not changed.)
This MR is missing changes to README.md
and doc/openfortivpn.1.in
read(newsockfd, buffer, MAX_REQUEST_SIZE - 1); | ||
log_debug("Received HTTP request:\n%s\n", buffer); | ||
|
||
char *id = parse_request(buffer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to have a check if the id string has zero length, just in case something went wrong. I won't make sense to continue in that case.
if (cfg->realm[0] != '\0') { | ||
strncat(url, "&realm=", sizeof(url) - 1); | ||
char *dt = url + strlen(url); | ||
url_encode(dt, cfg->realm); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the documentation of url_encode
it is stated that the buffer size must be at least 3x the size of cfg->realm
+1. I can't find that reflected in the code here.
add parameter --ext-browser-saml[=]
localhost:8020/?id=<auth-id>
)there is also parameter --auth-id= if retrieved externally
NEED HELP to review printed messages and update man pages
the ext-browser-saml can be configured configuration file but in this case the port is required
es