Skip to content

Commit

Permalink
dcs: Implement claiming with Gateway Controller
Browse files Browse the repository at this point in the history
  • Loading branch information
johanstokking committed Jul 29, 2024
1 parent 9d84679 commit 53efc1b
Show file tree
Hide file tree
Showing 12 changed files with 349 additions and 45 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ require (
go.packetbroker.org/api/mapping/v2 v2.3.2
go.packetbroker.org/api/routing v1.9.2
go.packetbroker.org/api/v3 v3.17.1
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240723094213-b40a14f3b543
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240729145607-ea516688afbd
go.thethings.industries/pkg/ca v0.0.0-20240723151912-b9bb4097ae6c
go.thethings.network/lorawan-application-payload v0.0.0-20220125153912-1198ff1e403e
go.thethings.network/lorawan-stack-legacy/v2 v2.1.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -654,8 +654,8 @@ go.packetbroker.org/api/routing v1.9.2 h1:J4+4vYZxa60UWC70Y9yy7sktU7DXaAp9Q13Bfq
go.packetbroker.org/api/routing v1.9.2/go.mod h1:kd2K7gieDI35YfPA8/zDmLX3qiKPuXia/MA77BEAeUA=
go.packetbroker.org/api/v3 v3.17.1 h1:LcyFPUGqVubGWMvQ16tZlQIKd+noGx7urzEYhSLiEQA=
go.packetbroker.org/api/v3 v3.17.1/go.mod h1:6bVbdWAYLnvZ5kgXxA7GBQvZTN7vxI0DoF1Di1NoAT4=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240723094213-b40a14f3b543 h1:CpDA1J3O/krqQrPypf+ePIV5xiLyy9RIayLXRnxiDSI=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240723094213-b40a14f3b543/go.mod h1:2+WsMwIunNLh22oauBzGL56JazE3UY34W1fstqEbacw=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240729145607-ea516688afbd h1:FvD516hdD/iWqoS20SFdcoiUgwRJP3egNXNiN+Ux2d0=
go.thethings.industries/pkg/api/gen/tti/gateway v0.0.0-20240729145607-ea516688afbd/go.mod h1:2+WsMwIunNLh22oauBzGL56JazE3UY34W1fstqEbacw=
go.thethings.industries/pkg/ca v0.0.0-20240723151912-b9bb4097ae6c h1:QkZ+O889SvaXAoJdIu2hyrAXvlIfuHDWOmlpOh97RHg=
go.thethings.industries/pkg/ca v0.0.0-20240723151912-b9bb4097ae6c/go.mod h1:89OU623VYKW9i3W4CZgIGFmtgb/jsN8JV2PAuCsj+7w=
go.thethings.network/lorawan-application-payload v0.0.0-20220125153912-1198ff1e403e h1:TWGQ3lh7gI2W5hnb6qPdpoAa0d7s/XPwvgf2VVCMJaY=
Expand Down
2 changes: 1 addition & 1 deletion pkg/deviceclaimingserver/deviceclaimingserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func New(c *component.Component, conf *Config, opts ...Option) (*DeviceClaimingS
}

if dcs.grpc.gatewayClaimingServer == nil {
upstream, err := gateways.NewUpstream(ctx, conf.GatewayClaimingServerConfig)
upstream, err := gateways.NewUpstream(ctx, c, conf.GatewayClaimingServerConfig)
if err != nil {
return nil, err
}
Expand Down
34 changes: 30 additions & 4 deletions pkg/deviceclaimingserver/gateways/gateways.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,34 @@ package gateways

import (
"context"
"crypto/tls"
"strings"

"go.thethings.network/lorawan-stack/v3/pkg/config"
"go.thethings.network/lorawan-stack/v3/pkg/config/tlsconfig"
"go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways/ttgc"
dcstypes "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/types"
"go.thethings.network/lorawan-stack/v3/pkg/errors"
"go.thethings.network/lorawan-stack/v3/pkg/types"
)

// Component is the interface to the component.
type Component interface {
GetBaseConfig(context.Context) config.ServiceBase
GetTLSClientConfig(context.Context, ...tlsconfig.Option) (*tls.Config, error)
}

// Config is the configuration for the Gateway Claiming Server.
type Config struct {
CreateOnNotFound bool `name:"create-on-not-found" description:"DEPRECATED"` // nolint:lll
DefaultGatewayServerAddress string `name:"default-gateway-server-address" description:"The default Gateway Server Address"` // nolint:lll
Upstreams map[string][]string `name:"upstreams" description:"Map of upstream type and the supported Gateway EUI ranges"` // nolint:lll
TTGC ttgc.Config `name:"ttgc"`
}

var errInvalidUpstream = errors.DefineInvalidArgument("invalid_upstream", "upstream `{name}` is invalid")
var (
errInvalidUpstream = errors.DefineInvalidArgument("invalid_upstream", "upstream `{name}` is invalid")
errTTGCNotEnabled = errors.DefineFailedPrecondition("ttgc_not_enabled", "TTGC is not enabled")
)

// ParseGatewayEUIRanges parses the configured upstream map and returns map of ranges.
func ParseGatewayEUIRanges(conf map[string][]string) (map[string][]dcstypes.EUI64Range, error) {
Expand Down Expand Up @@ -76,7 +87,7 @@ type Claimer interface {
// Claim claims a gateway.
Claim(ctx context.Context, eui types.EUI64, ownerToken string, clusterAddress string) error
// Unclaim unclaims a gateway.
Unclaim(context.Context, types.EUI64, string) error
Unclaim(ctx context.Context, eui types.EUI64) error
}

// rangeClaimer supports claiming a range of EUIs.
Expand All @@ -93,6 +104,7 @@ type Upstream struct {
// NewUpstream returns a new upstream based on the provided configuration.
func NewUpstream(
ctx context.Context,
c Component,
conf Config,
opts ...Option,
) (*Upstream, error) {
Expand All @@ -107,6 +119,17 @@ func NewUpstream(
if err != nil {
return nil, err
}

// Implicitly add TTGC if it is enabled and not already configured.
ttgcConf := c.GetBaseConfig(ctx).TTGC
if _, ttgcAdded := hosts["ttgc"]; ttgcConf.Enabled && !ttgcAdded {
ttgcRanges := make([]dcstypes.EUI64Range, len(ttgcConf.GatewayEUIs))
for i, prefix := range ttgcConf.GatewayEUIs {
ttgcRanges[i] = dcstypes.RangeFromEUI64Prefix(prefix)
}
hosts["ttgc"] = ttgcRanges
}

// Setup upstream table.
for name, ranges := range hosts {
if len(ranges) == 0 || name == "" {
Expand All @@ -115,7 +138,10 @@ func NewUpstream(
var claimer Claimer
switch name {
case "ttgc":
claimer, err = conf.TTGC.NewClient(ctx)
if !ttgcConf.Enabled {
return nil, errTTGCNotEnabled.New()
}
claimer, err = ttgc.New(ctx, c, ttgcConf)
if err != nil {
return nil, err
}
Expand Down
40 changes: 30 additions & 10 deletions pkg/deviceclaimingserver/gateways/gateways_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ package gateways_test
import (
"testing"

"go.thethings.network/lorawan-stack/v3/pkg/component"
componenttest "go.thethings.network/lorawan-stack/v3/pkg/component/test"
"go.thethings.network/lorawan-stack/v3/pkg/config"
"go.thethings.network/lorawan-stack/v3/pkg/config/tlsconfig"
"go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways"
"go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/gateways/ttgc"
dcstypes "go.thethings.network/lorawan-stack/v3/pkg/deviceclaimingserver/types"
"go.thethings.network/lorawan-stack/v3/pkg/errors"
"go.thethings.network/lorawan-stack/v3/pkg/ttgc"
"go.thethings.network/lorawan-stack/v3/pkg/types"
"go.thethings.network/lorawan-stack/v3/pkg/util/test"
"go.thethings.network/lorawan-stack/v3/pkg/util/test/assertions/should"
Expand All @@ -31,6 +35,25 @@ func TestUpstream(t *testing.T) {

a, ctx := test.New(t)

c := componenttest.NewComponent(t, &component.Config{
ServiceBase: config.ServiceBase{
TTGC: ttgc.Config{
Enabled: true,
GatewayEUIs: []types.EUI64Prefix{
{
EUI64: types.EUI64{0x58, 0xa0, 0xcb, 0xff, 0xfe, 0x80, 0x00, 0x00},
Length: 48,
},
},
TLS: tlsconfig.ClientAuth{
Source: "file",
Certificate: "testdata/client.pem",
Key: "testdata/client-key.pem",
},
},
},
})

// Invalid ranges.
ranges := map[string][]string{"ttgc": {"&S(FU*)"}}
euiPrefixes, err := gateways.ParseGatewayEUIRanges(ranges)
Expand Down Expand Up @@ -85,28 +108,25 @@ func TestUpstream(t *testing.T) {
})

// Invalid configurations
config := gateways.Config{
conf := gateways.Config{
Upstreams: map[string][]string{"ttgc": {"&S(FU*)"}},
TTGC: ttgc.Config{},
}
upstream, err := gateways.NewUpstream(ctx, config)
upstream, err := gateways.NewUpstream(ctx, c, conf)
a.So(errors.IsInvalidArgument(err), should.BeTrue)
a.So(upstream, should.BeNil)

config = gateways.Config{
conf = gateways.Config{
Upstreams: map[string][]string{"unsupported": {"58A0CBFFFE800000/48"}},
TTGC: ttgc.Config{},
}
upstream, err = gateways.NewUpstream(ctx, config)
upstream, err = gateways.NewUpstream(ctx, c, conf)
a.So(errors.IsInvalidArgument(err), should.BeTrue)
a.So(upstream, should.BeNil)

// Valid Configuration
config = gateways.Config{
conf = gateways.Config{
Upstreams: map[string][]string{"ttgc": {"58A0CBFFFE800000/48"}},
TTGC: ttgc.Config{},
}
upstream, err = gateways.NewUpstream(ctx, config)
upstream, err = gateways.NewUpstream(ctx, c, conf)
a.So(err, should.BeNil)
a.So(upstream, should.NotBeNil)

Expand Down
28 changes: 28 additions & 0 deletions pkg/deviceclaimingserver/gateways/testdata/client-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDF+is3SbxVrjeO
yooAeJfjAD9GylMI5Z4cfHjPgeTf2bZlfVnmx+q5rdrN6B/cQo1BEIqFHZ9B6oqx
CZUgBBg9yZx12XjQk4kWvGhEnuHfJI3c188iZdUt6aJHITKaGGkbu6JJzaJ+eUl+
pa4Ghae4gsP5qOgsp03LdSAZXByeP9E1nFu9MR1lJCfI2sBLoHLEIh3pA6YPhRvL
/aPBeCdKqnTVce61YnXxQQVHrA82smeICHRvx1KQWkvN7Y0icDSBh74vsRaaO+zP
suHTR5EmpmqNX5VZosoUrNCQHddAvQwBGZv/ULzykVPInKAzvHAnAKGyYmwdOrJM
fAVOQl3tAgMBAAECggEBALLy67kv1yKDNQjGnnLKjk/LW2a7Xs697mrFP9YhDSYh
fjLCWU63Cb4IHazc0l+fcFqNfwfPvLIyNGbNyJOF3/uJjvkfN4sgsFtytbTBAF1Y
hzpFf58R+N1lPx+YLEsJIYjF021uiCBVtU7apzCSAwZOfKHgQOyL1U/AcOE7V0rZ
v7efhQCSDJ76RAY1FuUDw8vk4kevWmWbjeVOoxnMjKfhOwL3Tfm8QXfc3USCv7Xk
85Od8mXxhrxRQLIk1RBYxhUj8uWSK0IvDE/jbyFEZQK10D8LnNWwbk1dg26nE3xu
oQbqUcF05ujlsDFGWCZ40d2mlu0Na/pskyjxu/9GQykCgYEA9lr8kVj+mTNamyPF
/ltc6l/+ofYDaMqZctZdYieZXg3J/6HN7y/xSDS5TBn4TVX7JvSw5rzoY3spzsO/
49ZbCR4bTxM+ZdVZkkLrtNzVonVAiSSLWpK+1oCPOO+1ULIUMmJanJHKkQ5R33iH
2BZaDSC2J11uJc1E+moXnr+YMu8CgYEAzbpUBcB3bmsG14kHh2PEcO03Gm9v7wuF
nPEcrANVuwAxrow/keM+oyKaj3g5EUvlZDMwh6PvlhOo3Xtp5gS+RRfkMVLcQPXd
Apf7B6OEPL54rwrcoZbxEl2+Hf3UUl9SIKastMCdtHzvLmCYh+12eM/R9Lp1D6DL
3sWQRw62DOMCgYEA1o7L5fLyWo9lXDS93hfIRsAwTvKqaXv6RQ/56ODALDAqO5+6
cZT5uX9h2qvLm99Ei9sUrwDcDLhZ4yCNYWtxgfFcq3QBJkO4bTAnhS/ISGOCP286
hznDR6JUGqx657sQ6AjNDgvTtp4YJF8fQM3GxCQ3QPWYVwf+CXKY+8O2VLUCgYEA
i75mtqV/OvTeZ+f/wjrFxEOOK5nIueLktq+dX0bApE7EcKF5yPpIoP2vaYcrlJEu
V7rh2zFDXHksOo5LZ+CO8lYBPnPfgwy/PLTJ4u1ytORZC+Xf6q+iP2yH6M1zvSRc
oCs3o2w3c9NtkN4ynhpyYCwinQ9O1vfNpBwlHe9jQCsCgYB3KRWR4UGc2faQbBE9
fMmbdTkA79gkwvr1q4pRQlCGnOxeIE4vfR4RCkfRkBJimMSI2sNDHFiDQZ8DJq6Y
3cwaL4uUSo9MEdY2ZZxaGgxWaEu4DH+ZV0lJv/RDgTbjD0xwomxXOn/+9UM17Nf9
KYSfdCWcjW0kxJJ7PO4n8d65rQ==
-----END PRIVATE KEY-----
26 changes: 26 additions & 0 deletions pkg/deviceclaimingserver/gateways/testdata/client.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEWzCCAsOgAwIBAgIQNsTUWUKitSTLD+uN4zzpuzANBgkqhkiG9w0BAQsFADCB
hTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMS0wKwYDVQQLDCRqb2hh
bkBKb2hhbi0yLmxvY2FsIChKb2hhbiBTdG9ra2luZykxNDAyBgNVBAMMK21rY2Vy
dCBqb2hhbkBKb2hhbi0yLmxvY2FsIChKb2hhbiBTdG9ra2luZykwHhcNMTkwNjAx
MDAwMDAwWhcNMzAwOTAzMTYyMDU0WjBYMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxv
cG1lbnQgY2VydGlmaWNhdGUxLTArBgNVBAsMJGpvaGFuQEpvaGFuLTIubG9jYWwg
KEpvaGFuIFN0b2traW5nKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AMX6KzdJvFWuN47KigB4l+MAP0bKUwjlnhx8eM+B5N/ZtmV9WebH6rmt2s3oH9xC
jUEQioUdn0HqirEJlSAEGD3JnHXZeNCTiRa8aESe4d8kjdzXzyJl1S3pokchMpoY
aRu7oknNon55SX6lrgaFp7iCw/mo6CynTct1IBlcHJ4/0TWcW70xHWUkJ8jawEug
csQiHekDpg+FG8v9o8F4J0qqdNVx7rVidfFBBUesDzayZ4gIdG/HUpBaS83tjSJw
NIGHvi+xFpo77M+y4dNHkSamao1flVmiyhSs0JAd10C9DAEZm/9QvPKRU8icoDO8
cCcAobJibB06skx8BU5CXe0CAwEAAaNzMHEwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
JQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMB8GA1UdIwQY
MBaAFDzAUZ9AtGTILuMWdD2VfCm0D72DMBEGA1UdEQQKMAiCBjAwMDAwMTANBgkq
hkiG9w0BAQsFAAOCAYEAI8mhLKPl4qUlZ2g1vmVrAGTGc0M2dzs4Xp2gyys+puup
sP4pRPQnIrEJaZcI2mk/4TTOpyF1tmLGaZ0V/hMzf07I5vB/Kz+jdj0+3AGVixwR
+KehvHVn6njfcZqa3l4Q7pFWyQtb199M1XPwGkQSEzxthU7dKH/447T8HwKp3xoK
hKb3pNd0h0MXgGGhawHFP5AHpI0x05cWT5zLXu5nXnjYt9UKpPCUH6Htcofg33GX
Y3rj3KvUz9WHYw/6LE+LlKjUKLkCG8bq2qaTRSrJQh5qTJRldzqjJ4qa7A7hUYJN
qm+Wb/FIMIA/sdfdMEwGAWhkZJjZlYoGv3LU7QLIkaCWjRsTs1gRw2EVCo0mFJ6x
LRm0UDBIIr30fEkGhSk38EYiX3HbxkCF61HPehhzL4noMkoDAuMq+8gFRVDBHOlv
YiNn1Y22LBsFdoKkbqI3nI62sFvI8lXpKRrL34lndSCevVXOEUOeupDdD1XvHYHQ
0bPVWAqh/KAO8JMDauCa
-----END CERTIFICATE-----
62 changes: 62 additions & 0 deletions pkg/deviceclaimingserver/gateways/ttgc/root_ca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright © 2024 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package ttgc provides functions to use The Things Gateway Controller.
package ttgc

import (
"context"
"crypto/tls"
"crypto/x509"
"net"

"go.thethings.network/lorawan-stack/v3/pkg/errors"
)

var (
errDialGatewayServer = errors.DefineAborted("dial_gateway_server", "failed to dial Gateway Gerver")
errGatewayServerTLS = errors.DefineAborted(
"gateway_server_tls", "failed to establish TLS connection with Gateway Server",
)
)

func getRootCA(ctx context.Context, address string) (*x509.Certificate, error) {
host, _, err := net.SplitHostPort(address)
if err != nil {
host = address
}
address = net.JoinHostPort(host, "8889")

d := new(net.Dialer)
netConn, err := d.DialContext(ctx, "tcp", address)
if err != nil {
return nil, errDialGatewayServer.WithCause(err)
}
defer netConn.Close()

tlsConn := tls.Client(netConn, &tls.Config{
InsecureSkipVerify: true, //nolint:gosec
ServerName: host,
})
if err := tlsConn.HandshakeContext(ctx); err != nil {
return nil, errGatewayServerTLS.WithCause(err)
}

state := tlsConn.ConnectionState()
if len(state.PeerCertificates) == 0 {
return nil, errGatewayServerTLS.New()
}

return state.PeerCertificates[len(state.PeerCertificates)-1], nil
}
Loading

0 comments on commit 53efc1b

Please sign in to comment.