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

Proxy websockets through nginx #390

Closed
usernameisalreadytaken2014 opened this issue Jul 23, 2014 · 2 comments
Closed

Proxy websockets through nginx #390

usernameisalreadytaken2014 opened this issue Jul 23, 2014 · 2 comments
Assignees
Labels
Milestone

Comments

@usernameisalreadytaken2014

Obviously, for ease-of-use, which is one of the main goals of Kimchi, we want to use just the standard HTTP and HTTPS ports.

Currently, Kimchi also requires port 64667 to be open for Kimchi to function correctly.

I tried to create a patch, but couldn't figure out how to make "if" work in nginx.conf. I managed to make nginx segfault though.

I turned off the nginx process that kimchi launches, and implemented the logic for running on just one HTTPS port via Varnish. Here's the config that I'm using:

vcl 4.0;

import std;

backend cherrypy {
    .host = "127.0.0.1";
    .port = "8010";
}

backend websockify {
    .host = "127.0.0.1";
    .port = "64666";
}

sub vcl_recv {
    #
    # HTTP to HTTPS redirect.
    #
    if (std.port(server.ip) == 80) {
        return (synth(302));
    }

    #
    # As a courtesy to browsers that for some reason do not send Authorization with WebSocket requests, copy/paste from cookie if found there.
    #
    if (req.http.authorization ~ "^$" && req.http.cookie ~ "Authorization=") {
        set req.http.authorization = regsub(req.http.cookie, "Authorization=([^;]*)", "\1");
    }

    #
    # Authenticate, could do with a bit of PAM integration using one of the VMOD auth plugins
    #
    if (! req.http.authorization ~ "Basic USER_AND_PASS_IN_BASE64") {
        return (synth(401, "Restricted"));
    }

    #
    # Send to proper subsystem given the path.
    # Start shuffling bytes if WebSocket.
    #
    if (req.url ~ "^/console.html" || req.http.Upgrade ~ "(?i)websocket") {
        set req.backend_hint = websockify;
        return(pipe);
    } else {
        set req.backend_hint = cherrypy;
    }

    #
    # Normal HTTP pipeline, but avoid caching in case kimchi doesn't support that.
    #
    return(pass);
}

sub vcl_pipe {
    #
    # Carry HTTP Upgrade header to WebSocket backend.
    #
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
    }
}

sub vcl_backend_response {
    #
    # As a courtesy to browsers that for some reason do not send Authorization with WebSocket requests, copy/paste into cookie ASAP.
    #
    if (bereq.http.cookie !~ "Authorization=" || bereq.http.authorization != regsub(bereq.http.cookie, "Authorization=([^;]*)", "\1")) {
        set beresp.http.Set-Cookie = "Authorization=" + bereq.http.authorization + "; secure";
    }

    #
    # Rewrite Location header from broken subsystems that push out wrong ports/hostnames and absolute URLs.  See RFC7231, section 7.1.2.
    #
    if (beresp.http.location ~ "^http") {
        set beresp.http.location = regsub(beresp.http.location, "^https?://[^/]*/", "/");
    }
}

sub vcl_synth {
    if (resp.status == 302) {
        set resp.http.Location = "https://" + regsub(req.http.host, ":.*", "") + req.url;
        synthetic("Redirecting to HTTPS...");
        return (deliver);
    }
    if (resp.status == 401) {
        set resp.http.Content-Type = "text/plain; charset=utf-8";
        set resp.http.WWW-Authenticate = "Basic realm=Authentication";
        synthetic("Please login.");
        return (deliver);
    }
}

Obviously, I don't care about session timeouts and silly stuff like that - it is the user's browser that should be their password manager and session manager, not us. Therefore using standard browser authentication mechanism.

Also, I didn't see the point of running SSL between the reverse proxy and the websockify backend, so I decreased the websockify listener by one from 127.0.0.1:64667 to 127.0.0.1:64666, and changed it from SSL to plaintext. Varnish handles all the SSL in one place.

And finally, I hardcoded the username and password rather than use one of the PAM plugins, which is just lazy. (Replace "USER_AND_PASS_IN_BASE64" with proper base64 version of "user:pass".)

Not sure that Kimchi authors want to switch from Nginx to Varnish though?

If not then the above code can maybe serve as inspiration. It only takes 55 lines of reverse proxy code to make Kimchi work 100% on the HTTP/HTTPs ports.

@alinefm
Copy link
Member

alinefm commented Jul 24, 2014

If I understood correctly you are proposing to integrate websockets into nginx. Is that right?
If so, there is an issue open related to that: #22

@alinefm alinefm added this to the Backlog milestone Dec 3, 2014
@alinefm alinefm added the feature label Dec 3, 2014
@alinefm alinefm modified the milestones: Kimchi 2.1, Backlog Jan 20, 2016
@alinefm alinefm self-assigned this Jan 20, 2016
@alinefm
Copy link
Member

alinefm commented Jan 20, 2016

Fixed by 84349f5 and kimchi-project/wok@62b0d80

@alinefm alinefm closed this as completed Jan 20, 2016
@alinefm alinefm changed the title kimchi requires non-standard ports Proxy websockets through nginx Jan 20, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants