Greetings!
(Inviting @cleoqc and @mitch.kremm to see this.)
Because of, (ahem!), “advances” in browser security, it may be necessary to present an outward-facing secure connection from the robot to the rest of the network.
-
In my case, in order to use the gamepad interface within the browser, the connection must be secure.
-
Though Flask/Werkzeug can be made secure relatively easily, other servers like streamserver cannot be made secure.
-
If you have multiple server services running on the same 'bot, trying to secure all of them at the same time can be a challenge, inviting error.
The answer is a secure “wrapper”, (proxy/reverse-proxy), that will create a network-facing secure connection and pass it through as insecure connections to the internal servers/services.
There are a number of solutions, but many are specific to Flask, and many won’t work with streamserver, which is the video-streaming server for the remote camera robot.
The solution I ultimately chose was nginx. Nginx is advertised as “the Swiss army knife” of web servers/services and it will do everything except make breakfast.
-
Advantages:
- It works. More important is that it is totally connection agnostic as far as the “upstream”, (inside), connections are concerned. It can pass through connections either as secure or insecure connections while maintaining a secure outward facing connection.
- Note that nginx is also famous as a load-balancer, automagic failover manager, etc. etc. etc., but these uses are more appropriate for more involved configurations.
- Once you understand what it’s doing, the configuration borders on trivial as most of it is “boilerplate” for simple reverse-proxy configurations.
- Since it acts as a “wrapper”, there is only one point of configuration which means you can leave the rest of the stuff alone.
- The best way to think of nginx in this configuration is that it is similar to a router doing port-forwarding.
- The best way to think of nginx in this configuration is that it is similar to a router doing port-forwarding.
- It works. More important is that it is totally connection agnostic as far as the “upstream”, (inside), connections are concerned. It can pass through connections either as secure or insecure connections while maintaining a secure outward facing connection.
-
Disadvantages:
- The available literature on-line is abysmal at best and absolutely hideous as a general rule.
- The typical use cases that are described in the available literature are either abysmally stupid or hideously complex; intended for server farms, e-commerce sites, monster database servers; with load balancing and automatic failover and recovery - etc. etc. etc.
- They say there’s more than one way to skin a cat. For nginx that’s very true and the shear number of methods is confusing to say the least.
- I said “Once you understand what it’s doing, the configuration borders on trivial”, and that’s true. Unfortunately, figuring it out is not a simple task.
- The “understand what it’s doing” is the tough part as there is literally too much information out there, contradictory, possibly wrong, and recommending ways of configuring nginx that border on insane. (i.e. One site actually recommends modifying the base nginx configuration file instead of adding a subsidiary configuration file to the “active servers” directory! )
- The available literature on-line is abysmal at best and absolutely hideous as a general rule.
By and large, once you get it figured out, it’s not that difficult.
I discuss this in detail over on Stack Overflow:
The basic idea is to create a configuration file that directs traffic to the two different servers - though you might have to change the outward port number for them to maintain the incoming port numbers.
This is my annotated configuration file, cleaned up for Stack Overflow. (they don’t allow “live” domain names in their articles)
Note that I pass through the Flask port as a secure connection and the streamserver port as an insecure connection. It is important to note that this is not necessary, (and probably too much complexity), but I left it this way as an exercise and proof-of-concept.
Viz.:
# I commented out the server connection redirect for port 80
# as that is already used by the GoPiGo3 interface web page.
#
#server {
# listen example.com:80;
# server_name example.com;
# return 301 https://example.com$request_uri;
# }
# This is the "web" server (command and control), running Flask/Werkzeug
# that must be passed through as a secure connection so that the
# joystick/gamepad works.
#
# Note that the internal Flask server must be configured to use a
# secure connection too. (Actually, that may not be true, but that's
# how I set it up. . .)
#
server {
listen example.com:443 ssl;
server_name example.com;
ssl_certificate /usr/local/share/ca-certificates/extra/combined.crt;
ssl_certificate_key /usr/local/share/ca-certificates/extra/example.com.key;
ssl_prefer_server_ciphers on;
location / {
proxy_pass https://example.com:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# This is the video streaming port/server running streamserver
# which is not, and cannot be, secured. However, since most
# modern browsers will not mix insecure and secure content on
# the same page, the outward facing connection must be secure.
#
server {
listen example.com:5001 ssl;
server_name example.com;
ssl_certificate /usr/local/share/ca-certificates/extra/combined.crt;
ssl_certificate_key /usr/local/share/ca-certificates/extra/www.example.com.key;
ssl_prefer_server_ciphers on;
# After securing the outward facing connection, pass it through
# as an insecure connection so streamserver doesn't barf.
location / {
proxy_pass http://example.com:5002;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}