example of application with both public and private interfaces

I need example showing how to setup application that on one port listens for public traffic, and on another listens only to private traffic. I have REST API that should be public on 8080 and gRPC that should pe private on 8081.

Please help.

Hi… All (or almost all) LiteFS applications are examples of that, since they serve HTTP publicly but use port 20202 for internal DB replication traffic.

(Indeed, you would create a gigantic security hole if you did allow external access to the latter, :dragon:.)

https://fly.io/docs/litefs/getting-started-fly/

That’s maybe more involved than what you were looking for, though.

In general, the main points are…

  • Listen on :: (IPv6).
  • Disable auto-stop / auto-suspend.†
  • Don’t cite port 8081 in any [http_service] or [[services]] declaration.

You can use fly services list to verify the list of things that are being exposed to the public.

Hope this helps!


Aside: You unfortunately cannot make this distinction with .flycast addresses—only with .internal.


†Unless your .internal addresses only need to be used for Machines that are already known to be awake for other reasons, e.g., because they’ve signed up as replication subscribers.

I dug up a simpler example, actually, although still maybe not ideal…

The fly.toml:

app = "qua-melon"
primary_region = "ewr"
swap_size_mb = 512

[[services]]
  protocol = "tcp"
  internal_port = 8080
  [[services.ports]]
    handlers = ["tls"]  # would be ["tls", "http"] for HTTPS.
    port = 4321

# note:  port 8081 is intentionally *not* mentioned anywhere.

The super-unoptimized Dockerfile:

FROM debian:bullseye-slim

RUN apt-get update  -y && \
    apt-get install -y --no-install-recommends racket procps iproute2

WORKDIR /app

COPY b.rkt b.rkt

CMD ["racket", "/app/b.rkt"]

And the barebones server itself (b.rkt):

#lang racket/base

(require racket/tcp)

(define (serve port msg)
  (define l (tcp-listen port))
  (eprintf "listening on ~v...\n" port)
  (let again ()
    (define-values (in out) (tcp-accept l))
    (write-string msg  out)
    (close-output-port out)
    (close-input-port   in)
    (again)))

(void (thread (λ () (serve 8081 "hello internal.\n"))))

(serve 8080 "hello world.\n")

Note that this uses plain TCP instead of HTTP, and the public service is wrapped in SSL, thus allowing a freebie shared IPv4 address to be used—since I am a cheapskatecost optimization expert.

Test the public one via…

$ socat STDIO OPENSSL:qua-melon.fly.dev:4321,no-sni=0  # note `OPENSSL:` with SNI.
hello world.

But from inside the internal network…

$ fly ssh console
# apt-get update
# apt-get install --no-install-recommends socat
# socat STDIO TCP6:qua-melon.internal:8081  # no SSL.
hello internal.

And to verify…

$ fly services list
PROTOCOL  PORTS         HANDLERS
TCP       4321 => 8080  [TLS]

Only port 8080 is mentioned, as intended. That’s the only one that the public Internet can get through to.

(Note: some output columns elided.)

2 Likes

Don’t cite port 8081 in any [http_service] or [[services]] declaration.

that is exactly what I needed!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.