Skip to content

Commit 001349e

Browse files
authored
MPX-24 - Add support for CoAP and DTLS (#60)
* feat: Add coap proxy Signed-off-by: 1998-felix <felix.gateru@gmail.com> * refactor: Refactor tls.go for load tls and dtls Signed-off-by: 1998-felix <felix.gateru@gmail.com> * refactor: refactor loadtls and loaddtls functions Signed-off-by: 1998-felix <felix.gateru@gmail.com> * refactor: refactor load tls and dtls using generics Signed-off-by: 1998-felix <felix.gateru@gmail.com> * refactor: rebase Signed-off-by: 1998-felix <felix.gateru@gmail.com> * refactor: merge listen and listendtls Signed-off-by: 1998-felix <felix.gateru@gmail.com> * feat: add examples for coap client, update readme Signed-off-by: 1998-felix <felix.gateru@gmail.com> * feat: add server for coap example Signed-off-by: 1998-felix <felix.gateru@gmail.com> * feat: add cancel observation Signed-off-by: 1998-felix <felix.gateru@gmail.com> * feat: add udp and dtls proxy Signed-off-by: 1998-felix <felix.gateru@gmail.com> * refactor: rename mproxy to mgate Signed-off-by: Felix Gateru <felix.gateru@gmail.com> * chore: update dependencies Signed-off-by: Felix Gateru <felix.gateru@gmail.com> * refactor: use plgd-dev/go-coap for parsing Signed-off-by: Felix Gateru <felix.gateru@gmail.com> * refactor: clean up code Signed-off-by: Felix Gateru <felix.gateru@gmail.com> * fix: revert port number change Signed-off-by: Felix Gateru <felix.gateru@gmail.com> * ci: fix linter setup Signed-off-by: Felix Gateru <felix.gateru@gmail.com> * refactor: add context timeout Signed-off-by: Felix Gateru <felix.gateru@gmail.com> * refactor: resolve mutex before logging Signed-off-by: Felix Gateru <felix.gateru@gmail.com> --------- Signed-off-by: 1998-felix <felix.gateru@gmail.com> Signed-off-by: Felix Gateru <felix.gateru@gmail.com>
1 parent 94a848b commit 001349e

File tree

23 files changed

+1035
-444
lines changed

23 files changed

+1035
-444
lines changed

.env

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,17 @@ MGATE_HTTP_WITH_MTLS_SERVER_CA_FILE=ssl/certs/ca.crt
9090
MGATE_HTTP_WITH_MTLS_CLIENT_CA_FILE=ssl/certs/ca.crt
9191
MGATE_HTTP_WITH_MTLS_CERT_VERIFICATION_METHODS=ocsp
9292
MGATE_HTTP_WITH_MTLS_OCSP_RESPONDER_URL=http://localhost:8080/ocsp
93+
94+
MGATE_COAP_WITHOUT_DTLS_HOST=
95+
MGATE_COAP_WITHOUT_DTLS_PORT=5682
96+
MGATE_COAP_WITHOUT_DTLS_TARGET_HOST=localhost
97+
MGATE_COAP_WITHOUT_DTLS_TARGET_PORT=5683
98+
99+
MGATE_COAP_WITH_DTLS_HOST=localhost
100+
MGATE_COAP_WITH_DTLS_PORT=5684
101+
MGATE_COAP_WITH_DTLS_TARGET_HOST=localhost
102+
MGATE_COAP_WITH_DTLS_TARGET_PORT=5683
103+
MGATE_COAP_WITH_DTLS_CERT_FILE=ssl/certs/server.crt
104+
MGATE_COAP_WITH_DTLS_KEY_FILE=ssl/certs/server.key
105+
MGATE_COAP_WITH_DTLS_SERVER_CA_FILE=ssl/certs/ca.crt
106+
MGATE_COAP_WITH_DTLS_CLIENT_CA_FILE=ssl/certs/ca.crt

.github/workflows/tests.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ jobs:
2323
- name: Setup Go
2424
uses: actions/setup-go@v5
2525
with:
26-
go-version: 1.22.x
26+
go-version: 1.25.x
2727
cache-dependency-path: "go.sum"
2828

2929
- name: golangci-lint
3030
uses: golangci/golangci-lint-action@v8
3131
with:
32-
version: v2.1.6
32+
version: v2.4.0
3333
args: --config .golangci.yaml
3434

3535
- name: Build Binaries

README.md

Lines changed: 92 additions & 50 deletions
Large diffs are not rendered by default.

cmd/main.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/absmach/mgate"
1515
"github.com/absmach/mgate/examples/simple"
16+
"github.com/absmach/mgate/pkg/coap"
1617
"github.com/absmach/mgate/pkg/http"
1718
"github.com/absmach/mgate/pkg/mqtt"
1819
"github.com/absmach/mgate/pkg/mqtt/websocket"
@@ -34,6 +35,9 @@ const (
3435
httpWithoutTLS = "MGATE_HTTP_WITHOUT_TLS_"
3536
httpWithTLS = "MGATE_HTTP_WITH_TLS_"
3637
httpWithmTLS = "MGATE_HTTP_WITH_MTLS_"
38+
39+
coapWithoutDTLS = "MGATE_COAP_WITHOUT_DTLS_"
40+
coapWithDTLS = "MGATE_COAP_WITH_DTLS_"
3741
)
3842

3943
func main() {
@@ -172,6 +176,30 @@ func main() {
172176
return httpMTLSProxy.Listen(ctx)
173177
})
174178

179+
// mGate server Configuration for CoAP without DTLS
180+
coapConfig, err := mgate.NewConfig(env.Options{Prefix: coapWithoutDTLS})
181+
if err != nil {
182+
panic(err)
183+
}
184+
185+
// mGate server for CoAP without DTLS
186+
coapProxy := coap.NewProxy(coapConfig, handler, logger)
187+
g.Go(func() error {
188+
return coapProxy.Listen(ctx)
189+
})
190+
191+
// mGate server Configuration for CoAP with DTLS
192+
coapDTLSConfig, err := mgate.NewConfig(env.Options{Prefix: coapWithDTLS})
193+
if err != nil {
194+
panic(err)
195+
}
196+
197+
// mGate server for CoAP with DTLS
198+
coapDTLSProxy := coap.NewProxy(coapDTLSConfig, handler, logger)
199+
g.Go(func() error {
200+
return coapDTLSProxy.Listen(ctx)
201+
})
202+
175203
g.Go(func() error {
176204
return StopSignalHandler(ctx, cancel, logger)
177205
})

config.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ import (
88

99
mptls "github.com/absmach/mgate/pkg/tls"
1010
"github.com/caarlos0/env/v11"
11+
"github.com/pion/dtls/v3"
1112
)
1213

1314
type Config struct {
14-
Host string `env:"HOST" envDefault:""`
15+
Host string `env:"HOST" envDefault:""`
1516
Port string `env:"PORT,required" envDefault:""`
1617
PathPrefix string `env:"PATH_PREFIX" envDefault:""`
1718
TargetHost string `env:"TARGET_HOST,required" envDefault:""`
1819
TargetPort string `env:"TARGET_PORT,required" envDefault:""`
1920
TargetProtocol string `env:"TARGET_PROTOCOL,required" envDefault:""`
2021
TargetPath string `env:"TARGET_PATH" envDefault:""`
2122
TLSConfig *tls.Config
23+
DTLSConfig *dtls.Config
2224
}
2325

2426
func NewConfig(opts env.Options) (Config, error) {
@@ -31,8 +33,11 @@ func NewConfig(opts env.Options) (Config, error) {
3133
if err != nil {
3234
return Config{}, err
3335
}
34-
35-
c.TLSConfig, err = mptls.Load(&cfg)
36+
c.TLSConfig, err = mptls.LoadTLSConfig(&cfg, &tls.Config{})
37+
if err != nil {
38+
return Config{}, err
39+
}
40+
c.DTLSConfig, err = mptls.LoadTLSConfig(&cfg, &dtls.Config{})
3641
if err != nil {
3742
return Config{}, err
3843
}

examples/client/coap/with_dtls.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/bin/bash
2+
protocol=coaps
3+
host=localhost
4+
port=5684
5+
path="test"
6+
content=0x32
7+
message="{\"message\": \"Hello mGate\"}"
8+
auth="TOKEN"
9+
cafile=ssl/certs/ca.crt
10+
certfile=ssl/certs/client.crt
11+
keyfile=ssl/certs/client.key
12+
13+
echo "Posting message to ${protocol}://${host}:${port}/${path} with dtls ..."
14+
coap-client -m post coap://${host}:${port}/${path} -e "${message}" -O 12,${content} -O 15,auth=${auth} \
15+
-c $certfile -k $keyfile -C $cafile
16+
17+
echo "Getting message from ${protocol}://${host}:${port}/${path} with dtls ..."
18+
coap-client -m get coaps://${host}:${port}/${path} -O 6,0x00 -O 15,auth=${auth} -c $certfile -k $keyfile -C $cafile
19+
20+
echo "Posting message to ${protocol}://${host}:${port}/${path} with dtls and invalid client certificate..."
21+
coap-client -m post ${protocol}://${host}:${port}/${path} -e "${message}" -O 12,${content} -O 15,auth=${auth} \
22+
-c ssl/certs/client_unknown.crt -j ssl/certs/client_unknown.key -C "$cafile"
23+
24+
echo "Getting message from ${protocol}://${host}:${port}/${path} with dtls and invalid client certificate..."
25+
coap-client -m get ${protocol}://${host}:${port}/${path} -O 6,0x00 -O 15,auth=${auth} -c ssl/certs/client_unknown.crt -j ssl/certs/client_unknown.key -C "$cafile"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/bash
2+
protocol=coap
3+
host=localhost
4+
port=5682
5+
path="test"
6+
content=0x32
7+
message="{\"message\": \"Hello mGate\"}"
8+
auth="TOKEN"
9+
10+
#Examples using lib-coap coap-client
11+
echo "Posting message to ${protocol}://${host}:${port}/${path} without tls ..."
12+
coap-client -m post coap://${host}:${port}/${path} -e "${message}" -O 12,${content} -O 15,auth=${auth}
13+
14+
echo "Getting message from ${protocol}://${host}:${port}/${path} without tls ..."
15+
coap-client -m get coap://${host}:${port}/${path} -O 6,0x00 -O 15,auth=${auth}
16+
17+
#Examples using Magistrala coap-cli
18+
echo "Posting message to ${protocol}://${host}:${port}/${path} without tls ..."
19+
coap-cli post ${host}:${port}/${path} -d "${message}" -O 12,${content} -O 15,auth=${auth}
20+
21+
echo "Getting message from ${protocol}://${host}:${port}/${path} without tls ..."
22+
coap-cli get ${host}:${port}/${path} -O 6,0x00 -O 15,auth=${auth}

examples/server/coap/main.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) Abstract Machines
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"fmt"
8+
"log"
9+
"strings"
10+
11+
coap "github.com/plgd-dev/go-coap/v3"
12+
"github.com/plgd-dev/go-coap/v3/message"
13+
"github.com/plgd-dev/go-coap/v3/message/codes"
14+
"github.com/plgd-dev/go-coap/v3/mux"
15+
)
16+
17+
const defaultPort = "5683"
18+
19+
func handleRequest(w mux.ResponseWriter, r *mux.Message) {
20+
resp := w.Conn().AcquireMessage(r.Context())
21+
defer w.Conn().ReleaseMessage(resp)
22+
resp.SetCode(codes.Content)
23+
resp.SetToken(r.Token())
24+
resp.SetContentFormat(message.TextPlain)
25+
resp.SetBody(strings.NewReader(fmt.Sprintf("%v OK", r.Code())))
26+
err := w.Conn().WriteMessage(resp)
27+
if err != nil {
28+
log.Printf("Cannot send response: %v", err)
29+
}
30+
}
31+
32+
func main() {
33+
r := mux.NewRouter()
34+
r.DefaultHandle(mux.HandlerFunc(handleRequest))
35+
log.Println("starting coap server, listening on port " + defaultPort)
36+
log.Fatal(coap.ListenAndServe("udp", ":"+defaultPort, r))
37+
}

go.mod

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@ require (
88
github.com/google/uuid v1.6.0
99
github.com/gorilla/websocket v1.5.3
1010
github.com/joho/godotenv v1.5.1
11+
github.com/pion/dtls/v3 v3.0.6
12+
github.com/plgd-dev/go-coap/v3 v3.4.0
1113
golang.org/x/crypto v0.41.0
1214
golang.org/x/sync v0.16.0
1315
)
1416

15-
require golang.org/x/net v0.43.0 // indirect
17+
require (
18+
github.com/dsnet/golib/memfile v1.0.0 // indirect
19+
github.com/pion/logging v0.2.3 // indirect
20+
github.com/pion/transport/v3 v3.0.7 // indirect
21+
go.uber.org/atomic v1.11.0 // indirect
22+
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
23+
golang.org/x/net v0.43.0 // indirect
24+
golang.org/x/sys v0.35.0 // indirect
25+
)

go.sum

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
22
github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
3+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/dsnet/golib/memfile v1.0.0 h1:J9pUspY2bDCbF9o+YGwcf3uG6MdyITfh/Fk3/CaEiFs=
6+
github.com/dsnet/golib/memfile v1.0.0/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64=
37
github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
48
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
59
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -8,11 +12,29 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
812
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
913
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
1014
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
15+
github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E=
16+
github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU=
17+
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
18+
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
19+
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
20+
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
21+
github.com/plgd-dev/go-coap/v3 v3.4.0 h1:ZoGYFDv94xboP+41yW458fLDuYui+4eTgamqp3XJ7k4=
22+
github.com/plgd-dev/go-coap/v3 v3.4.0/go.mod h1:azpceqoHFeGzzNVm3RX4ox6xKHLOJ+pD0emPpr7FDXA=
23+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
24+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
25+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
26+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
27+
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
28+
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
1129
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
1230
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
13-
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
14-
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
31+
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
32+
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
1533
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
1634
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
1735
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
1836
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
37+
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
38+
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
39+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
40+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)