Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MPX-24 - Add support for CoAP and DTLS #60

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
12 changes: 12 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,15 @@ MPROXY_HTTP_WITH_MTLS_SERVER_CA_FILE=ssl/certs/ca.crt
MPROXY_HTTP_WITH_MTLS_CLIENT_CA_FILE=ssl/certs/ca.crt
MPROXY_HTTP_WITH_MTLS_CERT_VERIFICATION_METHODS=ocsp
MPROXY_HTTP_WITH_MTLS_OCSP_RESPONDER_URL=http://localhost:8080/ocsp

MPROXY_COAP_WITHOUT_DTLS_ADDRESS=:5682
MPROXY_COAP_WITHOUT_DTLS_TARGET=localhost:5683

MPROXY_COAP_WITH_DTLS_ADDRESS=:5684
MPROXY_COAP_WITH_DTLS_TARGET=localhost:5683
MPROXY_COAP_WITH_DTLS_CERT_FILE=ssl/certs/server.crt
MPROXY_COAP_WITH_DTLS_KEY_FILE=ssl/certs/server.key
MPROXY_COAP_WITH_DTLS_SERVER_CA_FILE=ssl/certs/ca.crt
MPROXY_COAP_WITH_DTLS_CLIENT_CA_FILE=ssl/certs/ca.crt


28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ LB tasks can be offloaded to a standard ingress proxy - for example, NginX.
- Golang
- Mosquitto MQTT Server
- Mosquitto Publisher and Subscriber Client
- coap-client or Magistrala coap-cli

### Example Setup of mProxy

Expand Down Expand Up @@ -122,6 +123,8 @@ mProxy is used to proxy requests to a backend server. For the example setup, we
- mProxy server for `HTTP protocol without TLS` on port `8086` with prefix path `/messages`
- mProxy server for `HTTP protocol with TLS` on port `8087` with prefix path `/messages`
- mProxy server for `HTTP protocol with mTLS` on port `8088` with prefix path `/messages`
- mProxy server for `COAP protocol without DTLS` on port `5682`
- mProxy server for `COAP protocol with DTLS` on port `5684`

### Example testing of mProxy

Expand Down Expand Up @@ -191,6 +194,23 @@ Bash scripts available in `examples/client/http` directory help to test the mPro
examples/client/http/with_mtls.sh
```

### Test mProxy server for CoAP protocols
felixgateru marked this conversation as resolved.
Show resolved Hide resolved

Bash scripts available in `example/client/coap` directory help to test the mProxy servers running for CoAP protocols. You will require to have either the [coap-client](https://libcoap.net/doc/reference/4.3.1/man_coap-client.html) or the [Magistrala coap-cli](https://github.com/absmach/coap-cli).
The script can be used alongside the simple go-coap server provided at `example/server/coap`.

- Script to test mProxy server running at 5682 for CoAP without DTLS

```bash
examples/client/coap/without_dtls.sh
```

- Script to test mProxy server running at 5684 for CoAP with DTLS

```bash
examples/client/coap/with_dtls.sh
```

## Configuration

The service is configured using the environment variables presented in the following table. Note that any unset variables will be replaced with their default values.
Expand Down Expand Up @@ -246,6 +266,14 @@ The service is configured using the environment variables presented in the follo
| MPROXY_HTTP_WITH_MTLS_CLIENT_CA_FILE | HTTP with mTLS client CA file path | ssl/certs/ca.crt |
| MPROXY_HTTP_WITH_MTLS_CERT_VERIFICATION_METHODS | HTTP with mTLS certificate verification methods, if no value or unset then mProxy server will not do client validation | ocsp |
| MPROXY_HTTP_WITH_MTLS_OCSP_RESPONDER_URL | HTTP with mTLS OCSP responder URL, it is used if OCSP responder URL is not available in client certificate AIA | <http://localhost:8080/ocsp> |
| MPROXY_COAP_WITHOUT_DTLS_ADDRESS | CoAP without DTLS inbound (IN) connection listening address | localhost:5682 |
| MPROXY_COAP_WITH_DTLS_TARGET | CoAP without DTLS outbound (OUT) connection | localhost:5683 |
| MPROXY_COAP_WITH_DTLS_ADDRESS | CoAP with DTLS inbound (IN) connection listening address | localhost:5684 |
| MPROXY_COAP_WITH_DTLS_TARGET | CoAP with DTLS outbound (OUT) connection | localhost:5683 |
| MPROXY_COAP_WITH_DTLS_CERT_FILE | CoAP with DTLS certificate file | ssl/certs/server.crt |
| MPROXY_COAP_WITH_DTLS_KEY_FILE | CoAP with DTLS key file | ssl/certs/server.key |
| MPROXY_COAP_WITH_DTLS_SERVER_CA_FILE | CoAP with DTLS server CA file | ssl/certs/ca.crt |
| MPROXY_COAP_WITH_DTLS_CLIENT_CA_FILE | CoAP with DTLS client CA file | ssl/certs/ca.crt |

## mProxy Configuration Environment Variables

Expand Down
40 changes: 34 additions & 6 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/absmach/mproxy"
"github.com/absmach/mproxy/examples/simple"
"github.com/absmach/mproxy/pkg/coap"
"github.com/absmach/mproxy/pkg/http"
"github.com/absmach/mproxy/pkg/mqtt"
"github.com/absmach/mproxy/pkg/mqtt/websocket"
Expand All @@ -34,6 +35,9 @@ const (
httpWithoutTLS = "MPROXY_HTTP_WITHOUT_TLS_"
httpWithTLS = "MPROXY_HTTP_WITH_TLS_"
httpWithmTLS = "MPROXY_HTTP_WITH_MTLS_"

coapWithoutDTLS = "MPROXY_COAP_WITHOUT_DTLS_"
coapWithDTLS = "MPROXY_COAP_WITH_DTLS_"
)

func main() {
Expand Down Expand Up @@ -62,7 +66,7 @@ func main() {
}

// mProxy server for MQTT without TLS
mqttProxy := mqtt.New(mqttConfig, handler, interceptor, logger)
mqttProxy := mqtt.NewProxy(mqttConfig, handler, interceptor, logger)
g.Go(func() error {
return mqttProxy.Listen(ctx)
})
Expand All @@ -74,7 +78,7 @@ func main() {
}

// mProxy server for MQTT with TLS
mqttTLSProxy := mqtt.New(mqttTLSConfig, handler, interceptor, logger)
mqttTLSProxy := mqtt.NewProxy(mqttTLSConfig, handler, interceptor, logger)
g.Go(func() error {
return mqttTLSProxy.Listen(ctx)
})
Expand All @@ -86,7 +90,7 @@ func main() {
}

// mProxy server for MQTT with mTLS
mqttMTlsProxy := mqtt.New(mqttMTLSConfig, handler, interceptor, logger)
mqttMTlsProxy := mqtt.NewProxy(mqttMTLSConfig, handler, interceptor, logger)
g.Go(func() error {
return mqttMTlsProxy.Listen(ctx)
})
Expand All @@ -98,7 +102,7 @@ func main() {
}

// mProxy server for MQTT over Websocket without TLS
wsProxy := websocket.New(wsConfig, handler, interceptor, logger)
wsProxy := websocket.NewProxy(wsConfig, handler, interceptor, logger)
g.Go(func() error {
return wsProxy.Listen(ctx)
})
Expand All @@ -110,7 +114,7 @@ func main() {
}

// mProxy server for MQTT over Websocket with TLS
wsTLSProxy := websocket.New(wsTLSConfig, handler, interceptor, logger)
wsTLSProxy := websocket.NewProxy(wsTLSConfig, handler, interceptor, logger)
g.Go(func() error {
return wsTLSProxy.Listen(ctx)
})
Expand All @@ -122,7 +126,7 @@ func main() {
}

// mProxy server for MQTT over Websocket with mTLS
wsMTLSProxy := websocket.New(wsMTLSConfig, handler, interceptor, logger)
wsMTLSProxy := websocket.NewProxy(wsMTLSConfig, handler, interceptor, logger)
g.Go(func() error {
return wsMTLSProxy.Listen(ctx)
})
Expand Down Expand Up @@ -172,6 +176,30 @@ func main() {
return httpMTLSProxy.Listen(ctx)
})

// mProxy server Configuration for CoAP without DTLS
coapConfig, err := mproxy.NewConfig(env.Options{Prefix: coapWithoutDTLS})
if err != nil {
panic(err)
}

// mProxy server for CoAP without DTLS
coapProxy := coap.NewProxy(coapConfig, handler, logger)
g.Go(func() error {
return coapProxy.Listen(ctx)
})

// mProxy server Configuration for CoAP with DTLS
coapDTLSConfig, err := mproxy.NewConfig(env.Options{Prefix: coapWithDTLS})
if err != nil {
panic(err)
}

// mProxy server for CoAP with DTLS
coapDTLSProxy := coap.NewProxy(coapDTLSConfig, handler, logger)
g.Go(func() error {
return coapDTLSProxy.Listen(ctx)
})

g.Go(func() error {
return StopSignalHandler(ctx, cancel, logger)
})
Expand Down
9 changes: 7 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import (

mptls "github.com/absmach/mproxy/pkg/tls"
"github.com/caarlos0/env/v11"
"github.com/pion/dtls/v2"
)

type Config struct {
Address string `env:"ADDRESS" envDefault:""`
PathPrefix string `env:"PATH_PREFIX" envDefault:"/"`
felixgateru marked this conversation as resolved.
Show resolved Hide resolved
Target string `env:"TARGET" envDefault:""`
TLSConfig *tls.Config
DTLSConfig *dtls.Config
felixgateru marked this conversation as resolved.
Show resolved Hide resolved
}

func NewConfig(opts env.Options) (Config, error) {
Expand All @@ -27,8 +29,11 @@ func NewConfig(opts env.Options) (Config, error) {
if err != nil {
return Config{}, err
}

c.TLSConfig, err = mptls.Load(&cfg)
c.TLSConfig, err = mptls.LoadTLSConfig(&cfg, &tls.Config{})
if err != nil {
return Config{}, err
}
c.DTLSConfig, err = mptls.LoadTLSConfig(&cfg, &dtls.Config{})
if err != nil {
return Config{}, err
}
Expand Down
25 changes: 25 additions & 0 deletions examples/client/coap/with_dtls.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
protocol=coaps
host=localhost
port=5684
path="test"
content=0x32
message="{\"message\": \"Hello mProxy\"}"
auth="TOKEN"
cafile=ssl/certs/ca.crt
certfile=ssl/certs/client.crt
keyfile=ssl/certs/client.key

echo "Posting message to ${protocol}://${host}:${port}/${path} with dtls ..."
coap-client -m post coap://${host}:${port}/${path} -e "${message}" -O 12,${content} -O 15,auth=${auth} \
felixgateru marked this conversation as resolved.
Show resolved Hide resolved
-c $certfile -k $keyfile -C $cafile

echo "Getting message from ${protocol}://${host}:${port}/${path} with dtls ..."
coap-client -m get coap://${host}:${port}/${path} -O 6,0x00 -O 15,auth=${auth} -c $certfile -k $keyfile -C $cafile

echo "Posting message to ${protocol}://${host}:${port}/${path} with dtls and invalid client certificate..."
coap-client -m post coap://${host}:${port}/${path} -e "${message}" -O 12,${content} -O 15,auth=${auth} \
-c ssl/certs/client_unknown.crt -k ssl/certs/client_unknown.key -C $cafile

echo "Getting message from ${protocol}://${host}:${port}/${path} with dtls and invalid client certificate..."
coap-client -m get coap://${host}:${port}/${path} -O 6,0x00 -O 15,auth=${auth} -c ssl/certs/client_unknown.crt -k ssl/certs/client_unknown.key -C $cafile
22 changes: 22 additions & 0 deletions examples/client/coap/without_dtls.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
protocol=coap
host=localhost
port=5682
path="test"
content=0x32
message="{\"message\": \"Hello mProxy\"}"
auth="TOKEN"

#Examples using lib-coap coap-client
echo "Posting message to ${protocol}://${host}:${port}/${path} without tls ..."
coap-client -m post coap://${host}:${port}/${path} -e "${message}" -O 12,${content} -O 15,auth=${auth}

echo "Getting message from ${protocol}://${host}:${port}/${path} without tls ..."
coap-client -m get coap://${host}:${port}/${path} -O 6,0x00 -O 15,auth=${auth}

#Examples using Magisrala coap-cli
echo "Posting message to ${protocol}://${host}:${port}/${path} without tls ..."
coap-cli post ${host}:${port}/${path} -d "${message}" -O 12,${content} -O 15,auth=${auth}

echo "Getting message from ${protocol}://${host}:${port}/${path} without tls ..."
coap-cli get ${host}:${port}/${path} -O 6,0x00 -O 15,auth=${auth}
37 changes: 37 additions & 0 deletions examples/server/coap/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0

package main

import (
"fmt"
"log"
"strings"

coap "github.com/plgd-dev/go-coap/v3"
"github.com/plgd-dev/go-coap/v3/message"
"github.com/plgd-dev/go-coap/v3/message/codes"
"github.com/plgd-dev/go-coap/v3/mux"
)

const defaultPort = "5683"

func handleRequest(w mux.ResponseWriter, r *mux.Message) {
resp := w.Conn().AcquireMessage(r.Context())
defer w.Conn().ReleaseMessage(resp)
resp.SetCode(codes.Content)
resp.SetToken(r.Token())
resp.SetContentFormat(message.TextPlain)
resp.SetBody(strings.NewReader(fmt.Sprintf("%v OK", r.Code())))
err := w.Conn().WriteMessage(resp)
if err != nil {
log.Printf("Cannot send response: %v", err)
}
}

func main() {
r := mux.NewRouter()
r.DefaultHandle(mux.HandlerFunc(handleRequest))
log.Println("starting coap server, listening on port " + defaultPort)
log.Fatal(coap.ListenAndServe("udp", ":"+defaultPort, r))
}
15 changes: 14 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,25 @@ toolchain go1.21.4

require (
github.com/caarlos0/env/v11 v11.0.0
github.com/dustin/go-coap v0.0.0-20190908170653-752e0f79981e
github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.1
github.com/joho/godotenv v1.5.1
github.com/pion/dtls/v2 v2.2.8-0.20240201071732-2597464081c8
github.com/plgd-dev/go-coap/v3 v3.3.3
golang.org/x/crypto v0.22.0
golang.org/x/sync v0.7.0
)

require golang.org/x/net v0.24.0 // indirect
require (
github.com/dsnet/golib/memfile v1.0.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/transport/v3 v3.0.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.19.0 // indirect
)
Loading
Loading