Setting up a Private Routing Environment

Hylke Visser

TTN Core Team

Posted on 14-12-2016

This document is a guide for setting up The Things Network's routing services in a local or private environment. If you already have some knowledge about how the backend works and if you are comfortable with a command line, this is the perfect place to start.

NOTE: This guide applies to the previous version (v2) of our network stack. To get started with the current version of our network stack (v3), go to ttn.fyi/v3/getting-started.

In this guide we will get everything up and running on your local machine (on localhost). We will rely on the community account server of The Things Network (account.thethingsnetwork.org) so that you can use your TTN Account to manage devices in your local network. If you don't want your environment to use community accounts, you can implement your own account server that is compliant with the Account Server API Specification.

Preparation

The Discovery Server

The configuration for the Discovery server will be stored in ~/ttn/discovery/ttn.yml:

id: mynetwork-discovery
tls: true
key-dir: discovery/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"
  local: "file://discovery/server.pub"

discovery:
  master-auth-servers:
  - ttn-account-v2
  - local

This configuration creates a discovery server called "mynetwork-discovery". The server will use TLS and get its keys from ./discovery. It uses the public TTN account server and a local certificate for authentication, and both "account servers" are seen as "master servers".

With this configuration in place, we will generate the public/private keypair for the Discovery server:

$ ttn discovery gen-keypair --config ./discovery/ttn.yml

Since we use TLS, we need to create a certificate for localhost:

$ ttn discovery gen-cert localhost --config ./discovery/ttn.yml

You can now start the discovery server:

$ ttn discovery --config ./discovery/ttn.yml

Keep the discovery server running while you set up the other components.

Access Tokens

Routers, Brokers and Handlers authenticate with the discovery server using "access tokens". In the public community network you could get those with the command ttnctl components token [router/broker/handler] [id], but as we're building a private network, we have to manually create these access tokens with the command ttn discovery authorize [router/broker/handler] [id]. For the Router you would use the following command:

ttn discovery authorize router mynetwork-router --config ./discovery/ttn.yml

You can use the access token that is printed to your console in the router's configuration. Use the appropriate commands to do the same for the Broker and Handler.

The Router

The configuration for the Router will be stored in ~/ttn/router/ttn.yml:

id: mynetwork-router
tls: true
key-dir: router/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"

discovery-address: "localhost:1900"
auth-token: THE DISCOVERY ACCESS TOKEN THAT YOU GENERATED FOR THE **ROUTER**

router:
  server-address-announce: localhost
  skip-verify-gateway-token: true

This configuration creates a router called "mynetwork-router". The server will use TLS and get its keys from ./router. It uses the public TTN account server for authentication. It connects to the discovery server on localhost and uses the access token we just generated to authenticate with the discovery server. The Router announces itself as being on the localhost address. Furthermore it skips verification of the gateway token, because we're in a private network. You can remove this line if you do want to verify gateway tokens.

Just like with the discovery server, we have to generate a public/private keypair and a TLS certificate:

ttn router gen-keypair --config ./router/ttn.yml
ttn router gen-cert --config ./router/ttn.yml

In this case we don't have to specify that we're running on localhost when generating the certificate. The command will just take that from the configuration file.

The TLS certificate we generated for the discovery server is not trusted by the operating system, so we have to manually trust it. To do so, we can append it to a ca.cert file in the router directory:

cat discovery/server.cert > router/ca.cert

You will have to do the same when you set up the Broker and Handler.

You can now test your configuration by starting the Router:

$ ttn router --config ./router/ttn.yml

The NetworkServer

The configuration for the NetworkServer will be stored in ~/ttn/networkserver/ttn.yml:

id: mynetwork-networkserver
tls: true
key-dir: networkserver/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"

Again we have to generate a public/private keypair and a TLS certificate:

ttn networkserver gen-keypair --config ./networkserver/ttn.yml
ttn networkserver gen-cert --config ./networkserver/ttn.yml

Access Token

Brokers authenticate with the NetworkServer using access tokens. You can create an access token with the following command:

ttn networkserver authorize mynetwork-broker --config ./networkserver/ttn.yml

The Broker

The configuration for the Broker will be stored in ~/ttn/broker/ttn.yml:

id: mynetwork-broker
tls: true
key-dir: broker/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"

discovery-address: "localhost:1900"
auth-token: THE DISCOVERY ACCESS TOKEN THAT YOU GENERATED FOR THE **BROKER**

broker:
  server-address-announce: localhost
  networkserver-cert: broker/networkserver.cert
  networkserver-token: THE NETWORKSERVER ACCESS TOKEN THAT YOU GENERATED

The TLS certificate we generated for the discovery server and the NetworkServer are not trusted by the operating system, so we have to manually trust them. For the discovery server, we'll do the same as in the Router, for the NetworkServer, we can append the certificate to the ca.cert file or we can copy the NetworkServer's certificate and set the networkserver-cert in the configuration to broker/networkserver.cert. We will do the latter.

cat discovery/server.cert > broker/ca.cert
cp networkserver/server.cert broker/networkserver.cert

Again we have to generate a public/private keypair and a TLS certificate:

ttn broker gen-keypair --config ./broker/ttn.yml
ttn broker gen-cert --config ./broker/ttn.yml

Now we will tell the discovery server which DevAddr prefix we will handle with this Broker. In this case, we use a prefix that TTN reserved for private networks: 26000000/20. This prefix allows you to issue 4096 distinct addresses to devices in your private network, which should be more than enough (remember that device addresses are not unique; you can easily give 100 devices the exact same address). If you need a larger address space, you should file a request with The Things Network Foundation. If you are setting up a large network, you might have to apply for your own NetID from the LoRa Alliance, which will give you your own prefix.

ttn broker register-prefix 26000000/20 --config ./broker/ttn.yml

The Handler

The configuration for the Handler will be stored in ~/ttn/handler/ttn.yml:

id: mynetwork-handler
tls: true
key-dir: handler/
auth-servers:
  ttn-account-v2: "https://account.thethingsnetwork.org"

discovery-address: "localhost:1900"
auth-token: THE DISCOVERY ACCESS TOKEN THAT YOU GENERATED FOR THE **HANDLER**

handler:
  server-address-announce: localhost
  broker-id: mynetwork-broker
  mqtt-address: localhost:1883

Again we have to copy the discovery server certificate and generate a public/private keypair and a TLS certificate:

cat discovery/server.cert > handler/ca.cert
ttn handler gen-keypair --config ./handler/ttn.yml
ttn handler gen-cert --config ./handler/ttn.yml

The Bridge

TTN's backend components communicate using gRPC. Gateways, use different protocols, which are translated to gRPC with so-called bridges. The widely used packet_forwarder protocol is handled by the gateway-connector-bridge. If you followed this guide from the start, you should already have the correct version.

Unfortunately, this bridge does not support configuration files, so we will use CLI flags to configure it. We still need to have the discovery server certificate, so let's create an empty directory for the bridge and copy the discovery server certificate to the ca.cert file:

cat discovery/server.cert > bridge/ca.cert

We will need the following command line arguments to connect the bridge to your private backend:

--ttn-discovery-server "localhost:1900"
--root-ca-file "bridge/ca.cert"
--ttn-router "mynetwork-router"

Starting Everything Together

Just run each of the following commands in separate tabs:

ttn discovery --config discovery/ttn.yml
ttn router --config router/ttn.yml
ttn networkserver --config networkserver/ttn.yml
ttn broker --config broker/ttn.yml
ttn handler --config handler/ttn.yml
gateway-connector-bridge  --root-ca-file "bridge/ca.cert"  --ttn-router "localhost:1900/mynetwork-router"

You can also use a Procfile as shown below and use (for example) foreman or forego to start everything.

dsc: ttn discovery --config discovery/ttn.yml
rtr: ttn router --config router/ttn.yml
ns: ttn networkserver --config networkserver/ttn.yml
brk: ttn broker --config broker/ttn.yml
hdl: ttn handler --config handler/ttn.yml
br: gateway-connector-bridge --root-ca-file "bridge/ca.cert"  --ttn-router "localhost:1900/mynetwork-router"

ttnctl

Configuration of ttnctl is done by editing $XDG_CONFIG_HOME/config.yml or ~/.ttnctl.yml:

discovery-address: localhost:1900
mqtt-address: localhost:1883
router-id: mynetwork-router
handler-id: mynetwork-handler

ttnctl also needs to have the discovery server certificate, for that we have to create a ca.cert file in $XDG_DATA_HOME/ttnctl, $XDG_CACHE_HOME/ttnctl or ~/.ttnctl/

touch ~/.ttnctl/ca.cert
cat discovery/server.cert >> ~/.ttnctl/ca.cert

Now you can register your application to your private Handler:

ttnctl applications register

And manage your devices in the same way as you would do on the public community network.

Questions or Issues

If you have questions or remarks after following this guide, feel free use the forum or the #private-backend channel on Slack. This is a community-supported guide, so please help each other out.

What's Next

You should now have a fully operational private backend. Now you should probably do some of the following: