Running a cowboy server in the google cloud

I’ve written a server using Cowboy. The server responds fine when running on my desktop. I can connect to it from my phone and receive expected data. But, after I moved it to a VM in the Google Cloud it is unreachable. The VM has an static IP that I know works, because I get the default Apache Server page when navigating to the static IP in a browser. Also when I shut down the Apache server, I get a 404 as expected. Here’s what I tried so far.
1.

  {ok, _} = cowboy:start_clear(star_watch_http_listener,
        [{port, 8080}],
        #{env => #{dispatch => Dispatch}}
    ),

I also tried

 {ok, _} = cowboy:start_clear(star_watch_http_listener,
        [{ip, {0,0,0,0}, ["sometagname"]}, {port, 8080}],
        #{env => #{dispatch => Dispatch}}
    ),

I’ve tried modifying my sys.config file to specify an IP address but that prevents the server from starting at all. In short, is there a way to make Cowboy based server listen to connections on a specific IP address?

1 Like

Not to be “that guy”, but … if the IP and everything is correct then unless there’s some reverse proxy or something going on, when you shut down Apache you shouldn’t get a 404, just a browser connection failure (or possibly a timeout depending on firewall stuff). Is that what you mean by unreachable? If so then when you’re running the cowboy server, try doing curl 0.0.0.0:8080 from a shell on the VM to make sure it’s connecting, to rule out any internal networking stuff, and also curl ipinfo.io to check the public IP your VM is listening on. Also presumably the default Apache is on port 80 but you’re running cowboy on 8080 - is port 8080 open in the GCP firewall?

3 Likes

Igor, the two commands you gave me, curl 0.0.0.0:8080 and curl ipinfo.io helped me track down the problem. First, 8080 was closed (although the Google cloud console said it was opened), secondly the static IP address I have from Google is visible to the outside world and port 80 is accessible. Third, instead of 0.0.0.0:80 I had to use "my.ip.add.ress:80 Last but not least, I had to start the Cowboy server using sudo, otherwise the server was restricted from using any ports at all. I can now connect to the server, send and receive data. I have been a client-side programmer (C/C++/Kotlin) most of my career. Server-side programming in Erlang is like learning how to walk again. I greatly appreciate your help. Thank you, Igor.

2 Likes

Please for the love of all that is holy do not run a server using sudo.

Look into port forwarding, which gcloud should support, to forward port 80 to 8080.

The alternative is using setcap to allow beam.smp to bind to ports below 1000.

4 Likes

Here is some documentation from the Zotonic docs on how to run on port 80 and 443.

You can skip the Zotonic specific configuration stuff :slight_smile:

4 Likes

Good advice. I love all that is holy. Will forward the ports.

1 Like

Thanks, mworrel!

1 Like

In addition to what everyone else has said, if you need to run Apache sites as well, then you can use HAProxy to forward traffic to the correct ports. Here’s an example:

global
	#
	# upload to: /etc/haproxy/
	#
  # to have these messages end up in /var/log/haproxy.log you will
  # need to:
  #
  # 1) configure syslog to accept network log events.  This is done
  #    by adding the '-r' option to the SYSLOGD_OPTIONS in
  #    /etc/sysconfig/syslog
  #
  # 2) configure local2 events to go to the /var/log/haproxy.log
  #   file. A line like the following can be added to
  #   /etc/sysconfig/syslog
  #
  #    local2.*                       /var/log/haproxy.log
  #
  # log         127.0.0.1 local2


  tune.ssl.default-dh-param 2048

  ssl-default-bind-options no-sslv3 no-tls-tickets
  ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA

  ssl-default-server-options no-sslv3 no-tls-tickets
  ssl-default-server-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA



  # chroot      /var/lib/haproxy
  pidfile     /var/run/haproxy.pid
  maxconn     4000
  user        haproxy
  group       haproxy
  daemon

  # turn on stats unix socket
  stats socket /var/lib/haproxy/stats

  tune.ssl.default-dh-param 2048

defaults
  mode                    http
  log                     global
  option                  httplog
  option                  dontlognull
  option http-server-close
  # option forwardfor       except 127.0.0.0/8
  option forwardfor
  option                  redispatch
  retries                 3
  timeout http-request    10s
  timeout queue           1m
  timeout connect         10s
  timeout client          1m
  timeout server          1m
  timeout http-keep-alive 10s
  timeout check           10s
  maxconn                 3000


frontend http-in
  bind *:80
  bind :::80 
  bind *:443 ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10
  bind :::443 ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10
  acl letsencrypt-acl path_beg /.well-known/acme-challenge/
  use_backend letsencrypt-backend if letsencrypt-acl
  default_backend main_apache_sites
  reqadd X-Forwarded-Proto:\ https if { ssl_fc }


  # Define hosts
  redirect prefix http://domain1.com code 301 if { hdr(host) -i www.domain1.com }
  acl host_1 hdr(host) -i domain1.com
  redirect prefix http://domain2.com code 301 if { hdr(host) -i www.domain2.com }
  acl host_2 hdr(host) -i domain2.com


  #Redirect sites to HTTPS
  acl ssl_redirect_hosts hdr(Host) -i domain1.com
  acl ssl_redirect_hosts hdr(Host) -i domain2.com
  redirect scheme https if ssl_redirect_hosts !{ ssl_fc }
  redirect scheme https code 301 if !{ ssl_fc }


  # figure out which one to use
  use_backend b_1 if host_1
  use_backend b_2 if host_2


backend main_apache_sites
  server server1 127.0.0.1:8080 cookie A check
  cookie JSESSIONID prefix nocache

backend b_1
  server server2 127.0.0.1:8888 cookie A check
  cookie JSESSIONID prefix nocache

backend b_2
  server server3 127.0.0.1:8889 cookie A check
  cookie JSESSIONID prefix nocache

backend letsencrypt-backend
  server letsencrypt 127.0.0.1:54321

This is where you’d configure Apache to listen on 8080, Erlang site one to listen on 8888, Erlang site 2 on 8889, etc. The great thing about this is HAProxy takes care of https as well (you can use lets encrypt (with this guide) for certs).

2 Likes

Thanks!

1 Like