Expose Synpse application with DuckDNS and Let's Encrypt

Published on September 11, 21

One of the challenges of running an application at the edge is to be able to access it the same way as any other application running in the normal hosting environments. In this blog post we will show you how to use DDNS client with [DuckDNS]https://www.duckdns.org/) to expose your application to the outside world! Let’s get started!

Important: This will not work if your devices are not able to be accessed via an external IP address. For this to work you might need to configure your router with a port forwarding. Which is out of scope for this blog post.

###Technologies used

  1. Synpse for hosting and running applications anywhere
  2. DDNS for managing DNS records for multiple DNS providers
  3. Let’s Encrypt for TLS certificates
  4. DuckDNS for alternative if you don’t onw a domain

###Domain

If you don’t onw a domain, and don’t have a need for it - you can use DuckDNS to get one for you. It is very convenient online service to get free, not malware based DNS name

Sign-in into DuckDNS and create a domain for your application.

DuckDNS domain
DuckDNS domain

we go ourselfs a synpse-demo.duckdns.org

###Deploy basic application

As for an application, we will use quay.io/synpse/hello-synpse-go “Hello World” application. We are going to deploy the it alone for now:

Hello world
Hello world

As a yaml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
name: hello
scheduling:
  type: Conditional
  selectors:
    app: ddns
spec:
  containers:
    - name: hello
      image: quay.io/synpse/hello-synpse-go
      ports:
        - 8080:8080

We can access the application on our local network, but this is not our final goal:

1
2
3
4
5
6
7
8
9
$ curl 192.168.178.103:8080
<!DOCTYPE html>
<html lang="en">
  <head> </head>
  <body>
    <h1>Hello from Synpse</h1>
    
  </body>
</html>

###Extend an application with DDNS

We are going to use linuxserver docker image for DDNS client.

####Create DDNS config for DuckDNS

1
docker run linuxserver/ddclient ddclient --help | grep duckdns -A 10

Our example config looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# /etc/ddclient/ddclient.conf
#
protocol=duckdns
use=web
web=checkip.dyndns.org
daemon=60
syslog=yes
ssl=yes
ttl=2
# password is token from duckdns.org
password=68466070-xxxx-xxxx-xxx-xxxxxxxxxx
synpse-demo
1
synpse secret create ddns-config -f ddclient.conf

Extend “Hello world” application with DDNS image:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
name: hello
scheduling:
  type: Conditional
  selectors:
    app: ddns
spec:
  containers:
    - name: hello
      image: quay.io/synpse/hello-synpse-go:amd64
      forcePull: true
      networkMode: default
      ports:
        - 8080:8080
    - name: ddns
      image: ghcr.io/linuxserver/ddclient
      env:
        - name: TZ
          value: Europe/London
        - name: PUID
          value: "0"
        - name: PGID
          value: "0"
      secrets:
        - name: ddns-config
          filepath: /config/ddclient.conf

Once these steps are done, we should have:

  1. Application deployed and exposed on port 8080 http://synpse-demo.duckdns.org:8080
  2. DDNS running and managing DuckDNS configuration.

We could stop here, as this shows already how to expose an application.

But we will do one more step, add TLS certificate. For this, we will add “Let’s Encrypt” container to the mix and we will use CertBot

###Let’s Encrypt with DuckDNS

Let’s create a configuration for “CertBot” (same credentials as in DDNS). We gonna use them from the script, because all script is treated as a secret in Synpse. This script will renew the certificate on a periodic basis as certbot is not able to run as a daemon, which is required for containers:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh

# Small hack script to renew "Let's Encrypt" certs each week
# Once deployed in production remove  --test-cert flag to generate valid certificate
# doing development without this flag will get you banned from Lets Encrypt very fast!

pip install certbot_dns_duckdns

while [ : ]
do
    echo "Renewing Let's Encrypt certs"
    certbot certonly -v \
    --preferred-challenges dns \
    --authenticator dns-duckdns \
    --email [email protected] \
    --dns-duckdns-token 68466070-xxxx-xxxx-xxx-xxxxxxxxxx \
    --dns-duckdns-propagation-seconds 60 \
    --renew-by-default  \
    --agree-tos \
    --test-cert \
    -d "synpse-demo.duckdns.org" \
    -n

    echo "Sleeping for 7d"
    sleep 7d  
done
1
synpse secret create script-cert-bot -f ddns/renew.sh

and extend our existing application. Note variables we added to the original application and change of port.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
name: hello
scheduling:
  type: Conditional
  selectors:
    app: ddns
spec:
spec:
  containers:
    - name: hello
      image: quay.io/synpse/hello-synpse-go:amd64
      forcePull: true
      networkMode: default
      ports:
        - 443:443
      volumes:
        - /data/demo/letsencrypt:/etc/letsencrypt
      env:
        - name: TLS_CRT
          value: /etc/letsencrypt/live/synpse-demo.duckdns.org/fullchain.pem
        - name: TLS_KEY
          value: /etc/letsencrypt/live/synpse-demo.duckdns.org/privkey.pem
        - name: PORT
          value: :443
      restartPolicy: {}
    - name: ddns
      image: ghcr.io/linuxserver/ddclient
      env:
        - name: TZ
          value: Europe/London
        - name: PUID
          value: "0"
        - name: PGID
          value: "0"
      secrets:
        - name: ddns-config
          filepath: /config/ddclient.conf
      restartPolicy: {}
    - name: certbot
      image: certbot/certbot
      command: /run/secrets/renew.sh
      entrypoint:
        - sh
      volumes:
        - /data/demo/letsencrypt:/etc/letsencrypt
        - /data/demo/var-lib-letsencrypt:/var/lib/letsencrypt
      secrets:
        - name: script-cert-bot
          filepath: /run/secrets/renew.sh
      restartPolicy: {}

Once the application is updated, rolled out and all components are running you should be able to see this:

Application deployed
Application deployed

./wrap_up.sh

This stack now gives us a device at the edge, with TLS certificates from “Let’s Encrypt” with dynamic refresh and dynamic DNS updates for “DuckDNS”! This can be used for any other applications like “Drone”, “Prometheus”, “Grafana”. We will use this pattern in the future to show how you can deploy other applications!

In the next blog post we will show how you can use similar stack to expose application with your owned customer domain!

If you have any questions or suggestions, feel free to start a new discussion in our forum or drop us a line on Discord