How can I specify a static port for each shiny app with shiny-server?

I installed shiny server successfully on a webserver. When I turn off the firewall, I can view shiny apps through shiny server. However, I do not want to turn off my firewall. Can I restrict the ports that shiny server will use to publish apps? I would be comfortable allowing access to either a static single port, or a dynamic range of ports. Is there a way to accomplish this?

Thanks!

1 Like

It depends on what firewall software you use but with ufw on linux you can simply allow traffic on the port shiny server is listening on. The default for shiny server is 3838, so you can run:

sudo ufw allow 3838

I've used this successfully in the past, but some corporate users are behind firewalls that prevent them from accessing sites on anything other than 80 or 443.

What I would suggest doing if you can is proxying shiny server behind nginx (or apache, but nginx is made for fast proxying) so you can just leave 80 and/or 443 open. I serve shiny apps under /shiny and shiny admin at /shiny-admin. Here's the relevant bit of my nginx config you could copy:

    location /shiny/ {                                                             
      rewrite ^/shiny/(.*)$ /$1 break;                                             
      proxy_pass http://localhost:3838;                                            
      proxy_redirect http://localhost:3838/ $scheme://$host/shiny/;                
      proxy_http_version 1.1;                                                      
      proxy_set_header Upgrade $http_upgrade;                                      
      proxy_set_header Connection $connection_upgrade;                             
      proxy_read_timeout 20d;                                                      
    }                                                                              
                                                                                   
    location /shiny-admin/ {                                                       
      rewrite ^/shiny-admin/(.*)$ /$1 break;                                       
      proxy_pass http://localhost:4151;                                            
      proxy_redirect http://localhost:4151/ $scheme://$host/shiny-admin/;          
      proxy_http_version 1.1;                                                      
      proxy_set_header Upgrade $http_upgrade;                                      
      proxy_set_header Connection $connection_upgrade;                             
      proxy_read_timeout 20d;                                                      
    } 

I'm on linux and using iptables. I allowed port 3838 with

ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3838

However, the real issue is with shiny applications, not shiny itself. Shiny apps are configured to run by default on a randomly selected port. I tested this with the deploy example default in the quick start guide. When my firewall was turned off, I saw the shiny app and rmarkdown app running normally. Port 3838 runs shiny-server and two random ports are used for the two shiny apps. When I turn my firewall back on, it blocks the two random ports used for the shiny applications. I would like a way to specify which ports these applications run on so that I can allow them.

Thanks for the reply. I'm on apache webserver, not nginx. Are the apps themselves served on port 3838? I thought the apps were on a different port and the 3838 is for shiny server itself.

If I understand your nginx code, you are creating a rewrite rule so that shiny-server serves on for example `http://www.mywebsite.com/shiny' rather than on 'localhost:3838'. Is this correct?

All of this depends very much on how you configure your Shiny Server. In any case, a clean out-of-the-box Shiny Server should only need port 3838. You are correct that the shiny apps themselves are hosted on other ports, but Shiny Server takes care of the routing for you. You just need to worry about the path.

For illustration, I used rocker/shiny. I got it started with:

docker run --rm -p 3838:3838 -d rocker/shiny

And then use the hash (or look it up with docker ps) to get a shell inside the container:

docker exec -it a3f21fd0e198 bash

The shiny app directory, by default:

root@fd9f12fdc3c8:/ ls /srv/shiny-server/
01_hello/      03_reactivity/ 05_sliders/    07_widgets/    09_upload/     11_timer/      sample-apps/   
02_text/       04_mpg/        06_tabsets/    08_html/       10_download/   index.html    

Then, in my browser, I access applications at URLs like:

http://localhost:3838 (this is the home page by default)
http://localhost:3838/sample-apps/hello/
http://localhost:3838/03_reactivity/
etc.

With a tool like netstat, you can see that these applications are in fact being served on other ports and my traffic is being routed appropriately.

root@fd9f12fdc3c8:/ sudo netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:34711         0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:41339         0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:3838            0.0.0.0:*               LISTEN      1/shiny-server      
tcp        0      0 127.0.0.1:37129         0.0.0.0:*               LISTEN      -  

A lot of this is certainly configurable depending on how you set up your shiny-server.conf file, though! There is lots to read and explore in the admin guide.

1 Like

Hi Cole,

Yes, I am running a clean, out-of-the-box shiny server. After initializing the demo apps with

sudo /opt/shiny-server/bin/deploy-example default

I get netstat -lntp output very similar to yours. I can see shiny-server itself running on 0.0.0.0:3838 and the two default shiny apps running on 127.0.0.1:41339 and 127.0.0.1:37129. When I turn my firewall off, these apps run appropriately; I can browse to them with

http://localhost:3838/sample-apps/hello/
http://localhost:3838/03_reactivity/

just as you said in your example.

The issue for me is that these ports are dynamic, not static. I would like my firewall to allow access to these two ports only, but because these two ports are dynamic, I do not know which two ports to open. My other option is to leave every single port available open and vulnerable (not a secure idea, but at least shiny-server will run correctly).

When I run a shiny app without shiny-server, I can specify a static port with the port argument in runApp. For example, If I want to run the sample-apps/hello app on port 3839 I would use

runApp(appdir = .../sample-apps/hello, port = 3839)

Does shiny-server use the runApp command somewhere? Or is there someplace within each app directory where I can specify the port=<my_static_port> option? Either way will work but I cannot find any information on this in the admin guide.

Thanks!

You correctly got what those rules do. Turns out there's already a support article that explains how to proxy Shiny with nginx or Apache:

You should be able to run any Shiny app at a sub path of something like /shiny and it will even proxy the websockets through with the given Apache rules and the mod_proxy and mod_proxy_wstunnel plugins.

When I turn my firewall off, these apps run appropriately; I can browse to them with

Interesting. Just to be clear: are you saying that when your firewall is on, you cannot browse to them at http://localhost:3838/sample-apps/hello? Does your firewall allow traffic on port 3838?

I ask because firewalls are usually configured to stop external traffic from coming in. The random ports that you see active (41339 and 37129 in your case) are where shiny apps are being served, but they are being "proxied", so to speak, by the shiny server. As a result, the only traffic to them is internal to the box itself, forwarded from port 3838, and your firewall shouldn't care about internal traffic.

The correct place to access all of your applications is on port 3838, but this can be changed to another port if desirable. For instance, you can even configure running on multiple ports in the admin guide.

My point, though, is that you shouldn't need to open every port on your box and you shouldn't need to know which port the apps are served on. Shiny Server takes care of that for you. All your firewall needs to allow is access to port 3838 (or whatever ports you configure in the shiny server config).

Even from inside the box, I get a 403 Forbidden trying to access/curl/nmap the "hidden" ports that the shiny app is listening on. I think that port is locked down by shiny server.

<h1>403 Forbidden</h1><p>Shared secret mismatch</p>

As a final example, on Shiny Server Pro, the same "shiny app" might be listening on multiple ports simultaneously. Shiny Server takes care of routing the traffic using the client-side path (i.e. /sample-apps/hello) and server-side configuration provided.

EDIT: I am struggling to determine if you are concerned about opening all the ports (as I mentioned - you shouldn't need to) or if you are trying to configure an app to listen on a different port. The port that an app listens on is determined by the shiny-server configuration.

My issue was that iptables was blocking local loopback. After adding the line
-A INPUT -i lo -j ACCEPT
to my iptables rules, shiny-server is now serving apps locally to 127.0.0.1:3838.

1 Like