Merge branch 'dev/v5.0' into resolve-master-dev/v5.0-conflict-release-version

This commit is contained in:
Zaiming (Stone) Shi 2021-06-05 11:51:02 +02:00 committed by GitHub
commit 5dab6985c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
96 changed files with 3311 additions and 497 deletions

View File

@ -11,3 +11,4 @@ EMQX_AUTH__PGSQL__DATABASE=mqtt
EMQX_AUTH__REDIS__SERVER=redis_server:6379 EMQX_AUTH__REDIS__SERVER=redis_server:6379
EMQX_AUTH__REDIS__PASSWORD=public EMQX_AUTH__REDIS__PASSWORD=public
CUTTLEFISH_ENV_OVERRIDE_PREFIX=EMQX_ CUTTLEFISH_ENV_OVERRIDE_PREFIX=EMQX_
HOCON_ENV_OVERRIDE_PREFIX=EMQX_

View File

@ -86,7 +86,7 @@ jobs:
if: matrix.connect_type == 'tls' if: matrix.connect_type == 'tls'
run: | run: |
cat <<-EOF >> "$GITHUB_ENV" cat <<-EOF >> "$GITHUB_ENV"
EMQX_AUTH__MONGO__SSL=on EMQX_AUTH__MONGO__SSL__ENABLE=on
EMQX_AUTH__MONGO__SSL__CACERTFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem EMQX_AUTH__MONGO__SSL__CACERTFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/ca.pem
EMQX_AUTH__MONGO__SSL__CERTFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem EMQX_AUTH__MONGO__SSL__CERTFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-cert.pem
EMQX_AUTH__MONGO__SSL__KEYFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem EMQX_AUTH__MONGO__SSL__KEYFILE=/emqx/apps/emqx_auth_mongo/test/emqx_auth_mongo_SUITE_data/client-key.pem
@ -98,7 +98,7 @@ jobs:
MONGO_TAG: ${{ matrix.mongo_tag }} MONGO_TAG: ${{ matrix.mongo_tag }}
if: matrix.connect_type == 'tcp' if: matrix.connect_type == 'tcp'
run: | run: |
echo EMQX_AUTH__MONGO__SSL=off >> "$GITHUB_ENV" echo EMQX_AUTH__MONGO__SSL__ENABLE=off >> "$GITHUB_ENV"
- name: setup - name: setup
if: matrix.network_type == 'ipv4' if: matrix.network_type == 'ipv4'
run: | run: |
@ -160,10 +160,10 @@ jobs:
if: matrix.connect_type == 'tls' if: matrix.connect_type == 'tls'
run: | run: |
cat <<-EOF >> "$GITHUB_ENV" cat <<-EOF >> "$GITHUB_ENV"
EMQX_AUTH__MYSQL__SSL__ENABLE=on
EMQX_AUTH__MYSQL__USERNAME=ssluser EMQX_AUTH__MYSQL__USERNAME=ssluser
EMQX_AUTH__MYSQL__PASSWORD=public EMQX_AUTH__MYSQL__PASSWORD=public
EMQX_AUTH__MYSQL__DATABASE=mqtt EMQX_AUTH__MYSQL__DATABASE=mqtt
EMQX_AUTH__MYSQL__SSL=on
EMQX_AUTH__MYSQL__SSL__CACERTFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem EMQX_AUTH__MYSQL__SSL__CACERTFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/ca.pem
EMQX_AUTH__MYSQL__SSL__CERTFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem EMQX_AUTH__MYSQL__SSL__CERTFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-cert.pem
EMQX_AUTH__MYSQL__SSL__KEYFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem EMQX_AUTH__MYSQL__SSL__KEYFILE=/emqx/apps/emqx_auth_mysql/test/emqx_auth_mysql_SUITE_data/client-key.pem
@ -179,7 +179,7 @@ jobs:
EMQX_AUTH__MYSQL__USERNAME=root EMQX_AUTH__MYSQL__USERNAME=root
EMQX_AUTH__MYSQL__PASSWORD=public EMQX_AUTH__MYSQL__PASSWORD=public
EMQX_AUTH__MYSQL__DATABASE=mqtt EMQX_AUTH__MYSQL__DATABASE=mqtt
EMQX_AUTH__MYSQL__SSL=off EMQX_AUTH__MYSQL__SSL__ENABLE=off
EOF EOF
- name: setup - name: setup
if: matrix.network_type == 'ipv4' if: matrix.network_type == 'ipv4'
@ -239,7 +239,7 @@ jobs:
if: matrix.connect_type == 'tls' if: matrix.connect_type == 'tls'
run: | run: |
cat <<-EOF >> "$GITHUB_ENV" cat <<-EOF >> "$GITHUB_ENV"
EMQX_AUTH__PGSQL__SSL=on EMQX_AUTH__PGSQL__SSL__ENABLE=on
EMQX_AUTH__PGSQL__SSL__CACERTFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem EMQX_AUTH__PGSQL__SSL__CACERTFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/ca.pem
EMQX_AUTH__PGSQL__SSL__CERTFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem EMQX_AUTH__PGSQL__SSL__CERTFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-cert.pem
EMQX_AUTH__PGSQL__SSL__KEYFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem EMQX_AUTH__PGSQL__SSL__KEYFILE=/emqx/apps/emqx_auth_pgsql/test/emqx_auth_pgsql_SUITE_data/client-key.pem
@ -251,7 +251,7 @@ jobs:
PGSQL_TAG: ${{ matrix.pgsql_tag }} PGSQL_TAG: ${{ matrix.pgsql_tag }}
if: matrix.connect_type == 'tcp' if: matrix.connect_type == 'tcp'
run: | run: |
echo EMQX_AUTH__PGSQL__SSL=off >> "$GITHUB_ENV" echo EMQX_AUTH__PGSQL__SSL__ENABLE=off >> "$GITHUB_ENV"
- name: setup - name: setup
if: matrix.network_type == 'ipv4' if: matrix.network_type == 'ipv4'
run: | run: |
@ -318,7 +318,7 @@ jobs:
if: matrix.connect_type == 'tls' if: matrix.connect_type == 'tls'
run: | run: |
cat <<-EOF >> "$GITHUB_ENV" cat <<-EOF >> "$GITHUB_ENV"
EMQX_AUTH__REDIS__SSL=on EMQX_AUTH__REDIS__SSL__ENABLE=on
EMQX_AUTH__REDIS__SSL__CACERTFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt EMQX_AUTH__REDIS__SSL__CACERTFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/ca.crt
EMQX_AUTH__REDIS__SSL__CERTFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt EMQX_AUTH__REDIS__SSL__CERTFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.crt
EMQX_AUTH__REDIS__SSL__KEYFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key EMQX_AUTH__REDIS__SSL__KEYFILE=/emqx/apps/emqx_auth_redis/test/emqx_auth_redis_SUITE_data/certs/redis.key
@ -330,7 +330,7 @@ jobs:
REDIS_TAG: ${{ matrix.redis_tag }} REDIS_TAG: ${{ matrix.redis_tag }}
if: matrix.connect_type == 'tcp' if: matrix.connect_type == 'tcp'
run: | run: |
echo EMQX_AUTH__REDIS__SSL=off >> "$GITHUB_ENV" echo EMQX_AUTH__REDIS__SSL__ENABLE=off >> "$GITHUB_ENV"
- name: get server address - name: get server address
run: | run: |
ipv4_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis) ipv4_address=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis)

View File

@ -7,7 +7,7 @@
## Value: URL ## Value: URL
## ##
## Examples: http://127.0.0.1:80/mqtt/auth, https://[::1]:80/mqtt/auth ## Examples: http://127.0.0.1:80/mqtt/auth, https://[::1]:80/mqtt/auth
auth.http.auth_req.url = http://127.0.0.1:80/mqtt/auth auth.http.auth_req.url = "http://127.0.0.1:80/mqtt/auth"
## HTTP Request Method for Auth Request ## HTTP Request Method for Auth Request
## ##
@ -18,7 +18,8 @@ auth.http.auth_req.method = post
## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json ## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json
## ##
## Examples: auth.http.auth_req.headers.accept = */* ## Examples: auth.http.auth_req.headers.accept = */*
auth.http.auth_req.headers.content_type = application/x-www-form-urlencoded
auth.http.auth_req.headers.content_type = "application/x-www-form-urlencoded"
## Parameters used to construct the request body or query string parameters ## Parameters used to construct the request body or query string parameters
## When the request method is GET, these parameters will be converted into query string parameters ## When the request method is GET, these parameters will be converted into query string parameters
@ -35,14 +36,14 @@ auth.http.auth_req.headers.content_type = application/x-www-form-urlencoded
## - %d: subject of client TLS cert ## - %d: subject of client TLS cert
## ##
## Value: <K1>=<V1>,<K2>=<V2>,... ## Value: <K1>=<V1>,<K2>=<V2>,...
auth.http.auth_req.params = clientid=%c,username=%u,password=%P auth.http.auth_req.params = "clientid=%c,username=%u,password=%P"
## HTTP URL API path for SuperUser Request ## HTTP URL API path for SuperUser Request
## ##
## Value: URL ## Value: URL
## ##
## Examples: http://127.0.0.1:80/mqtt/superuser, https://[::1]:80/mqtt/superuser ## Examples: http://127.0.0.1:80/mqtt/superuser, https://[::1]:80/mqtt/superuser
auth.http.super_req.url = http://127.0.0.1:80/mqtt/superuser auth.http.super_req.url = "http://127.0.0.1:80/mqtt/superuser"
## HTTP Request Method for SuperUser Request ## HTTP Request Method for SuperUser Request
## ##
@ -53,7 +54,7 @@ auth.http.super_req.method = post
## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json ## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json
## ##
## Examples: auth.http.super_req.headers.accept = */* ## Examples: auth.http.super_req.headers.accept = */*
auth.http.super_req.headers.content-type = application/x-www-form-urlencoded auth.http.super_req.headers.content-type = "application/x-www-form-urlencoded"
## Parameters used to construct the request body or query string parameters ## Parameters used to construct the request body or query string parameters
## When the request method is GET, these parameters will be converted into query string parameters ## When the request method is GET, these parameters will be converted into query string parameters
@ -70,7 +71,7 @@ auth.http.super_req.headers.content-type = application/x-www-form-urlencoded
## - %d: subject of client TLS cert ## - %d: subject of client TLS cert
## ##
## Value: <K1>=<V1>,<K2>=<V2>,... ## Value: <K1>=<V1>,<K2>=<V2>,...
auth.http.super_req.params = clientid=%c,username=%u auth.http.super_req.params = "clientid=%c,username=%u"
## HTTP URL API path for ACL Request ## HTTP URL API path for ACL Request
## Comment out this config to disable ACL checks ## Comment out this config to disable ACL checks
@ -78,7 +79,7 @@ auth.http.super_req.params = clientid=%c,username=%u
## Value: URL ## Value: URL
## ##
## Examples: http://127.0.0.1:80/mqtt/acl, https://[::1]:80/mqtt/acl ## Examples: http://127.0.0.1:80/mqtt/acl, https://[::1]:80/mqtt/acl
auth.http.acl_req.url = http://127.0.0.1:80/mqtt/acl auth.http.acl_req.url = "http://127.0.0.1:80/mqtt/acl"
## HTTP Request Method for ACL Request ## HTTP Request Method for ACL Request
## ##
@ -89,7 +90,7 @@ auth.http.acl_req.method = post
## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json ## The possible values of the Content-Type header: application/x-www-form-urlencoded, application/json
## ##
## Examples: auth.http.acl_req.headers.accept = */* ## Examples: auth.http.acl_req.headers.accept = */*
auth.http.acl_req.headers.content-type = application/x-www-form-urlencoded auth.http.acl_req.headers.content-type = "application/x-www-form-urlencoded"
## Parameters used to construct the request body or query string parameters ## Parameters used to construct the request body or query string parameters
## When the request method is GET, these parameters will be converted into query string parameters ## When the request method is GET, these parameters will be converted into query string parameters
@ -108,7 +109,7 @@ auth.http.acl_req.headers.content-type = application/x-www-form-urlencoded
## - %t: topic ## - %t: topic
## ##
## Value: <K1>=<V1>,<K2>=<V2>,... ## Value: <K1>=<V1>,<K2>=<V2>,...
auth.http.acl_req.params = access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t,mountpoint=%m auth.http.acl_req.params = "access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t,mountpoint=%m"
## Time-out time for the request. ## Time-out time for the request.
## ##
@ -143,17 +144,17 @@ auth.http.pool_size = 32
## are used during server authentication and when building the client certificate chain. ## are used during server authentication and when building the client certificate chain.
## ##
## Value: File ## Value: File
## auth.http.ssl.cacertfile = {{ platform_etc_dir }}/certs/ca.pem ## auth.http.ssl.cacertfile = "{{ platform_etc_dir }}/certs/ca.pem"
## The path to a file containing the client's certificate. ## The path to a file containing the client's certificate.
## ##
## Value: File ## Value: File
## auth.http.ssl.certfile = {{ platform_etc_dir }}/certs/client-cert.pem ## auth.http.ssl.certfile = "{{ platform_etc_dir }}/certs/client-cert.pem"
## Path to a file containing the client's private PEM-encoded key. ## Path to a file containing the client's private PEM-encoded key.
## ##
## Value: File ## Value: File
## auth.http.ssl.keyfile = {{ platform_etc_dir }}/certs/client-key.pem ## auth.http.ssl.keyfile = "{{ platform_etc_dir }}/certs/client-key.pem"
## In mode verify_none the default behavior is to allow all x509-path ## In mode verify_none the default behavior is to allow all x509-path
## validation errors. ## validation errors.

View File

@ -19,7 +19,7 @@
{profiles, {profiles,
[{test, [{test,
[{deps, [{deps,
[{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}, [
{emqtt, {git, "https://github.com/emqx/emqtt", {tag, "v1.2.2"}}} {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "v1.2.2"}}}
]} ]}
]} ]}

View File

@ -10,13 +10,13 @@ auth.jwt.secret = emqxsecret
## RSA or ECDSA public key file. ## RSA or ECDSA public key file.
## ##
## Value: File ## Value: File
#auth.jwt.pubkey = etc/certs/jwt_public_key.pem #auth.jwt.pubkey = "etc/certs/jwt_public_key.pem"
## The JWKs server address ## The JWKs server address
## ##
## see: http://self-issued.info/docs/draft-ietf-jose-json-web-key.html ## see: http://self-issued.info/docs/draft-ietf-jose-json-web-key.html
## ##
#auth.jwt.jwks = https://127.0.0.1:8080/jwks #auth.jwt.jwks.endpoint = "https://127.0.0.1:8080/jwks"
## The JWKs refresh interval ## The JWKs refresh interval
## ##
@ -32,7 +32,7 @@ auth.jwt.from = password
## Enable to verify claims fields ## Enable to verify claims fields
## ##
## Value: on | off ## Value: on | off
auth.jwt.verify_claims = off auth.jwt.verify_claims.enable = off
## The checklist of claims to validate ## The checklist of claims to validate
## ##
@ -46,4 +46,4 @@ auth.jwt.verify_claims = off
## ##
## For example, to verify that the username in the JWT payload is the same ## For example, to verify that the username in the JWT payload is the same
## as the client (MQTT protocol) username ## as the client (MQTT protocol) username
#auth.jwt.verify_claims.username = %u #auth.jwt.verify_claims.username = "%u"

View File

@ -4,7 +4,7 @@
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "auth.jwt.jwks", "emqx_auth_jwt.jwks", [ {mapping, "auth.jwt.jwks.endpoint", "emqx_auth_jwt.jwks", [
{datatype, string} {datatype, string}
]}. ]}.
@ -26,7 +26,7 @@
{datatype, {enum, [raw, der]}} {datatype, {enum, [raw, der]}}
]}. ]}.
{mapping, "auth.jwt.verify_claims", "emqx_auth_jwt.verify_claims", [ {mapping, "auth.jwt.verify_claims.enable", "emqx_auth_jwt.verify_claims", [
{default, off}, {default, off},
{datatype, flag} {datatype, flag}
]}. ]}.
@ -36,7 +36,7 @@
]}. ]}.
{translation, "emqx_auth_jwt.verify_claims", fun(Conf) -> {translation, "emqx_auth_jwt.verify_claims", fun(Conf) ->
case cuttlefish:conf_get("auth.jwt.verify_claims", Conf) of case cuttlefish:conf_get("auth.jwt.verify_claims.enable", Conf) of
false -> cuttlefish:unset(); false -> cuttlefish:unset();
true -> true ->
lists:foldr( lists:foldr(

View File

@ -20,6 +20,6 @@
{profiles, {profiles,
[{test, [{test,
[{deps, [{emqx_ct_helpers, {git, "http://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}]} [{deps, []}
]} ]}
]}. ]}.

View File

@ -5,7 +5,7 @@
## LDAP server list, seperated by ','. ## LDAP server list, seperated by ','.
## ##
## Value: String ## Value: String
auth.ldap.servers = 127.0.0.1 auth.ldap.servers = "127.0.0.1"
## LDAP server port. ## LDAP server port.
## ##
@ -20,7 +20,7 @@ auth.ldap.pool = 8
## LDAP Bind DN. ## LDAP Bind DN.
## ##
## Value: DN ## Value: DN
auth.ldap.bind_dn = cn=root,dc=emqx,dc=io auth.ldap.bind_dn = "cn=root,dc=emqx,dc=io"
## LDAP Bind Password. ## LDAP Bind Password.
## ##
@ -37,7 +37,7 @@ auth.ldap.timeout = 30s
## Variables: ## Variables:
## ##
## Value: DN ## Value: DN
auth.ldap.device_dn = ou=device,dc=emqx,dc=io auth.ldap.device_dn = "ou=device,dc=emqx,dc=io"
## Specified ObjectClass ## Specified ObjectClass
## ##
@ -63,14 +63,14 @@ auth.ldap.password.attributetype = userPassword
## Whether to enable SSL. ## Whether to enable SSL.
## ##
## Value: true | false ## Value: true | false
auth.ldap.ssl = false auth.ldap.ssl.enable = false
#auth.ldap.ssl.certfile = etc/certs/cert.pem #auth.ldap.ssl.certfile = "etc/certs/cert.pem"
#auth.ldap.ssl.keyfile = etc/certs/key.pem #auth.ldap.ssl.keyfile = "etc/certs/key.pem"
#auth.ldap.ssl.cacertfile = etc/certs/cacert.pem #auth.ldap.ssl.cacertfile = "etc/certs/cacert.pem"
#auth.ldap.ssl.verify = verify_peer #auth.ldap.ssl.verify = "verify_peer"
#auth.ldap.ssl.server_name_indication = your_server_name #auth.ldap.ssl.server_name_indication = your_server_name

View File

@ -31,7 +31,7 @@
{datatype, {duration, ms}} {datatype, {duration, ms}}
]}. ]}.
{mapping, "auth.ldap.ssl", "emqx_auth_ldap.ldap", [ {mapping, "auth.ldap.ssl.enable", "emqx_auth_ldap.ldap", [
{default, false}, {default, false},
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
@ -83,7 +83,7 @@
{bind_password, BindPassword}, {bind_password, BindPassword},
{pool, Pool}, {pool, Pool},
{auto_reconnect, 2}], {auto_reconnect, 2}],
case cuttlefish:conf_get("auth.ldap.ssl", Conf) of case cuttlefish:conf_get("auth.ldap.ssl.enable", Conf) of
true -> [{ssl, true}, {sslopts, Filter(SslOpts())}|Opts]; true -> [{ssl, true}, {sslopts, Filter(SslOpts())}|Opts];
false -> [{ssl, false}|Opts] false -> [{ssl, false}|Opts]
end end

View File

@ -4,7 +4,7 @@
{profiles, {profiles,
[{test, [{test,
[{deps, [{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}]} [{deps, []}
]} ]}
]}. ]}.

View File

@ -1,6 +1,6 @@
{application, emqx_auth_ldap, {application, emqx_auth_ldap,
[{description, "EMQ X Authentication/ACL with LDAP"}, [{description, "EMQ X Authentication/ACL with LDAP"},
{vsn, "4.3.0"}, % strict semver, bump manually! {vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_auth_ldap_sup]}, {registered, [emqx_auth_ldap_sup]},
{applications, [kernel,stdlib,eldap2,ecpool]}, {applications, [kernel,stdlib,eldap2,ecpool]},

View File

@ -10,12 +10,12 @@ auth.mnesia.password_hash = sha256
## Examples ## Examples
##auth.client.1.clientid = id ##auth.client.1.clientid = id
##auth.client.1.password = passwd ##auth.client.1.password = passwd
##auth.client.2.clientid = dev:devid ##auth.client.2.clientid = "dev:devid"
##auth.client.2.password = passwd2 ##auth.client.2.password = passwd2
##auth.client.3.clientid = app:appid ##auth.client.3.clientid = "app:appid"
##auth.client.3.password = passwd3 ##auth.client.3.password = passwd3
##auth.client.4.clientid = client~!@#$%^&*()_+ ##auth.client.4.clientid = "client~!@#$%^&*()_+"
##auth.client.4.password = passwd~!@#$%^&*()_+ ##auth.client.4.password = "passwd~!@#$%^&*()_+"
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Username Authentication ## Username Authentication
@ -26,5 +26,5 @@ auth.mnesia.password_hash = sha256
##auth.user.1.password = public ##auth.user.1.password = public
##auth.user.2.username = feng@emqtt.io ##auth.user.2.username = feng@emqtt.io
##auth.user.2.password = public ##auth.user.2.password = public
##auth.user.3.username = name~!@#$%^&*()_+ ##auth.user.3.username = "name~!@#$%^&*()_+"
##auth.user.3.password = pwsswd~!@#$%^&*()_+ ##auth.user.3.password = "pwsswd~!@#$%^&*()_+"

View File

@ -16,8 +16,8 @@ auth.mongo.type = single
## ##
## Value: String ## Value: String
## ##
## Examples: 127.0.0.1:27017,127.0.0.2:27017... ## Examples: "127.0.0.1:27017,127.0.0.2:27017,..."
auth.mongo.server = 127.0.0.1:27017 auth.mongo.server = "127.0.0.1:27017"
## MongoDB pool size ## MongoDB pool size
## ##
@ -53,7 +53,7 @@ auth.mongo.database = mqtt
## Whether to enable SSL connection. ## Whether to enable SSL connection.
## ##
## Value: on | off ## Value: on | off
## auth.mongo.ssl = off ## auth.mongo.ssl.enable = off
## SSL keyfile. ## SSL keyfile.
## ##
@ -117,17 +117,17 @@ auth.mongo.topology.max_overflow = 0
auth.mongo.auth_query.password_hash = sha256 auth.mongo.auth_query.password_hash = sha256
## sha256 with salt suffix ## sha256 with salt suffix
## auth.mongo.auth_query.password_hash = sha256,salt ## auth.mongo.auth_query.password_hash = "sha256,salt"
## sha256 with salt prefix ## sha256 with salt prefix
## auth.mongo.auth_query.password_hash = salt,sha256 ## auth.mongo.auth_query.password_hash = "salt,sha256"
## bcrypt with salt prefix ## bcrypt with salt prefix
## auth.mongo.auth_query.password_hash = salt,bcrypt ## auth.mongo.auth_query.password_hash = "salt,bcrypt"
## pbkdf2 with macfun iterations dklen ## pbkdf2 with macfun iterations dklen
## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 ## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512
## auth.mongo.auth_query.password_hash = pbkdf2,sha256,1000,20 ## auth.mongo.auth_query.password_hash = "pbkdf2,sha256,1000,20"
## Authentication query. ## Authentication query.
auth.mongo.auth_query.collection = mqtt_user auth.mongo.auth_query.collection = mqtt_user
@ -146,15 +146,15 @@ auth.mongo.auth_query.password_field = password
## - %d: subject of client TLS cert ## - %d: subject of client TLS cert
## ##
## auth.mongo.auth_query.selector = {Field}={Placeholder} ## auth.mongo.auth_query.selector = {Field}={Placeholder}
auth.mongo.auth_query.selector = username=%u auth.mongo.auth_query.selector = "username=%u"
## ------------------------------------------------- ## -------------------------------------------------
## Super User Query ## Super User Query
## ------------------------------------------------- ## -------------------------------------------------
auth.mongo.super_query.collection = mqtt_user auth.mongo.super_query.collection = mqtt_user
auth.mongo.super_query.super_field = is_superuser auth.mongo.super_query.super_field = is_superuser
#auth.mongo.super_query.selector = username=%u, clientid=%c #auth.mongo.super_query.selector.1 = username=%u, clientid=%c
auth.mongo.super_query.selector = username=%u auth.mongo.super_query.selector = "username=%u"
## ACL Selector. ## ACL Selector.
## ##
@ -165,8 +165,8 @@ auth.mongo.super_query.selector = username=%u
## ##
## With following 2 selectors configured: ## With following 2 selectors configured:
## ##
## auth.mongo.acl_query.selector.1 = username=%u ## auth.mongo.acl_query.selector.1 = "username=%u"
## auth.mongo.acl_query.selector.2 = username=$all ## auth.mongo.acl_query.selector.2 = "username=$all"
## ##
## And if a client connected using username 'ilyas', ## And if a client connected using username 'ilyas',
## then the following mongo command will be used to ## then the following mongo command will be used to
@ -180,8 +180,8 @@ auth.mongo.super_query.selector = username=%u
## ##
## Examples: ## Examples:
## ##
## auth.mongo.acl_query.selector.1 = username=%u,clientid=%c ## auth.mongo.acl_query.selector.1 = "username=%u,clientid=%c"
## auth.mongo.acl_query.selector.2 = username=$all ## auth.mongo.acl_query.selector.2 = "username=$all"
## auth.mongo.acl_query.selector.3 = clientid=$all ## auth.mongo.acl_query.selector.3 = "clientid=$all"
auth.mongo.acl_query.collection = mqtt_acl auth.mongo.acl_query.collection = mqtt_acl
auth.mongo.acl_query.selector = username=%u auth.mongo.acl_query.selector = "username=%u"

View File

@ -45,7 +45,7 @@
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "auth.mongo.ssl", "emqx_auth_mongo.server", [ {mapping, "auth.mongo.ssl.enable", "emqx_auth_mongo.server", [
{default, off}, {default, off},
{datatype, {enum, [on, off, true, false]}} %% FIXME: ture/false is compatible with 4.0-4.2 version format, plan to delete in 5.0 {datatype, {enum, [on, off, true, false]}} %% FIXME: ture/false is compatible with 4.0-4.2 version format, plan to delete in 5.0
]}. ]}.
@ -130,8 +130,6 @@
true -> []; true -> [];
false -> [{r_mode, R}] false -> [{r_mode, R}]
end, end,
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
SslOpts = fun(Prefix) -> SslOpts = fun(Prefix) ->
Verify = case cuttlefish:conf_get(Prefix ++ ".verify", Conf, false) of Verify = case cuttlefish:conf_get(Prefix ++ ".verify", Conf, false) of
@ -149,8 +147,14 @@
end, end,
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
Ssl = case cuttlefish:conf_get("auth.mongo.ssl", Conf) of GenSsl = case cuttlefish:conf_get("auth.mongo.ssl.cacertfile", Conf, undefined) of
on -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl")}]; undefined -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl_opts")}];
_ -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl")}]
end,
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
Ssl = case cuttlefish:conf_get("auth.mongo.ssl.enable", Conf) of
on -> GenSsl;
off -> []; off -> [];
true -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl_opts")}]; true -> [{ssl, true}, {ssl_opts, SslOpts("auth.mongo.ssl_opts")}];
false -> [] false -> []

View File

@ -1,6 +1,6 @@
{application, emqx_auth_mongo, {application, emqx_auth_mongo,
[{description, "EMQ X Authentication/ACL with MongoDB"}, [{description, "EMQ X Authentication/ACL with MongoDB"},
{vsn, "4.3.0"}, % strict semver, bump manually! {vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_auth_mongo_sup]}, {registered, [emqx_auth_mongo_sup]},
{applications, [kernel,stdlib,mongodb,ecpool]}, {applications, [kernel,stdlib,mongodb,ecpool]},

View File

@ -7,7 +7,7 @@
## Value: Port | IP:Port ## Value: Port | IP:Port
## ##
## Examples: 3306, 127.0.0.1:3306, localhost:3306 ## Examples: 3306, 127.0.0.1:3306, localhost:3306
auth.mysql.server = 127.0.0.1:3306 auth.mysql.server = "127.0.0.1:3306"
## MySQL pool size. ## MySQL pool size.
## ##
@ -50,7 +50,7 @@ auth.mysql.database = mqtt
## - %C: common name of client TLS cert ## - %C: common name of client TLS cert
## - %d: subject of client TLS cert ## - %d: subject of client TLS cert
## ##
auth.mysql.auth_query = select password from mqtt_user where username = '%u' limit 1 auth.mysql.auth_query = "select password from mqtt_user where username = '%u' limit 1"
## auth.mysql.auth_query = select password_hash as password from mqtt_user where username = '%u' limit 1 ## auth.mysql.auth_query = select password_hash as password from mqtt_user where username = '%u' limit 1
## Password hash. ## Password hash.
@ -59,17 +59,17 @@ auth.mysql.auth_query = select password from mqtt_user where username = '%u' lim
auth.mysql.password_hash = sha256 auth.mysql.password_hash = sha256
## sha256 with salt prefix ## sha256 with salt prefix
## auth.mysql.password_hash = salt,sha256 ## auth.mysql.password_hash = "salt,sha256"
## bcrypt with salt only prefix ## bcrypt with salt only prefix
## auth.mysql.password_hash = salt,bcrypt ## auth.mysql.password_hash = "salt,bcrypt"
## sha256 with salt suffix ## sha256 with salt suffix
## auth.mysql.password_hash = sha256,salt ## auth.mysql.password_hash = "sha256,salt"
## pbkdf2 with macfun iterations dklen ## pbkdf2 with macfun iterations dklen
## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 ## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512
## auth.mysql.password_hash = pbkdf2,sha256,1000,20 ## auth.mysql.password_hash = "pbkdf2,sha256,1000,20"
## Superuser query. ## Superuser query.
## ##
@ -81,7 +81,7 @@ auth.mysql.password_hash = sha256
## - %C: common name of client TLS cert ## - %C: common name of client TLS cert
## - %d: subject of client TLS cert ## - %d: subject of client TLS cert
## ##
auth.mysql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1 auth.mysql.super_query = "select is_superuser from mqtt_user where username = '%u' limit 1"
## ACL query. ## ACL query.
## ##
@ -93,12 +93,12 @@ auth.mysql.super_query = select is_superuser from mqtt_user where username = '%u
## - %c: clientid ## - %c: clientid
## ##
## Note: You can add the 'ORDER BY' statement to control the rules match order ## Note: You can add the 'ORDER BY' statement to control the rules match order
auth.mysql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c' auth.mysql.acl_query = "select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'"
## Mysql ssl configuration. ## Mysql ssl configuration.
## ##
## Value: on | off ## Value: on | off
#auth.mysql.ssl = off ## auth.mysql.ssl.enable = off
## CA certificate. ## CA certificate.
## ##

View File

@ -30,7 +30,7 @@
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "auth.mysql.ssl", "emqx_auth_mysql.server", [ {mapping, "auth.mysql.ssl.enable", "emqx_auth_mysql.server", [
{default, off}, {default, off},
{datatype, flag} {datatype, flag}
]}. ]}.
@ -94,7 +94,7 @@
{keep_alive, true}], {keep_alive, true}],
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
Options1 = Options1 =
case cuttlefish:conf_get("auth.mysql.ssl", Conf) of case cuttlefish:conf_get("auth.mysql.ssl.enable", Conf) of
true -> true ->
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
CA = cuttlefish:conf_get( CA = cuttlefish:conf_get(

View File

@ -1,6 +1,6 @@
{application, emqx_auth_mysql, {application, emqx_auth_mysql,
[{description, "EMQ X Authentication/ACL with MySQL"}, [{description, "EMQ X Authentication/ACL with MySQL"},
{vsn, "4.3.0"}, % strict semver, bump manually! {vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_auth_mysql_sup]}, {registered, [emqx_auth_mysql_sup]},
{applications, [kernel,stdlib,mysql,ecpool]}, {applications, [kernel,stdlib,mysql,ecpool]},

View File

@ -49,7 +49,7 @@ auth.pgsql.encoding = utf8
## Whether to enable SSL connection. ## Whether to enable SSL connection.
## ##
## Value: true | false ## Value: true | false
auth.pgsql.ssl = false auth.pgsql.ssl.enable = false
## SSL keyfile. ## SSL keyfile.
## ##

View File

@ -6,8 +6,8 @@
## ##
## Value: Port | IP:Port ## Value: Port | IP:Port
## ##
## Examples: 5432, 127.0.0.1:5432, localhost:5432 ## Examples: 5432, "127.0.0.1:5432", "localhost:5432"
auth.pgsql.server = 127.0.0.1:5432 auth.pgsql.server = "127.0.0.1:5432"
## PostgreSQL pool size. ## PostgreSQL pool size.
## ##
@ -37,7 +37,7 @@ auth.pgsql.encoding = utf8
## Whether to enable SSL connection. ## Whether to enable SSL connection.
## ##
## Value: on | off ## Value: on | off
auth.pgsql.ssl = off auth.pgsql.ssl.enable = off
## TLS version. ## TLS version.
## ##
@ -87,7 +87,7 @@ auth.pgsql.ssl = off
## - %C: common name of client TLS cert ## - %C: common name of client TLS cert
## - %d: subject of client TLS cert ## - %d: subject of client TLS cert
## ##
auth.pgsql.auth_query = select password from mqtt_user where username = '%u' limit 1 auth.pgsql.auth_query = "select password from mqtt_user where username = '%u' limit 1"
## Password hash. ## Password hash.
## ##
@ -95,17 +95,17 @@ auth.pgsql.auth_query = select password from mqtt_user where username = '%u' lim
auth.pgsql.password_hash = sha256 auth.pgsql.password_hash = sha256
## sha256 with salt prefix ## sha256 with salt prefix
## auth.pgsql.password_hash = salt,sha256 ## auth.pgsql.password_hash = "salt,sha256"
## sha256 with salt suffix ## sha256 with salt suffix
## auth.pgsql.password_hash = sha256,salt ## auth.pgsql.password_hash = "sha256,salt"
## bcrypt with salt prefix ## bcrypt with salt prefix
## auth.pgsql.password_hash = salt,bcrypt ## auth.pgsql.password_hash = "salt,bcrypt"
## pbkdf2 with macfun iterations dklen ## pbkdf2 with macfun iterations dklen
## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 ## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512
## auth.pgsql.password_hash = pbkdf2,sha256,1000,20 ## auth.pgsql.password_hash = "pbkdf2,sha256,1000,20"
## Superuser query. ## Superuser query.
## ##
@ -117,7 +117,7 @@ auth.pgsql.password_hash = sha256
## - %C: common name of client TLS cert ## - %C: common name of client TLS cert
## - %d: subject of client TLS cert ## - %d: subject of client TLS cert
## ##
auth.pgsql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1 auth.pgsql.super_query = "select is_superuser from mqtt_user where username = '%u' limit 1"
## ACL query. Comment this query, the ACL will be disabled. ## ACL query. Comment this query, the ACL will be disabled.
## ##
@ -129,4 +129,4 @@ auth.pgsql.super_query = select is_superuser from mqtt_user where username = '%u
## - %c: clientid ## - %c: clientid
## ##
## Note: You can add the 'ORDER BY' statement to control the rules match order ## Note: You can add the 'ORDER BY' statement to control the rules match order
auth.pgsql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c' auth.pgsql.acl_query = "select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'"

View File

@ -30,7 +30,7 @@
{datatype, atom} {datatype, atom}
]}. ]}.
{mapping, "auth.pgsql.ssl", "emqx_auth_pgsql.server", [ {mapping, "auth.pgsql.ssl.enable", "emqx_auth_pgsql.server", [
{default, off}, {default, off},
{datatype, {enum, [on, off, true, false]}} %% FIXME: true/fasle is compatible with 4.0-4.2 version format, plan to delete in 5.0 {datatype, {enum, [on, off, true, false]}} %% FIXME: true/fasle is compatible with 4.0-4.2 version format, plan to delete in 5.0
]}. ]}.
@ -116,8 +116,14 @@
end, end,
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0 %% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
Ssl = case cuttlefish:conf_get("auth.pgsql.ssl", Conf) of GenSsl = case cuttlefish:conf_get("auth.pgsql.ssl.cacertfile", Conf, undefined) of
on -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl")}]; undefined -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl_opts")}];
_ -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl")}]
end,
%% FIXME: compatible with 4.0-4.2 version format, plan to delete in 5.0
Ssl = case cuttlefish:conf_get("auth.pgsql.ssl.enable", Conf) of
on -> GenSsl;
off -> []; off -> [];
true -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl_opts")}]; true -> [{ssl, true}, {ssl_opts, SslOpts("auth.pgsql.ssl_opts")}];
false -> [] false -> []

View File

@ -1,6 +1,6 @@
{application, emqx_auth_pgsql, {application, emqx_auth_pgsql,
[{description, "EMQ X Authentication/ACL with PostgreSQL"}, [{description, "EMQ X Authentication/ACL with PostgreSQL"},
{vsn, "4.3.0"}, % strict semver, bump manually! {vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_auth_pgsql_sup]}, {registered, [emqx_auth_pgsql_sup]},
{applications, [kernel,stdlib,epgsql,ecpool]}, {applications, [kernel,stdlib,epgsql,ecpool]},

View File

@ -12,9 +12,9 @@ auth.redis.type = single
## Value: Port | IP:Port ## Value: Port | IP:Port
## ##
## Single Redis Server: 127.0.0.1:6379, localhost:6379 ## Single Redis Server: 127.0.0.1:6379, localhost:6379
## Redis Sentinel: 127.0.0.1:26379,127.0.0.2:26379,127.0.0.3:26379 ## Redis Sentinel: "127.0.0.1:26379,127.0.0.2:26379,127.0.0.3:26379"
## Redis Cluster: 127.0.0.1:6379,127.0.0.2:6379,127.0.0.3:6379 ## Redis Cluster: "127.0.0.1:6379,127.0.0.2:6379,127.0.0.3:6379"
auth.redis.server = 127.0.0.1:6379 auth.redis.server = "127.0.0.1:6379"
## Redis sentinel cluster name. ## Redis sentinel cluster name.
## ##
@ -52,10 +52,10 @@ auth.redis.database = 0
## - %d: subject of client TLS cert ## - %d: subject of client TLS cert
## ##
## Examples: ## Examples:
## - HGET mqtt_user:%u password ## - "HGET mqtt_user:%u password"
## - HMGET mqtt_user:%u password ## - "HMGET mqtt_user:%u password"
## - HMGET mqtt_user:%u password salt ## - "HMGET mqtt_user:%u password salt"
auth.redis.auth_cmd = HMGET mqtt_user:%u password auth.redis.auth_cmd = "HMGET mqtt_user:%u password"
## Password hash. ## Password hash.
## ##
@ -63,17 +63,17 @@ auth.redis.auth_cmd = HMGET mqtt_user:%u password
auth.redis.password_hash = plain auth.redis.password_hash = plain
## sha256 with salt prefix ## sha256 with salt prefix
## auth.redis.password_hash = salt,sha256 ## auth.redis.password_hash = "salt,sha256"
## sha256 with salt suffix ## sha256 with salt suffix
## auth.redis.password_hash = sha256,salt ## auth.redis.password_hash = "sha256,salt"
## bcrypt with salt prefix ## bcrypt with salt prefix
## auth.redis.password_hash = salt,bcrypt ## auth.redis.password_hash = "salt,bcrypt"
## pbkdf2 with macfun iterations dklen ## pbkdf2 with macfun iterations dklen
## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512 ## macfun: md4, md5, ripemd160, sha, sha224, sha256, sha384, sha512
## auth.redis.password_hash = pbkdf2,sha256,1000,20 ## auth.redis.password_hash = "pbkdf2,sha256,1000,20"
## Superuser query command. ## Superuser query command.
## ##
@ -84,7 +84,7 @@ auth.redis.password_hash = plain
## - %c: clientid ## - %c: clientid
## - %C: common name of client TLS cert ## - %C: common name of client TLS cert
## - %d: subject of client TLS cert ## - %d: subject of client TLS cert
auth.redis.super_cmd = HGET mqtt_user:%u is_superuser auth.redis.super_cmd = "HGET mqtt_user:%u is_superuser"
## ACL query command. ## ACL query command.
## ##
@ -93,12 +93,12 @@ auth.redis.super_cmd = HGET mqtt_user:%u is_superuser
## Variables: ## Variables:
## - %u: username ## - %u: username
## - %c: clientid ## - %c: clientid
auth.redis.acl_cmd = HGETALL mqtt_acl:%u auth.redis.acl_cmd = "HGETALL mqtt_acl:%u"
## Redis ssl configuration. ## Redis ssl configuration.
## ##
## Value: on | off ## Value: on | off
#auth.redis.ssl = off # auth.redis.ssl.enable = off
## CA certificate. ## CA certificate.
## ##
@ -108,12 +108,12 @@ auth.redis.acl_cmd = HGETALL mqtt_acl:%u
## Client ssl certificate. ## Client ssl certificate.
## ##
## Value: File ## Value: File
#auth.redis.ssl.certfile = path/to/your/certfile # auth.redis.ssl.certfile = path/to/your/certfile
## Client ssl keyfile. ## Client ssl keyfile.
## ##
## Value: File ## Value: File
#auth.redis.ssl.keyfile = path/to/your/keyfile # auth.redis.ssl.keyfile = path/to/your/keyfile
## In mode verify_none the default behavior is to allow all x509-path ## In mode verify_none the default behavior is to allow all x509-path
## validation errors. ## validation errors.

View File

@ -33,7 +33,7 @@
hidden hidden
]}. ]}.
{mapping, "auth.redis.ssl", "emqx_auth_redis.options", [ {mapping, "auth.redis.ssl.enable", "emqx_auth_redis.options", [
{default, off}, {default, off},
{datatype, flag} {datatype, flag}
]}. ]}.
@ -75,7 +75,7 @@
]}. ]}.
{translation, "emqx_auth_redis.options", fun(Conf) -> {translation, "emqx_auth_redis.options", fun(Conf) ->
Ssl = cuttlefish:conf_get("auth.redis.ssl", Conf, false), Ssl = cuttlefish:conf_get("auth.redis.ssl.enable", Conf, false),
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
case Ssl of case Ssl of
true -> true ->

View File

@ -1,6 +1,6 @@
{application, emqx_auth_redis, {application, emqx_auth_redis,
[{description, "EMQ X Authentication/ACL with Redis"}, [{description, "EMQ X Authentication/ACL with Redis"},
{vsn, "4.3.0"}, % strict semver, bump manually! {vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_auth_redis_sup]}, {registered, [emqx_auth_redis_sup]},
{applications, [kernel,stdlib,eredis,eredis_cluster,ecpool]}, {applications, [kernel,stdlib,eredis,eredis_cluster,ecpool]},

View File

@ -53,13 +53,13 @@ The following is the basic configuration of RPC bridging. A simplest RPC bridgin
``` ```
## Bridge Address: Use node name (nodename@host) for rpc bridging, and host:port for mqtt connection ## Bridge Address: Use node name (nodename@host) for rpc bridging, and host:port for mqtt connection
bridge.mqtt.emqx2.address = emqx2@192.168.1.2 bridge.mqtt.emqx2.address = "emqx2@192.168.1.2"
## Forwarding topics of the message ## Forwarding topics of the message
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# bridge.mqtt.emqx2.forwards = "sensor1/#,sensor2/#"
## bridged mountpoint ## bridged mountpoint
bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/ bridge.mqtt.emqx2.mountpoint = "bridge/emqx2/${node}/"
``` ```
If the messages received by the local node emqx1 matches the topic `sersor1/#` or `sensor2/#`, these messages will be forwarded to the `sensor1/#` or `sensor2/#` topic of the remote node emqx2. If the messages received by the local node emqx1 matches the topic `sersor1/#` or `sensor2/#`, these messages will be forwarded to the `sensor1/#` or `sensor2/#` topic of the remote node emqx2.
@ -82,66 +82,66 @@ EMQ X MQTT bridging principle: Create an MQTT client on the EMQ X broker, and co
``` ```
## Bridge Address: Use node name for rpc bridging, use host:port for mqtt connection ## Bridge Address: Use node name for rpc bridging, use host:port for mqtt connection
bridge.mqtt.emqx2.address = 192.168.1.2:1883 bridge.mqtt.emqx2.address = "192.168.1.2:1883"
## Bridged Protocol Version ## Bridged Protocol Version
## Enumeration value: mqttv3 | mqttv4 | mqttv5 ## Enumeration value: mqttv3 | mqttv4 | mqttv5
bridge.mqtt.emqx2.proto_ver = mqttv4 bridge.mqtt.emqx2.proto_ver = "mqttv4"
## mqtt client's clientid ## mqtt client's clientid
bridge.mqtt.emqx2.clientid = bridge_emq bridge.mqtt.emqx2.clientid = "bridge_emq"
## mqtt client's clean_start field ## mqtt client's clean_start field
## Note: Some MQTT Brokers need to set the clean_start value as `true` ## Note: Some MQTT Brokers need to set the clean_start value as `true`
bridge.mqtt.emqx2.clean_start = true bridge.mqtt.emqx2.clean_start = true
## mqtt client's username field ## mqtt client's username field
bridge.mqtt.emqx2.username = user bridge.mqtt.emqx2.username = "user"
## mqtt client's password field ## mqtt client's password field
bridge.mqtt.emqx2.password = passwd bridge.mqtt.emqx2.password = "passwd"
## Whether the mqtt client uses ssl to connect to a remote serve or not ## Whether the mqtt client uses ssl to connect to a remote serve or not
bridge.mqtt.emqx2.ssl = off bridge.mqtt.emqx2.ssl = off
## CA Certificate of Client SSL Connection (PEM format) ## CA Certificate of Client SSL Connection (PEM format)
bridge.mqtt.emqx2.cacertfile = etc/certs/cacert.pem bridge.mqtt.emqx2.cacertfile = "etc/certs/cacert.pem"
## SSL certificate of Client SSL connection ## SSL certificate of Client SSL connection
bridge.mqtt.emqx2.certfile = etc/certs/client-cert.pem bridge.mqtt.emqx2.certfile = "etc/certs/client-cert.pem"
## Key file of Client SSL connection ## Key file of Client SSL connection
bridge.mqtt.emqx2.keyfile = etc/certs/client-key.pem bridge.mqtt.emqx2.keyfile = "etc/certs/client-key.pem"
## SSL encryption ## SSL encryption
bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384 bridge.mqtt.emqx2.ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384"
## TTLS PSK password ## TTLS PSK password
## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time ## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time
## ##
## See 'https://tools.ietf.org/html/rfc4279#section-2'. ## See 'https://tools.ietf.org/html/rfc4279#section-2'.
## bridge.mqtt.emqx2.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA ## bridge.mqtt.emqx2.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
## Client's heartbeat interval ## Client's heartbeat interval
bridge.mqtt.emqx2.keepalive = 60s bridge.mqtt.emqx2.keepalive = 60s
## Supported TLS version ## Supported TLS version
bridge.mqtt.emqx2.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 bridge.mqtt.emqx2.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## Forwarding topics of the message ## Forwarding topics of the message
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# bridge.mqtt.emqx2.forwards = "sensor1/#,sensor2/#"
## Bridged mountpoint ## Bridged mountpoint
bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/ bridge.mqtt.emqx2.mountpoint = "bridge/emqx2/${node}/"
## Subscription topic for bridging ## Subscription topic for bridging
bridge.mqtt.emqx2.subscription.1.topic = cmd/topic1 bridge.mqtt.emqx2.subscription.1.topic = "cmd/topic1"
## Subscription qos for bridging ## Subscription qos for bridging
bridge.mqtt.emqx2.subscription.1.qos = 1 bridge.mqtt.emqx2.subscription.1.qos = 1
## Subscription topic for bridging ## Subscription topic for bridging
bridge.mqtt.emqx2.subscription.2.topic = cmd/topic2 bridge.mqtt.emqx2.subscription.2.topic = "cmd/topic2"
## Subscription qos for bridging ## Subscription qos for bridging
bridge.mqtt.emqx2.subscription.2.qos = 1 bridge.mqtt.emqx2.subscription.2.qos = 1

View File

@ -39,7 +39,7 @@ In EMQ X, bridge is configured by modifying ``etc/emqx.conf``. EMQ X distinguish
.. code-block:: .. code-block::
## Bridge address: node name for local bridge, host:port for remote. ## Bridge address: node name for local bridge, host:port for remote.
bridge.mqtt.aws.address = 127.0.0.1:1883 bridge.mqtt.aws.address = "127.0.0.1:1883"
This configuration declares a bridge named ``aws`` and specifies that it is bridged to the MQTT broker of 127.0.0.1:1883 by MQTT mode. This configuration declares a bridge named ``aws`` and specifies that it is bridged to the MQTT broker of 127.0.0.1:1883 by MQTT mode.
@ -69,13 +69,13 @@ The following is the basic configuration of RPC bridging. A simplest RPC bridgin
.. code-block:: .. code-block::
## Bridge Address: Use node name (nodename@host) for rpc bridging, and host:port for mqtt connection ## Bridge Address: Use node name (nodename@host) for rpc bridging, and host:port for mqtt connection
bridge.mqtt.emqx2.address = emqx2@192.168.1.2 bridge.mqtt.emqx2.address = "emqx2@192.168.1.2"
## Forwarding topics of the message ## Forwarding topics of the message
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# bridge.mqtt.emqx2.forwards = "sensor1/#,sensor2/#"
## bridged mountpoint ## bridged mountpoint
bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/ bridge.mqtt.emqx2.mountpoint = "bridge/emqx2/${node}/"
If the messages received by the local node emqx1 matches the topic ``sersor1/#`` or ``sensor2/#``\ , these messages will be forwarded to the ``sensor1/#`` or ``sensor2/#`` topic of the remote node emqx2. If the messages received by the local node emqx1 matches the topic ``sersor1/#`` or ``sensor2/#``\ , these messages will be forwarded to the ``sensor1/#`` or ``sensor2/#`` topic of the remote node emqx2.
@ -86,10 +86,10 @@ If the messages received by the local node emqx1 matches the topic ``sersor1/#``
Limitations of RPC bridging: Limitations of RPC bridging:
#. #.
The RPC bridge of emqx can only forward local messages to the remote node, and cannot synchronize the messages of the remote node to the local node; The RPC bridge of emqx can only forward local messages to the remote node, and cannot synchronize the messages of the remote node to the local node;
#. #.
RPC bridge can only bridge two EMQ X broker together and cannot bridge EMQ X broker to other MQTT brokers. RPC bridge can only bridge two EMQ X broker together and cannot bridge EMQ X broker to other MQTT brokers.
EMQ X MQTT Bridge Configuration EMQ X MQTT Bridge Configuration
@ -102,66 +102,66 @@ EMQ X MQTT bridging principle: Create an MQTT client on the EMQ X broker, and co
.. code-block:: .. code-block::
## Bridge Address: Use node name for rpc bridging, use host:port for mqtt connection ## Bridge Address: Use node name for rpc bridging, use host:port for mqtt connection
bridge.mqtt.emqx2.address = 192.168.1.2:1883 bridge.mqtt.emqx2.address = "192.168.1.2:1883"
## Bridged Protocol Version ## Bridged Protocol Version
## Enumeration value: mqttv3 | mqttv4 | mqttv5 ## Enumeration value: mqttv3 | mqttv4 | mqttv5
bridge.mqtt.emqx2.proto_ver = mqttv4 bridge.mqtt.emqx2.proto_ver = "mqttv4"
## mqtt client's clientid ## mqtt client's clientid
bridge.mqtt.emqx2.clientid = bridge_emq bridge.mqtt.emqx2.clientid = "bridge_emq"
## mqtt client's clean_start field ## mqtt client's clean_start field
## Note: Some MQTT Brokers need to set the clean_start value as `true` ## Note: Some MQTT Brokers need to set the clean_start value as `true`
bridge.mqtt.emqx2.clean_start = true bridge.mqtt.emqx2.clean_start = true
## mqtt client's username field ## mqtt client's username field
bridge.mqtt.emqx2.username = user bridge.mqtt.emqx2.username = "user"
## mqtt client's password field ## mqtt client's password field
bridge.mqtt.emqx2.password = passwd bridge.mqtt.emqx2.password = "passwd"
## Whether the mqtt client uses ssl to connect to a remote serve or not ## Whether the mqtt client uses ssl to connect to a remote serve or not
bridge.mqtt.emqx2.ssl = off bridge.mqtt.emqx2.ssl = off
## CA Certificate of Client SSL Connection (PEM format) ## CA Certificate of Client SSL Connection (PEM format)
bridge.mqtt.emqx2.cacertfile = etc/certs/cacert.pem bridge.mqtt.emqx2.cacertfile = "etc/certs/cacert.pem"
## SSL certificate of Client SSL connection ## SSL certificate of Client SSL connection
bridge.mqtt.emqx2.certfile = etc/certs/client-cert.pem bridge.mqtt.emqx2.certfile = "etc/certs/client-cert.pem"
## Key file of Client SSL connection ## Key file of Client SSL connection
bridge.mqtt.emqx2.keyfile = etc/certs/client-key.pem bridge.mqtt.emqx2.keyfile = "etc/certs/client-key.pem"
## TTLS PSK password ## TTLS PSK password
## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time ## Note 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot be configured at the same time
## ##
## See 'https://tools.ietf.org/html/rfc4279#section-2'. ## See 'https://tools.ietf.org/html/rfc4279#section-2'.
## bridge.mqtt.emqx2.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA ## bridge.mqtt.emqx2.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
## Client's heartbeat interval ## Client's heartbeat interval
bridge.mqtt.emqx2.keepalive = 60s bridge.mqtt.emqx2.keepalive = 60s
## Supported TLS version ## Supported TLS version
bridge.mqtt.emqx2.tls_versions = tlsv1.2 bridge.mqtt.emqx2.tls_versions = "tlsv1.2"
## SSL encryption ## SSL encryption
bridge.mqtt.emqx2.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384 bridge.mqtt.emqx2.ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384"
## Forwarding topics of the message ## Forwarding topics of the message
bridge.mqtt.emqx2.forwards = sensor1/#,sensor2/# bridge.mqtt.emqx2.forwards = "sensor1/#,sensor2/#"
## Bridged mountpoint ## Bridged mountpoint
bridge.mqtt.emqx2.mountpoint = bridge/emqx2/${node}/ bridge.mqtt.emqx2.mountpoint = "bridge/emqx2/${node}/"
## Subscription topic for bridging ## Subscription topic for bridging
bridge.mqtt.emqx2.subscription.1.topic = cmd/topic1 bridge.mqtt.emqx2.subscription.1.topic = "cmd/topic1"
## Subscription qos for bridging ## Subscription qos for bridging
bridge.mqtt.emqx2.subscription.1.qos = 1 bridge.mqtt.emqx2.subscription.1.qos = 1
## Subscription topic for bridging ## Subscription topic for bridging
bridge.mqtt.emqx2.subscription.2.topic = cmd/topic2 bridge.mqtt.emqx2.subscription.2.topic = "cmd/topic2"
## Subscription qos for bridging ## Subscription qos for bridging
bridge.mqtt.emqx2.subscription.2.qos = 1 bridge.mqtt.emqx2.subscription.2.qos = 1
@ -190,7 +190,7 @@ The bridge of EMQ X has a message caching mechanism. The caching mechanism is ap
bridge.mqtt.emqx2.queue.batch_bytes_limit = 1000MB bridge.mqtt.emqx2.queue.batch_bytes_limit = 1000MB
## The path for placing replayq queue. If it is not specified, then replayq will run in `mem-only` mode and messages will not be cached on disk. ## The path for placing replayq queue. If it is not specified, then replayq will run in `mem-only` mode and messages will not be cached on disk.
bridge.mqtt.emqx2.queue.replayq_dir = data/emqx_emqx2_bridge/ bridge.mqtt.emqx2.queue.replayq_dir = "data/emqx_emqx2_bridge/"
## Replayq data segment size ## Replayq data segment size
bridge.mqtt.emqx2.queue.replayq_seg_bytes = 10MB bridge.mqtt.emqx2.queue.replayq_seg_bytes = 10MB

View File

@ -9,8 +9,8 @@
## Bridge address: node name for local bridge, host:port for remote. ## Bridge address: node name for local bridge, host:port for remote.
## ##
## Value: String ## Value: String
## Example: emqx@127.0.0.1, 127.0.0.1:1883 ## Example: emqx@127.0.0.1, "127.0.0.1:1883"
bridge.mqtt.aws.address = 127.0.0.1:1883 bridge.mqtt.aws.address = "127.0.0.1:1883"
## Protocol version of the bridge. ## Protocol version of the bridge.
## ##
@ -65,18 +65,18 @@ bridge.mqtt.aws.password = passwd
## Topics that need to be forward to AWS IoTHUB ## Topics that need to be forward to AWS IoTHUB
## ##
## Value: String ## Value: String
## Example: topic1/#,topic2/# ## Example: "topic1/#,topic2/#"
bridge.mqtt.aws.forwards = topic1/#,topic2/# bridge.mqtt.aws.forwards = "topic1/#,topic2/#"
## Forward messages to the mountpoint of an AWS IoTHUB ## Forward messages to the mountpoint of an AWS IoTHUB
## ##
## Value: String ## Value: String
bridge.mqtt.aws.forward_mountpoint = bridge/aws/${node}/ bridge.mqtt.aws.forward_mountpoint = "bridge/aws/${node}/"
## Need to subscribe to AWS topics ## Need to subscribe to AWS topics
## ##
## Value: String ## Value: String
## bridge.mqtt.aws.subscription.1.topic = cmd/topic1 ## bridge.mqtt.aws.subscription.1.topic = "cmd/topic1"
## Need to subscribe to AWS topics QoS. ## Need to subscribe to AWS topics QoS.
## ##
@ -86,7 +86,7 @@ bridge.mqtt.aws.forward_mountpoint = bridge/aws/${node}/
## A mountpoint that receives messages from AWS IoTHUB ## A mountpoint that receives messages from AWS IoTHUB
## ##
## Value: String ## Value: String
## bridge.mqtt.aws.receive_mountpoint = receive/aws/ ## bridge.mqtt.aws.receive_mountpoint = "receive/aws/"
## Bribge to remote server via SSL. ## Bribge to remote server via SSL.
@ -97,28 +97,28 @@ bridge.mqtt.aws.ssl = off
## PEM-encoded CA certificates of the bridge. ## PEM-encoded CA certificates of the bridge.
## ##
## Value: File ## Value: File
bridge.mqtt.aws.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem bridge.mqtt.aws.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
## Client SSL Certfile of the bridge. ## Client SSL Certfile of the bridge.
## ##
## Value: File ## Value: File
bridge.mqtt.aws.certfile = {{ platform_etc_dir }}/certs/client-cert.pem bridge.mqtt.aws.certfile = "{{ platform_etc_dir }}/certs/client-cert.pem"
## Client SSL Keyfile of the bridge. ## Client SSL Keyfile of the bridge.
## ##
## Value: File ## Value: File
bridge.mqtt.aws.keyfile = {{ platform_etc_dir }}/certs/client-key.pem bridge.mqtt.aws.keyfile = "{{ platform_etc_dir }}/certs/client-key.pem"
## SSL Ciphers used by the bridge. ## SSL Ciphers used by the bridge.
## ##
## Value: String ## Value: String
bridge.mqtt.aws.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA bridge.mqtt.aws.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## Ciphers for TLS PSK. ## Ciphers for TLS PSK.
## Note that 'bridge.${BridgeName}.ciphers' and 'bridge.${BridgeName}.psk_ciphers' cannot ## Note that 'bridge.${BridgeName}.ciphers' and 'bridge.${BridgeName}.psk_ciphers' cannot
## be configured at the same time. ## be configured at the same time.
## See 'https://tools.ietf.org/html/rfc4279#section-2'. ## See 'https://tools.ietf.org/html/rfc4279#section-2'.
#bridge.mqtt.aws.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA #bridge.mqtt.aws.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
## Ping interval of a down bridge. ## Ping interval of a down bridge.
## ##
@ -130,7 +130,7 @@ bridge.mqtt.aws.keepalive = 60s
## ##
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier ## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
## Value: String ## Value: String
bridge.mqtt.aws.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 bridge.mqtt.aws.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## Bridge reconnect time. ## Bridge reconnect time.
## ##
@ -160,7 +160,7 @@ bridge.mqtt.aws.max_inflight_size = 32
## replayq works in a mem-only manner. ## replayq works in a mem-only manner.
## ##
## Value: String ## Value: String
bridge.mqtt.aws.queue.replayq_dir = {{ platform_data_dir }}/replayq/emqx_aws_bridge/ bridge.mqtt.aws.queue.replayq_dir = "{{ platform_data_dir }}/replayq/emqx_aws_bridge/"
## Replayq segment size ## Replayq segment size
## ##

View File

@ -4,13 +4,13 @@
## The IP and UDP port that CoAP bind with. ## The IP and UDP port that CoAP bind with.
## ##
## Default: 0.0.0.0:5683 ## Default: "0.0.0.0:5683"
## ##
## Examples: ## Examples:
## coap.bind.udp.x = 0.0.0.0:5683 | :::5683 | 127.0.0.1:5683 | ::1:5683 ## coap.bind.udp.x = "0.0.0.0:5683" | ":::5683" | "127.0.0.1:5683" | "::1:5683"
## ##
coap.bind.udp.1 = 0.0.0.0:5683 coap.bind.udp.1 = "0.0.0.0:5683"
##coap.bind.udp.2 = 0.0.0.0:6683 ##coap.bind.udp.2 = "0.0.0.0:6683"
## Whether to enable statistics for CoAP clients. ## Whether to enable statistics for CoAP clients.
## ##
@ -23,13 +23,13 @@ coap.enable_stats = off
## The DTLS port that CoAP is listening on. ## The DTLS port that CoAP is listening on.
## ##
## Default: 0.0.0.0:5684 ## Default: "0.0.0.0:5684"
## ##
## Examples: ## Examples:
## coap.bind.dtls.x = 0.0.0.0:5684 | :::5684 | 127.0.0.1:5684 | ::1:5684 ## coap.bind.dtls.x = "0.0.0.0:5684" | ":::5684" | "127.0.0.1:5684" | "::1:5684"
## ##
coap.bind.dtls.1 = 0.0.0.0:5684 coap.bind.dtls.1 = "0.0.0.0:5684"
##coap.bind.dtls.2 = 0.0.0.0:6684 ##coap.bind.dtls.2 = "0.0.0.0:6684"
## A server only does x509-path validation in mode verify_peer, ## A server only does x509-path validation in mode verify_peer,
## as it then sends a certificate request to the client (this ## as it then sends a certificate request to the client (this
@ -43,17 +43,17 @@ coap.bind.dtls.1 = 0.0.0.0:5684
## Private key file for DTLS ## Private key file for DTLS
## ##
## Value: File ## Value: File
coap.dtls.keyfile = {{ platform_etc_dir }}/certs/key.pem coap.dtls.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
## Server certificate for DTLS. ## Server certificate for DTLS.
## ##
## Value: File ## Value: File
coap.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem coap.dtls.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
## PEM-encoded CA certificates for DTLS ## PEM-encoded CA certificates for DTLS
## ##
## Value: File ## Value: File
## coap.dtls.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem ## coap.dtls.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
## Used together with {verify, verify_peer} by an SSL server. If set to true, ## Used together with {verify, verify_peer} by an SSL server. If set to true,
## the server fails if the client does not have a certificate to send, that is, ## the server fails if the client does not have a certificate to send, that is,
@ -79,4 +79,4 @@ coap.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem
## Most of it was copied from Mozillas Server Side TLS article ## Most of it was copied from Mozillas Server Side TLS article
## ##
## Value: Ciphers ## Value: Ciphers
coap.dtls.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA coap.dtls.ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"

View File

@ -8,8 +8,8 @@
## The gRPC server url ## The gRPC server url
## ##
## exhook.server.$name.url = url() ## exhook.server.$name.url = url()
exhook.server.default.url = http://127.0.0.1:9000 exhook.server.default.url = "http://127.0.0.1:9000"
#exhook.server.default.ssl.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem #exhook.server.default.ssl.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
#exhook.server.default.ssl.certfile = {{ platform_etc_dir }}/certs/cert.pem #exhook.server.default.ssl.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
#exhook.server.default.ssl.keyfile = {{ platform_etc_dir }}/certs/key.pem #exhook.server.default.ssl.keyfile = "{{ platform_etc_dir }}/certs/key.pem"

View File

@ -43,7 +43,6 @@
{profiles, {profiles,
[{test, [{test,
[{deps, [{deps,
[{emqx_ct_helper, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "v1.3.1"}}} []}
]}
]} ]}
]}. ]}.

View File

@ -5,9 +5,9 @@
exproto.server.http.port = 9100 exproto.server.http.port = 9100
exproto.server.https.port = 9101 exproto.server.https.port = 9101
exproto.server.https.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem exproto.server.https.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
exproto.server.https.certfile = {{ platform_etc_dir }}/certs/cert.pem exproto.server.https.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
exproto.server.https.keyfile = {{ platform_etc_dir }}/certs/key.pem exproto.server.https.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Listeners ## Listeners
@ -20,12 +20,12 @@ exproto.server.https.keyfile = {{ platform_etc_dir }}/certs/key.pem
## ##
## Value: <tcp|ssl|udp|dtls>://<ip>:<port> ## Value: <tcp|ssl|udp|dtls>://<ip>:<port>
## ##
## Examples: tcp://0.0.0.0:7993 | ssl://127.0.0.1:7994 ## Examples: "tcp://0.0.0.0:7993" | "ssl://127.0.0.1:7994"
exproto.listener.protoname = tcp://0.0.0.0:7993 exproto.listener.protoname.endpoint = "tcp://0.0.0.0:7993"
## The ConnectionHandler server address ## The ConnectionHandler server address
## ##
exproto.listener.protoname.connection_handler_url = http://127.0.0.1:9001 exproto.listener.protoname.connection_handler_url = "http://127.0.0.1:9001"
#exproto.listener.protoname.connection_handler_certfile = #exproto.listener.protoname.connection_handler_certfile =
#exproto.listener.protoname.connection_handler_cacertfile = #exproto.listener.protoname.connection_handler_cacertfile =
@ -62,8 +62,8 @@ exproto.listener.protoname.idle_timeout = 30s
## ##
## Value: ACL Rule ## Value: ACL Rule
## ##
## Example: allow 192.168.0.0/24 ## Example: "allow 192.168.0.0/24"
exproto.listener.protoname.access.1 = allow all exproto.listener.protoname.access.1 = "allow all"
## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed ## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed
## behind HAProxy or Nginx. ## behind HAProxy or Nginx.
@ -146,27 +146,27 @@ exproto.listener.protoname.reuseaddr = true
## See: http://erlang.org/doc/man/ssl.html ## See: http://erlang.org/doc/man/ssl.html
## ##
## Value: String, seperated by ',' ## Value: String, seperated by ','
#exproto.listener.protoname.tls_versions = tlsv1.2,tlsv1.1,tlsv1 #exproto.listener.protoname.tls_versions = "tlsv1.2,tlsv1.1,tlsv1"
## Path to the file containing the user's private PEM-encoded key. ## Path to the file containing the user's private PEM-encoded key.
## ##
## See: http://erlang.org/doc/man/ssl.html ## See: http://erlang.org/doc/man/ssl.html
## ##
## Value: File ## Value: File
#exproto.listener.protoname.keyfile = {{ platform_etc_dir }}/certs/key.pem #exproto.listener.protoname.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
## Path to a file containing the user certificate. ## Path to a file containing the user certificate.
## ##
## See: http://erlang.org/doc/man/ssl.html ## See: http://erlang.org/doc/man/ssl.html
## ##
## Value: File ## Value: File
#exproto.listener.protoname.certfile = {{ platform_etc_dir }}/certs/cert.pem #exproto.listener.protoname.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
## Path to the file containing PEM-encoded CA certificates. The CA certificates ## Path to the file containing PEM-encoded CA certificates. The CA certificates
## are used during server authentication and when building the client certificate chain. ## are used during server authentication and when building the client certificate chain.
## ##
## Value: File ## Value: File
#exproto.listener.protoname.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem #exproto.listener.protoname.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
## The Ephemeral Diffie-Helman key exchange is a very effective way of ## The Ephemeral Diffie-Helman key exchange is a very effective way of
## ensuring Forward Secrecy by exchanging a set of keys that never hit ## ensuring Forward Secrecy by exchanging a set of keys that never hit
@ -183,7 +183,7 @@ exproto.listener.protoname.reuseaddr = true
## openssl dhparam -out dh-params.pem 2048 ## openssl dhparam -out dh-params.pem 2048
## ##
## Value: File ## Value: File
#exproto.listener.protoname.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem #exproto.listener.protoname.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
## A server only does x509-path validation in mode verify_peer, ## A server only does x509-path validation in mode verify_peer,
## as it then sends a certificate request to the client (this ## as it then sends a certificate request to the client (this
@ -218,13 +218,13 @@ exproto.listener.protoname.reuseaddr = true
## Most of it was copied from Mozillas Server Side TLS article ## Most of it was copied from Mozillas Server Side TLS article
## ##
## Value: Ciphers ## Value: Ciphers
#exproto.listener.protoname.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA #exproto.listener.protoname.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## Ciphers for TLS PSK. ## Ciphers for TLS PSK.
## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot ## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot
## be configured at the same time. ## be configured at the same time.
## See 'https://tools.ietf.org/html/rfc4279#section-2'. ## See 'https://tools.ietf.org/html/rfc4279#section-2'.
#exproto.listener.protoname.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA #exproto.listener.protoname.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
## SSL parameter renegotiation is a feature that allows a client and a server ## SSL parameter renegotiation is a feature that allows a client and a server
## to renegotiate the parameters of the SSL connection on the fly. ## to renegotiate the parameters of the SSL connection on the fly.

View File

@ -44,7 +44,7 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Listeners %% Listeners
{mapping, "exproto.listener.$proto", "emqx_exproto.listeners", [ {mapping, "exproto.listener.$proto.endpoint", "emqx_exproto.listeners", [
{datatype, string} {datatype, string}
]}. ]}.
@ -340,7 +340,7 @@ end}.
Listeners = fun(Proto) -> Listeners = fun(Proto) ->
Prefix = string:join(["exproto","listener", Proto], "."), Prefix = string:join(["exproto","listener", Proto], "."),
Opts = HandlerOpts(Prefix) ++ ConnOpts(Prefix) ++ LisOpts(Prefix), Opts = HandlerOpts(Prefix) ++ ConnOpts(Prefix) ++ LisOpts(Prefix),
case cuttlefish:conf_get(Prefix, Conf, undefined) of case cuttlefish:conf_get(Prefix ++ ".endpoint", Conf, undefined) of
undefined -> []; undefined -> [];
ListenOn0 -> ListenOn0 ->
case ParseListenOn(ListenOn0) of case ParseListenOn(ListenOn0) of
@ -359,6 +359,6 @@ end}.
end end
end end
end, end,
lists:flatten([Listeners(Proto) || {[_, "listener", Proto], ListenOn} lists:flatten([Listeners(Proto) || {[_, "listener", Proto, "endpoint"], ListenOn}
<- cuttlefish_variable:filter_by_prefix("exproto.listener", Conf)]) <- cuttlefish_variable:filter_by_prefix("exproto.listener", Conf)])
end}. end}.

View File

@ -46,7 +46,6 @@
{profiles, {profiles,
[{test, [{test,
[{deps, [{deps,
[{emqx_ct_helper, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "v1.3.0"}}} []}
]}
]} ]}
]}. ]}.

View File

@ -1,6 +1,6 @@
{application, emqx_exproto, {application, emqx_exproto,
[{description, "EMQ X Extension for Protocol"}, [{description, "EMQ X Extension for Protocol"},
{vsn, "4.3.0"}, %% strict semver {vsn, "4.4.0"}, %% strict semver
{modules, []}, {modules, []},
{registered, []}, {registered, []},
{mod, {emqx_exproto_app, []}}, {mod, {emqx_exproto_app, []}},

View File

@ -25,25 +25,25 @@ lwm2m.lifetime_max = 86400s
# Placeholders supported: # Placeholders supported:
# '%e': Endpoint Name # '%e': Endpoint Name
# '%a': IP Address # '%a': IP Address
lwm2m.mountpoint = lwm2m/%e/ lwm2m.mountpoint = "lwm2m/%e/"
# The topic subscribed by the lwm2m client after it is connected # The topic subscribed by the lwm2m client after it is connected
# Placeholders supported: # Placeholders supported:
# '%e': Endpoint Name # '%e': Endpoint Name
# '%a': IP Address # '%a': IP Address
lwm2m.topics.command = dn/# lwm2m.topics.command = "dn/#"
# The topic to which the lwm2m client's response is published # The topic to which the lwm2m client's response is published
lwm2m.topics.response = up/resp lwm2m.topics.response = "up/resp"
# The topic to which the lwm2m client's notify message is published # The topic to which the lwm2m client's notify message is published
lwm2m.topics.notify = up/notify lwm2m.topics.notify = "up/notify"
# The topic to which the lwm2m client's register message is published # The topic to which the lwm2m client's register message is published
lwm2m.topics.register = up/resp lwm2m.topics.register = "up/resp"
# The topic to which the lwm2m client's update message is published # The topic to which the lwm2m client's update message is published
lwm2m.topics.update = up/resp lwm2m.topics.update = "up/resp"
# When publish the update message. # When publish the update message.
# #
@ -55,18 +55,18 @@ lwm2m.topics.update = up/resp
#lwm2m.update_msg_publish_condition = contains_object_list #lwm2m.update_msg_publish_condition = contains_object_list
# Dir where the object definition files can be found # Dir where the object definition files can be found
lwm2m.xml_dir = {{ platform_etc_dir }}/lwm2m_xml lwm2m.xml_dir = "{{ platform_etc_dir }}/lwm2m_xml"
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## UDP Listener options ## UDP Listener options
## The IP and port of the LwM2M Gateway ## The IP and port of the LwM2M Gateway
## ##
## Default: 0.0.0.0:5683 ## Default: "0.0.0.0:5683"
## Examples: ## Examples:
## lwm2m.bind.udp.x = 0.0.0.0:5683 | :::5683 | 127.0.0.1:5683 | ::1:5683 ## lwm2m.bind.udp.x = "0.0.0.0:5683" | ":::5683" | "127.0.0.1:5683" | "::1:5683"
lwm2m.bind.udp.1 = 0.0.0.0:5683 lwm2m.bind.udp.1 = "0.0.0.0:5683"
#lwm2m.bind.udp.2 = 0.0.0.0:6683 #lwm2m.bind.udp.2 = "0.0.0.0:6683"
## Socket options, used for performance tuning ## Socket options, used for performance tuning
## ##
@ -83,13 +83,13 @@ lwm2m.opts.read_packets = 20
## The DTLS port that LwM2M is listening on. ## The DTLS port that LwM2M is listening on.
## ##
## Default: 0.0.0.0:5684 ## Default: "0.0.0.0:5684"
## ##
## Examples: ## Examples:
## lwm2m.bind.dtls.x = 0.0.0.0:5684 | :::5684 | 127.0.0.1:5684 | ::1:5684 ## lwm2m.bind.dtls.x = "0.0.0.0:5684" | ":::5684" | "127.0.0.1:5684" | "::1:5684"
## ##
lwm2m.bind.dtls.1 = 0.0.0.0:5684 lwm2m.bind.dtls.1 = "0.0.0.0:5684"
#lwm2m.bind.dtls.2 = 0.0.0.0:6684 #lwm2m.bind.dtls.2 = "0.0.0.0:6684"
## A server only does x509-path validation in mode verify_peer, ## A server only does x509-path validation in mode verify_peer,
## as it then sends a certificate request to the client (this ## as it then sends a certificate request to the client (this
@ -103,17 +103,17 @@ lwm2m.bind.dtls.1 = 0.0.0.0:5684
## Private key file for DTLS ## Private key file for DTLS
## ##
## Value: File ## Value: File
lwm2m.dtls.keyfile = {{ platform_etc_dir }}/certs/key.pem lwm2m.dtls.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
## Server certificate for DTLS. ## Server certificate for DTLS.
## ##
## Value: File ## Value: File
lwm2m.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem lwm2m.dtls.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
## PEM-encoded CA certificates for DTLS ## PEM-encoded CA certificates for DTLS
## ##
## Value: File ## Value: File
#lwm2m.dtls.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem #lwm2m.dtls.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
## Used together with {verify, verify_peer} by an SSL server. If set to true, ## Used together with {verify, verify_peer} by an SSL server. If set to true,
## the server fails if the client does not have a certificate to send, that is, ## the server fails if the client does not have a certificate to send, that is,
@ -139,11 +139,11 @@ lwm2m.dtls.certfile = {{ platform_etc_dir }}/certs/cert.pem
## Most of it was copied from Mozillas Server Side TLS article ## Most of it was copied from Mozillas Server Side TLS article
## ##
## Value: Ciphers ## Value: Ciphers
lwm2m.dtls.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA lwm2m.dtls.ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## Ciphers for TLS PSK. ## Ciphers for TLS PSK.
## ##
## Note that 'lwm2m.dtls.ciphers' and 'lwm2m.dtls.psk_ciphers' cannot ## Note that 'lwm2m.dtls.ciphers' and 'lwm2m.dtls.psk_ciphers' cannot
## be configured at the same time. ## be configured at the same time.
## See 'https://tools.ietf.org/html/rfc4279#section-2'. ## See 'https://tools.ietf.org/html/rfc4279#section-2'.
#lwm2m.dtls.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA #lwm2m.dtls.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"

View File

@ -5,7 +5,6 @@
{profiles, {profiles,
[{test, [{test,
[{deps, [{er_coap_client, {git, "https://github.com/emqx/er_coap_client", {tag, "v1.0"}}}, [{deps, [{er_coap_client, {git, "https://github.com/emqx/er_coap_client", {tag, "v1.0"}}},
{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}},
{emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}} {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.0"}}}
]} ]}
]} ]}

View File

@ -23,7 +23,7 @@ management.default_application.secret = public
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## HTTP Listener ## HTTP Listener
management.listener.http = 8081 management.listener.http.port = 8081
management.listener.http.acceptors = 2 management.listener.http.acceptors = 2
management.listener.http.max_clients = 512 management.listener.http.max_clients = 512
management.listener.http.backlog = 512 management.listener.http.backlog = 512
@ -35,19 +35,19 @@ management.listener.http.ipv6_v6only = false
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## HTTPS Listener ## HTTPS Listener
## management.listener.https = 8081 ## management.listener.https.port = 8081
## management.listener.https.acceptors = 2 ## management.listener.https.acceptors = 2
## management.listener.https.max_clients = 512 ## management.listener.https.max_clients = 512
## management.listener.https.backlog = 512 ## management.listener.https.backlog = 512
## management.listener.https.send_timeout = 15s ## management.listener.https.send_timeout = 15s
## management.listener.https.send_timeout_close = on ## management.listener.https.send_timeout_close = on
## management.listener.https.certfile = etc/certs/cert.pem ## management.listener.https.certfile = "etc/certs/cert.pem"
## management.listener.https.keyfile = etc/certs/key.pem ## management.listener.https.keyfile = "etc/certs/key.pem"
## management.listener.https.cacertfile = etc/certs/cacert.pem ## management.listener.https.cacertfile = "etc/certs/cacert.pem"
## management.listener.https.verify = verify_peer ## management.listener.https.verify = verify_peer
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier ## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
## management.listener.https.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 ## management.listener.https.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## management.listener.https.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA ## management.listener.https.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## management.listener.https.fail_if_no_peer_cert = true ## management.listener.https.fail_if_no_peer_cert = true
## management.listener.https.inet6 = false ## management.listener.https.inet6 = false
## management.listener.https.ipv6_v6only = false ## management.listener.https.ipv6_v6only = false

View File

@ -21,7 +21,7 @@
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "management.listener.http", "emqx_management.listeners", [ {mapping, "management.listener.http.port", "emqx_management.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
@ -85,7 +85,7 @@
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{mapping, "management.listener.https", "emqx_management.listeners", [ {mapping, "management.listener.https.port", "emqx_management.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
@ -225,7 +225,7 @@ end}.
lists:foldl( lists:foldl(
fun(Proto, Acc) -> fun(Proto, Acc) ->
Prefix = "management.listener." ++ atom_to_list(Proto), Prefix = "management.listener." ++ atom_to_list(Proto),
case cuttlefish:conf_get(Prefix, Conf, undefined) of case cuttlefish:conf_get(Prefix ++ ".port", Conf, undefined) of
undefined -> Acc; undefined -> Acc;
Port -> Port ->
[{Proto, Port, TcpOpts(Prefix) ++ Opts(Prefix) [{Proto, Port, TcpOpts(Prefix) ++ Opts(Prefix)

View File

@ -1,6 +1,6 @@
{application, emqx_management, {application, emqx_management,
[{description, "EMQ X Management API and CLI"}, [{description, "EMQ X Management API and CLI"},
{vsn, "4.3.3"}, % strict semver, bump manually! {vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_management_sup]}, {registered, [emqx_management_sup]},
{applications, [kernel,stdlib,minirest]}, {applications, [kernel,stdlib,minirest]},

View File

@ -181,4 +181,4 @@ remove_resources() ->
lists:foreach(fun(#resource{id = Id}) -> lists:foreach(fun(#resource{id = Id}) ->
emqx_rule_engine:delete_resource(Id) emqx_rule_engine:delete_resource(Id)
end, emqx_rule_registry:get_resources()), end, emqx_rule_registry:get_resources()),
timer:sleep(500). timer:sleep(500).

View File

@ -5,7 +5,7 @@
## The Prometheus Push Gateway URL address ## The Prometheus Push Gateway URL address
## ##
## Note: You can comment out this line to disable it ## Note: You can comment out this line to disable it
prometheus.push.gateway.server = http://127.0.0.1:9091 prometheus.push.gateway.server = "http://127.0.0.1:9091"
## The metrics data push interval (millisecond) ## The metrics data push interval (millisecond)
## ##

View File

@ -1,2 +1,2 @@
psk.file.path = {{ platform_etc_dir }}/psk.txt psk.file.path = "{{ platform_etc_dir }}/psk.txt"
psk.file.delimiter = : psk.file.delimiter = ":"

View File

@ -1,6 +1,6 @@
{application, emqx_recon, {application, emqx_recon,
[{description, "EMQ X Recon Plugin"}, [{description, "EMQ X Recon Plugin"},
{vsn, "4.3.0"}, % strict semver, bump manually! {vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, []}, {registered, []},
{applications, [kernel,stdlib,recon]}, {applications, [kernel,stdlib,recon]},

View File

@ -0,0 +1,43 @@
REBAR := rebar3
.PHONY: all
all: es
.PHONY: compile
compile:
$(REBAR) compile
.PHONY: clean
clean: distclean
.PHONY: distclean
distclean:
@rm -rf _build erl_crash.dump rebar3.crashdump
.PHONY: xref
xref:
$(REBAR) xref
.PHONY: eunit
eunit: compile
$(REBAR) eunit -v -c
$(REBAR) cover
.PHONY: ct
ct: compile
$(REBAR) as test ct -v
cover:
$(REBAR) cover
.PHONY: dialyzer
dialyzer:
$(REBAR) dialyzer
.PHONY: es
es: compile
$(REBAR) escriptize
.PHONY: elvis
elvis:
./scripts/elvis-check.sh

View File

@ -0,0 +1,53 @@
# emqx_resource
The `emqx_resource` is an application that manages configuration specs and runtime states
for components that need to be configured and manipulated from the emqx-dashboard.
It is intended to be used by resources, actions, acl, auth, backend_logics and more.
It reads the configuration spec from *.spec (in HOCON format) and provide APIs for
creating, updating and destroying resource instances among all nodes in the cluster.
It handles the problem like storing the configs and runtime states for both resource
and resource instances, and how porting them between different emqx_resource versions.
It may maintain the config and data in JSON or HOCON files in data/ dir.
After restarting the emqx_resource, it re-creates all the resource instances.
There can be foreign references between resource instances via resource-id.
So they may find each other via this Id.
## Try it out
$ ./demo.sh
Eshell V11.1.8 (abort with ^G)
1> == the demo log tracer <<"log_tracer_clientid_shawn">> started.
config: #{<<"config">> =>
#{<<"bulk">> => <<"10KB">>,<<"cache_log_dir">> => <<"/tmp">>,
<<"condition">> => #{<<"clientid">> => <<"abc">>},
<<"level">> => <<"debug">>},
<<"id">> => <<"log_tracer_clientid_shawn">>,
<<"resource_type">> => <<"log_tracer">>}
1> emqx_resource_instance:health_check(<<"log_tracer_clientid_shawn">>).
== the demo log tracer <<"log_tracer_clientid_shawn">> is working well
state: #{health_checked => 1,logger_handler_id => abc}
ok
2> emqx_resource_instance:health_check(<<"log_tracer_clientid_shawn">>).
== the demo log tracer <<"log_tracer_clientid_shawn">> is working well
state: #{health_checked => 2,logger_handler_id => abc}
ok
3> emqx_resource_instance:query(<<"log_tracer_clientid_shawn">>, get_log).
== the demo log tracer <<"log_tracer_clientid_shawn">> received request: get_log
state: #{health_checked => 2,logger_handler_id => abc}
"this is a demo log messages..."
4> emqx_resource_instance:remove(<<"log_tracer_clientid_shawn">>).
== the demo log tracer <<"log_tracer_clientid_shawn">> stopped.
state: #{health_checked => 0,logger_handler_id => abc}
ok
5> emqx_resource_instance:query(<<"log_tracer_clientid_shawn">>, get_log).
** exception error: {get_instance,{<<"log_tracer_clientid_shawn">>,not_found}}

6
apps/emqx_resource/demo.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -e
rebar3 compile
erl -sname abc -pa _build/default/lib/*/ebin _build/default/lib/emqx_resource/examples -s demo

View File

@ -0,0 +1,14 @@
[{elvis, [{config, [
#{dirs => ["src"],
filter => "*.erl",
%ignore => [],
ruleset => erl_files,
rules => [{elvis_style, operator_spaces, #{
rules => [{right, ","},
{right, "|"},
{left, "|"},
{right, "||"},
{left, "||"}]}},
{elvis_style, god_modules, #{limit => 100}}]}
]}]}].

View File

@ -0,0 +1,3 @@
##--------------------------------------------------------------------
## EMQ X Resource Plugin
##--------------------------------------------------------------------

View File

@ -0,0 +1,13 @@
-module(demo).
-export([start/0]).
start() ->
code:load_file(log_tracer),
code:load_file(log_tracer_schema),
{ok, _} = application:ensure_all_started(minirest),
{ok, _} = application:ensure_all_started(emqx_resource),
emqx_resource:load_instances("./_build/default/lib/emqx_resource/examples"),
Handlers = [{"/", minirest:handler(#{modules => [log_tracer]})}],
Dispatch = [{"/[...]", minirest, Handlers}],
minirest:start_http(?MODULE, #{socket_opts => [inet, {port, 9900}]}, Dispatch).

View File

@ -0,0 +1,147 @@
---
theme: gaia
color: #000
colorSecondary: #333
backgroundColor: #fff
backgroundImage: url('https://marp.app/assets/hero-background.jpg')
paginate: true
marp: true
---
<!-- _class: lead -->
# EMQX Resource
---
## What is it for
The [emqx_resource](https://github.com/terry-xiaoyu/emqx_resource) for managing configurations and runtime states for dashboard components .
![bg right](https://docs.emqx.cn/assets/img/rule_action_1@2x.73766093.png)
---
<!-- _class: lead -->
# The Demo
The little log tracer
---
- The hocon schema file (log_tracer_schema.erl):
https://github.com/terry-xiaoyu/emqx_resource/blob/main/examples/log_tracer_schema.erl
- The callback file (log_tracer.erl):
https://github.com/terry-xiaoyu/emqx_resource/blob/main/examples/log_tracer.erl
---
Start the demo log tracer
```
./demo.sh
```
Load instance from config files (auto loaded)
```
## This will load all of the "*.conf" file under that directory:
emqx_resource:load_instances("./_build/default/lib/emqx_resource/examples").
```
The config file is validated against the schema (`*_schema.erl`) before loaded.
---
# List Types and Instances
- To list all the available resource types:
```
emqx_resource:list_types().
emqx_resource:list_instances().
```
- And there's `*_verbose` versions for these `list_*` APIs:
```
emqx_resource:list_types_verbose().
emqx_resource:list_instances_verbose().
```
---
# Instance management
- To get a resource types and instances:
```
emqx_resource:get_type(log_tracer).
emqx_resource:get_instance("log_tracer_clientid_shawn").
```
- To create a resource instances:
```
emqx_resource:create("log_tracer2", log_tracer,
#{bulk => <<"1KB">>,cache_log_dir => <<"/tmp">>,
cache_logs_in => <<"memory">>,chars_limit => 1024,
condition => #{<<"app">> => <<"emqx">>},
enable_cache => true,level => debug}).
```
---
- To update a resource:
```
emqx_resource:update("log_tracer2", log_tracer, #{bulk => <<"100KB">>}, []).
```
- To delete a resource:
```
emqx_resource:remove("log_tracer2").
```
---
<!-- _class: lead -->
# HTTP APIs Demo
---
# Get a log tracer
To list current log tracers:
```
curl -s -XGET 'http://localhost:9900/log_tracer' | jq .
```
---
## Update or Create
To update an existing log tracer or create a new one:
```
INST='{
"resource_type": "log_tracer",
"config": {
"condition": {
"app": "emqx"
},
"level": "debug",
"cache_log_dir": "/tmp",
"bulk": "10KB",
"chars_limit": 1024
}
}'
curl -sv -XPUT 'http://localhost:9900/log_tracer/log_tracer2' -d $INST | jq .
```

View File

@ -0,0 +1,11 @@
{
"id": "log_tracer_clientid_shawn"
"resource_type": "log_tracer"
"config": {
"condition": {"app": "emqx"}
"level": "debug"
"cache_log_dir": "/tmp"
"bulk": "10KB"
"chars_limit": 1024
}
}

View File

@ -0,0 +1,45 @@
-module(log_tracer).
-include_lib("emqx_resource/include/emqx_resource_behaviour.hrl").
-emqx_resource_api_path("/log_tracer").
%% callbacks of behaviour emqx_resource
-export([ on_start/2
, on_stop/2
, on_query/4
, on_health_check/2
, on_api_reply_format/1
, on_config_merge/3
]).
%% callbacks for emqx_resource config schema
-export([fields/1]).
fields(ConfPath) ->
log_tracer_schema:fields(ConfPath).
on_start(InstId, Config) ->
io:format("== the demo log tracer ~p started.~nconfig: ~p~n", [InstId, Config]),
{ok, #{logger_handler_id => abc, health_checked => 0}}.
on_stop(InstId, State) ->
io:format("== the demo log tracer ~p stopped.~nstate: ~p~n", [InstId, State]),
ok.
on_query(InstId, Request, AfterQuery, State) ->
io:format("== the demo log tracer ~p received request: ~p~nstate: ~p~n",
[InstId, Request, State]),
emqx_resource:query_success(AfterQuery),
"this is a demo log messages...".
on_health_check(InstId, State = #{health_checked := Checked}) ->
NState = State#{health_checked => Checked + 1},
io:format("== the demo log tracer ~p is working well~nstate: ~p~n", [InstId, NState]),
{ok, NState}.
on_api_reply_format(#{id := Id, status := Status, state := #{health_checked := NChecked}}) ->
#{id => Id, status => Status, checked_count => NChecked}.
on_config_merge(OldConfig, NewConfig, _Params) ->
maps:merge(OldConfig, NewConfig).

View File

@ -0,0 +1,45 @@
-module(log_tracer_schema).
-include_lib("typerefl/include/types.hrl").
-export([fields/1]).
-reflect_type([t_level/0, t_cache_logs_in/0]).
-type t_level() :: debug | info | notice | warning | error | critical | alert | emergency.
-type t_cache_logs_in() :: memory | file.
fields("config") ->
[ {condition, fun condition/1}
, {level, fun level/1}
, {enable_cache, fun enable_cache/1}
, {cache_logs_in, fun cache_logs_in/1}
, {cache_log_dir, fun cache_log_dir/1}
, {bulk, fun bulk/1}
];
fields(_) -> [].
condition(mapping) -> "config.condition";
condition(type) -> map();
condition(_) -> undefined.
level(mapping) -> "config.level";
level(type) -> t_level();
level(_) -> undefined.
enable_cache(mapping) -> "config.enable_cache";
enable_cache(type) -> boolean();
enable_cache(_) -> undefined.
cache_logs_in(mapping) -> "config.cache_logs_in";
cache_logs_in(type) -> t_cache_logs_in();
cache_logs_in(_) -> undefined.
cache_log_dir(mapping) -> "config.cache_log_dir";
cache_log_dir(type) -> typerefl:regexp_string("^(.*)$");
cache_log_dir(_) -> undefined.
bulk(mapping) -> "config.bulk";
bulk(type) -> typerefl:regexp_string("^[. 0-9]+(B|KB|MB|GB)$");
bulk(_) -> undefined.

View File

@ -0,0 +1,34 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-type resource_type() :: module().
-type instance_id() :: binary().
-type resource_config() :: term().
-type resource_spec() :: map().
-type resource_state() :: term().
-type resource_data() :: #{
id => instance_id(),
mod => module(),
config => resource_config(),
state => resource_state(),
status => started | stopped
}.
-type after_query() :: {OnSuccess :: after_query_fun(), OnFailed :: after_query_fun()} |
undefined.
%% the `after_query_fun()` is mainly for callbacks that increment counters or do some fallback
%% actions upon query failure
-type after_query_fun() :: {fun((...) -> ok), Args :: [term()]}.

View File

@ -0,0 +1,18 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-include_lib("emqx_resource/include/emqx_resource.hrl").
-behaviour(emqx_resource).
-compile({parse_transform, emqx_resource_transform}).

View File

@ -0,0 +1,53 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-define(CLUSTER_CALL(Func, Args), ?CLUSTER_CALL(Func, Args, ok)).
-define(CLUSTER_CALL(Func, Args, ResParttern),
%% ekka_mnesia:running_nodes()
fun() ->
case LocalResult = erlang:apply(?MODULE, Func, Args) of
ResParttern ->
case rpc:multicall(nodes(), ?MODULE, Func, Args, 5000) of
{ResL, []} ->
Filter = fun
(ResParttern) -> false;
({badrpc, {'EXIT', {undef, [{?MODULE, Func0, _, []}]}}})
when Func0 =:= Func -> false;
(_) -> true
end,
case lists:filter(Filter, ResL) of
[] -> LocalResult;
ErrL -> {error, ErrL}
end;
{ResL, BadNodes} ->
{error, {failed_on_nodes, BadNodes, ResL}}
end;
ErrorResult ->
{error, ErrorResult}
end
end()).
-define(SAFE_CALL(_EXP_),
?SAFE_CALL(_EXP_, _ = do_nothing)).
-define(SAFE_CALL(_EXP_, _EXP_ON_FAIL_),
fun() ->
try (_EXP_)
catch _EXCLASS_:_EXCPTION_:_ST_ ->
_EXP_ON_FAIL_,
{error, {_EXCLASS_, _EXCPTION_, _ST_}}
end
end()).

View File

@ -0,0 +1,2 @@
%%-*- mode: erlang -*-
%% emqx-resource config mapping

View File

@ -0,0 +1,17 @@
{erl_opts, [ debug_info
, nowarn_unused_import
%, {d, 'RESOURCE_DEBUG'}
]}.
{erl_first_files, ["src/emqx_resource_transform.erl"]}.
{extra_src_dirs, ["examples"]}.
%% try to override the dialyzer 'race_conditions' defined in the top-level dir,
%% but it doesn't work
{dialyzer, [{warnings, [unmatched_returns, error_handling]}
]}.
{deps, [ {jsx, {git, "https://github.com/talentdeficit/jsx", {tag, "v3.1.0"}}}
]}.

View File

@ -0,0 +1,17 @@
#!/bin/bash
set -euo pipefail
ELVIS_VERSION='1.0.0-emqx-2'
elvis_version="${2:-$ELVIS_VERSION}"
echo "elvis -v: $elvis_version"
if [ ! -f ./elvis ] || [ "$(./elvis -v | grep -oE '[1-9]+\.[0-9]+\.[0-9]+\-emqx-[0-9]+')" != "$elvis_version" ]; then
curl -fLO "https://github.com/emqx/elvis/releases/download/$elvis_version/elvis"
chmod +x ./elvis
fi
./elvis rock --config elvis.config

View File

@ -0,0 +1,18 @@
{application, emqx_resource,
[{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, {emqx_resource_app, []}},
{applications,
[kernel,
stdlib,
gproc,
hocon,
jsx
]},
{env,[]},
{modules, []},
{licenses, ["Apache 2.0"]},
{links, []}
]}.

View File

@ -0,0 +1,310 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-module(emqx_resource).
-include("emqx_resource.hrl").
-include("emqx_resource_utils.hrl").
%% APIs for resource types
-export([ get_type/1
, list_types/0
, list_types_verbose/0
]).
-export([ discover_resource_mods/0
, is_resource_mod/1
, call_instance/2
]).
-export([ query_success/1
, query_failed/1
]).
%% APIs for instances
-export([ parse_config/2
, resource_type_from_str/1
]).
%% Sync resource instances and files
%% provisional solution: rpc:multical to all the nodes for creating/updating/removing
%% todo: replicate operations
-export([ create/3 %% store the config and start the instance
, create_dry_run/3 %% run start/2, health_check/2 and stop/1 sequentially
, update/4 %% update the config, stop the old instance and start the new one
%% it will create a new resource when the id does not exist
, remove/1 %% remove the config and stop the instance
]).
%% Calls to the callback module with current resource state
%% They also save the state after the call finished (except query/2,3).
-export([ restart/1 %% restart the instance.
, health_check/1 %% verify if the resource is working normally
, stop/1 %% stop the instance
, query/2 %% query the instance
, query/3 %% query the instance with after_query()
]).
%% Direct calls to the callback module
-export([ call_start/3 %% start the instance
, call_health_check/3 %% verify if the resource is working normally
, call_stop/3 %% stop the instance
, call_config_merge/4 %% merge the config when updating
, call_jsonify/2
, call_api_reply_format/2
]).
-export([ list_instances/0 %% list all the instances, id only.
, list_instances_verbose/0 %% list all the instances
, get_instance/1 %% return the data of the instance
, get_instance_by_type/1 %% return all the instances of the same resource type
, load_instances_from_dir/1 %% load instances from a directory
, load_instance_from_file/1 %% load an instance from a config file
, load_instance_from_config/1 %% load an instance from a map or json-string config
% , dependents/1
% , inc_counter/2 %% increment the counter of the instance
% , inc_counter/3 %% increment the counter by a given integer
]).
-define(EXT, "*.spec").
-optional_callbacks([ on_query/4
, on_health_check/2
, on_config_merge/3
, on_jsonify/1
, on_api_reply_format/1
]).
-callback on_api_reply_format(resource_data()) -> jsx:json_term().
-callback on_config_merge(resource_config(), resource_config(), term()) -> resource_config().
-callback on_jsonify(resource_config()) -> jsx:json_term().
%% when calling emqx_resource:start/1
-callback on_start(instance_id(), resource_config()) ->
{ok, resource_state()} | {error, Reason :: term()}.
%% when calling emqx_resource:stop/1
-callback on_stop(instance_id(), resource_state()) -> term().
%% when calling emqx_resource:query/3
-callback on_query(instance_id(), Request :: term(), after_query(), resource_state()) -> term().
%% when calling emqx_resource:health_check/2
-callback on_health_check(instance_id(), resource_state()) ->
{ok, resource_state()} | {error, Reason:: term(), resource_state()}.
%% load specs and return the loaded resources this time.
-spec list_types_verbose() -> [resource_spec()].
list_types_verbose() ->
[get_spec(Mod) || Mod <- list_types()].
-spec list_types() -> [module()].
list_types() ->
discover_resource_mods().
-spec get_type(module()) -> {ok, resource_spec()} | {error, not_found}.
get_type(Mod) ->
case is_resource_mod(Mod) of
true -> {ok, get_spec(Mod)};
false -> {error, not_found}
end.
-spec get_spec(module()) -> resource_spec().
get_spec(Mod) ->
maps:put(<<"resource_type">>, Mod, Mod:emqx_resource_schema()).
-spec discover_resource_mods() -> [module()].
discover_resource_mods() ->
[Mod || {Mod, _} <- code:all_loaded(), is_resource_mod(Mod)].
-spec is_resource_mod(module()) -> boolean().
is_resource_mod(Mod) ->
erlang:function_exported(Mod, emqx_resource_schema, 0).
-spec query_success(after_query()) -> ok.
query_success(undefined) -> ok;
query_success({{OnSucc, Args}, _}) ->
safe_apply(OnSucc, Args).
-spec query_failed(after_query()) -> ok.
query_failed(undefined) -> ok;
query_failed({_, {OnFailed, Args}}) ->
safe_apply(OnFailed, Args).
%% =================================================================================
%% APIs for resource instances
%% =================================================================================
-spec create(instance_id(), resource_type(), resource_config()) ->
{ok, resource_data()} | {error, Reason :: term()}.
create(InstId, ResourceType, Config) ->
?CLUSTER_CALL(call_instance, [InstId, {create, InstId, ResourceType, Config}], {ok, _}).
-spec create_dry_run(instance_id(), resource_type(), resource_config()) ->
ok | {error, Reason :: term()}.
create_dry_run(InstId, ResourceType, Config) ->
?CLUSTER_CALL(call_instance, [InstId, {create_dry_run, InstId, ResourceType, Config}]).
-spec update(instance_id(), resource_type(), resource_config(), term()) ->
{ok, resource_data()} | {error, Reason :: term()}.
update(InstId, ResourceType, Config, Params) ->
?CLUSTER_CALL(call_instance, [InstId, {update, InstId, ResourceType, Config, Params}], {ok, _}).
-spec remove(instance_id()) -> ok | {error, Reason :: term()}.
remove(InstId) ->
?CLUSTER_CALL(call_instance, [InstId, {remove, InstId}]).
-spec query(instance_id(), Request :: term()) -> Result :: term().
query(InstId, Request) ->
query(InstId, Request, undefined).
%% same to above, also defines what to do when the Module:on_query success or failed
%% it is the duty of the Moudle to apply the `after_query()` functions.
-spec query(instance_id(), Request :: term(), after_query()) -> Result :: term().
query(InstId, Request, AfterQuery) ->
case get_instance(InstId) of
{ok, #{mod := Mod, state := ResourceState}} ->
%% the resource state is readonly to Moudle:on_query/4
%% and the `after_query()` functions should be thread safe
Mod:on_query(InstId, Request, AfterQuery, ResourceState);
{error, Reason} ->
error({get_instance, {InstId, Reason}})
end.
-spec restart(instance_id()) -> ok | {error, Reason :: term()}.
restart(InstId) ->
call_instance(InstId, {restart, InstId}).
-spec stop(instance_id()) -> ok | {error, Reason :: term()}.
stop(InstId) ->
call_instance(InstId, {stop, InstId}).
-spec health_check(instance_id()) -> ok | {error, Reason :: term()}.
health_check(InstId) ->
call_instance(InstId, {health_check, InstId}).
-spec get_instance(instance_id()) -> {ok, resource_data()} | {error, Reason :: term()}.
get_instance(InstId) ->
emqx_resource_instance:lookup(InstId).
-spec list_instances() -> [instance_id()].
list_instances() ->
[Id || #{id := Id} <- list_instances_verbose()].
-spec list_instances_verbose() -> [resource_data()].
list_instances_verbose() ->
emqx_resource_instance:list_all().
-spec get_instance_by_type(module()) -> [resource_data()].
get_instance_by_type(ResourceType) ->
emqx_resource_instance:lookup_by_type(ResourceType).
-spec load_instances_from_dir(Dir :: string()) -> ok.
load_instances_from_dir(Dir) ->
emqx_resource_instance:load_dir(Dir).
-spec load_instance_from_file(File :: string()) -> ok.
load_instance_from_file(File) ->
emqx_resource_instance:load_file(File).
-spec load_instance_from_config(binary() | map()) -> {ok, resource_data()} | {error, term()}.
load_instance_from_config(Config) ->
emqx_resource_instance:load_config(Config).
-spec call_start(instance_id(), module(), resource_config()) ->
{ok, resource_state()} | {error, Reason :: term()}.
call_start(InstId, Mod, Config) ->
?SAFE_CALL(Mod:on_start(InstId, Config)).
-spec call_health_check(instance_id(), module(), resource_state()) ->
{ok, resource_state()} | {error, Reason:: term(), resource_state()}.
call_health_check(InstId, Mod, ResourceState) ->
?SAFE_CALL(Mod:on_health_check(InstId, ResourceState)).
-spec call_stop(instance_id(), module(), resource_state()) -> term().
call_stop(InstId, Mod, ResourceState) ->
?SAFE_CALL(Mod:on_stop(InstId, ResourceState)).
-spec call_config_merge(module(), resource_config(), resource_config(), term()) ->
resource_config().
call_config_merge(Mod, OldConfig, NewConfig, Params) ->
case erlang:function_exported(Mod, on_jsonify, 1) of
true ->
?SAFE_CALL(Mod:on_config_merge(OldConfig, NewConfig, Params));
false when is_map(OldConfig), is_map(NewConfig) ->
maps:merge(OldConfig, NewConfig);
false ->
NewConfig
end.
-spec call_jsonify(module(), resource_config()) -> jsx:json_term().
call_jsonify(Mod, Config) ->
case erlang:function_exported(Mod, on_jsonify, 1) of
false -> Config;
true -> ?SAFE_CALL(Mod:on_jsonify(Config))
end.
-spec call_api_reply_format(module(), resource_data()) -> jsx:json_term().
call_api_reply_format(Mod, Data) ->
case erlang:function_exported(Mod, on_api_reply_format, 1) of
false -> emqx_resource_api:default_api_reply_format(Data);
true -> ?SAFE_CALL(Mod:on_api_reply_format(Data))
end.
-spec parse_config(resource_type(), binary() | term()) ->
{ok, resource_config()} | {error, term()}.
parse_config(ResourceType, RawConfig) when is_binary(RawConfig) ->
case hocon:binary(RawConfig, #{format => richmap}) of
{ok, MapConfig} ->
do_parse_config(ResourceType, MapConfig);
Error -> Error
end;
parse_config(ResourceType, RawConfigTerm) ->
parse_config(ResourceType, jsx:encode(#{config => RawConfigTerm})).
-spec do_parse_config(resource_type(), map()) -> {ok, resource_config()} | {error, term()}.
do_parse_config(ResourceType, MapConfig) ->
case ?SAFE_CALL(hocon_schema:generate(ResourceType, MapConfig)) of
{error, Reason} -> {error, Reason};
Config ->
InstConf = maps:from_list(proplists:get_value(config, Config)),
{ok, InstConf}
end.
%% =================================================================================
-spec resource_type_from_str(string()) -> {ok, resource_type()} | {error, term()}.
resource_type_from_str(ResourceType) ->
try Mod = list_to_existing_atom(str(ResourceType)),
case emqx_resource:is_resource_mod(Mod) of
true -> {ok, Mod};
false -> {error, {invalid_resource, Mod}}
end
catch error:badarg ->
{error, {resource_not_found, ResourceType}}
end.
call_instance(InstId, Query) ->
emqx_resource_instance:hash_call(InstId, Query).
safe_apply(Func, Args) ->
?SAFE_CALL(erlang:apply(Func, Args)).
str(S) when is_binary(S) -> binary_to_list(S);
str(S) when is_list(S) -> S.

View File

@ -0,0 +1,78 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-module(emqx_resource_api).
-export([ get_all/3
, get/3
, put/3
, delete/3
]).
-export([default_api_reply_format/1]).
get_all(Mod, _Binding, _Params) ->
{200, #{code => 0, data =>
[format_data(Mod, Data) || Data <- emqx_resource:list_instances_verbose()]}}.
get(Mod, #{id := Id}, _Params) ->
case emqx_resource:get_instance(stringnify(Id)) of
{ok, Data} ->
{200, #{code => 0, data => format_data(Mod, Data)}};
{error, not_found} ->
{404, #{code => 102, message => {resource_instance_not_found, stringnify(Id)}}}
end.
put(Mod, #{id := Id}, Params) ->
ConfigParams = proplists:get_value(<<"config">>, Params),
ResourceTypeStr = proplists:get_value(<<"resource_type">>, Params, #{}),
case emqx_resource:resource_type_from_str(ResourceTypeStr) of
{ok, ResourceType} ->
do_put(Mod, stringnify(Id), ConfigParams, ResourceType, Params);
{error, Reason} ->
{404, #{code => 102, message => stringnify(Reason)}}
end.
do_put(Mod, Id, ConfigParams, ResourceType, Params) ->
case emqx_resource:parse_config(ResourceType, ConfigParams) of
{ok, Config} ->
case emqx_resource:update(Id, ResourceType, Config, Params) of
{ok, Data} ->
{200, #{code => 0, data => format_data(Mod, Data)}};
{error, Reason} ->
{500, #{code => 102, message => stringnify(Reason)}}
end;
{error, Reason} ->
{400, #{code => 108, message => stringnify(Reason)}}
end.
delete(_Mod, #{id := Id}, _Params) ->
case emqx_resource:remove(stringnify(Id)) of
ok -> {200, #{code => 0, data => #{}}};
{error, Reason} ->
{500, #{code => 102, message => stringnify(Reason)}}
end.
format_data(Mod, Data) ->
emqx_resource:call_api_reply_format(Mod, Data).
default_api_reply_format(#{id := Id, mod := Mod, status := Status, config := Config}) ->
#{node => node(), id => Id, status => Status, resource_type => Mod,
config => emqx_resource:call_jsonify(Mod, Config)}.
stringnify(Bin) when is_binary(Bin) -> Bin;
stringnify(Str) when is_list(Str) -> list_to_binary(Str);
stringnify(Reason) ->
iolist_to_binary(io_lib:format("~p", [Reason])).

View File

@ -0,0 +1,33 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-module(emqx_resource_app).
-behaviour(application).
-include("emqx_resource.hrl").
-emqx_plugin(?MODULE).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
emqx_resource_sup:start_link().
stop(_State) ->
ok.
%% internal functions

View File

@ -0,0 +1,315 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-module(emqx_resource_instance).
-behaviour(gen_server).
-include("emqx_resource.hrl").
-include("emqx_resource_utils.hrl").
-export([start_link/2]).
%% load resource instances from *.conf files
-export([ load_dir/1
, load_file/1
, load_config/1
, lookup/1
, list_all/0
, lookup_by_type/1
, create_local/3
]).
-export([ hash_call/2
, hash_call/3
]).
%% gen_server Callbacks
-export([ init/1
, handle_call/3
, handle_cast/2
, handle_info/2
, terminate/2
, code_change/3
]).
-record(state, {worker_pool, worker_id}).
-type state() :: #state{}.
%%------------------------------------------------------------------------------
%% Start the registry
%%------------------------------------------------------------------------------
start_link(Pool, Id) ->
gen_server:start_link({local, proc_name(?MODULE, Id)},
?MODULE, {Pool, Id}, []).
%% call the worker by the hash of resource-instance-id, to make sure we always handle
%% operations on the same instance in the same worker.
hash_call(InstId, Request) ->
hash_call(InstId, Request, infinity).
hash_call(InstId, Request, Timeout) ->
gen_server:call(pick(InstId), Request, Timeout).
-spec lookup(instance_id()) -> {ok, resource_data()} | {error, Reason :: term()}.
lookup(InstId) ->
case ets:lookup(emqx_resource_instance, InstId) of
[] -> {error, not_found};
[{_, Data}] -> {ok, Data#{id => InstId}}
end.
force_lookup(InstId) ->
{ok, Data} = lookup(InstId),
Data.
-spec list_all() -> [resource_data()].
list_all() ->
[Data#{id => Id} || {Id, Data} <- ets:tab2list(emqx_resource_instance)].
-spec lookup_by_type(module()) -> [resource_data()].
lookup_by_type(ResourceType) ->
[Data || #{mod := Mod} = Data <- list_all()
, Mod =:= ResourceType].
-spec load_dir(Dir :: string()) -> ok.
load_dir(Dir) ->
lists:foreach(fun load_file/1, filelib:wildcard(filename:join([Dir, "*.conf"]))).
load_file(File) ->
case ?SAFE_CALL(hocon_token:read(File)) of
{error, Reason} ->
logger:error("load resource from ~p failed: ~p", [File, Reason]);
RawConfig ->
case load_config(RawConfig) of
{ok, Data} ->
logger:debug("loaded resource instance from file: ~p, data: ~p",
[File, Data]);
{error, Reason} ->
logger:error("load resource from ~p failed: ~p", [File, Reason])
end
end.
-spec load_config(binary() | map()) -> {ok, resource_data()} | {error, term()}.
load_config(RawConfig) when is_binary(RawConfig) ->
case hocon:binary(RawConfig, #{format => map}) of
{ok, ConfigTerm} -> load_config(ConfigTerm);
Error -> Error
end;
load_config(#{<<"id">> := Id, <<"resource_type">> := ResourceTypeStr} = Config) ->
MapConfig = maps:get(<<"config">>, Config, #{}),
case emqx_resource:resource_type_from_str(ResourceTypeStr) of
{ok, ResourceType} -> parse_and_load_config(Id, ResourceType, MapConfig);
Error -> Error
end.
parse_and_load_config(InstId, ResourceType, MapConfig) ->
case emqx_resource:parse_config(ResourceType, MapConfig) of
{ok, InstConf} -> create_local(InstId, ResourceType, InstConf);
Error -> Error
end.
create_local(InstId, ResourceType, InstConf) ->
case hash_call(InstId, {create, InstId, ResourceType, InstConf}, 15000) of
{ok, Data} -> {ok, Data};
Error -> Error
end.
save_config_to_disk(InstId, ResourceType, Config) ->
%% TODO: send an event to the config handler, and the hander (single process)
%% will dump configs for all instances (from an ETS table) to a file.
file:write_file(filename:join([emqx_data_dir(), binary_to_list(InstId) ++ ".conf"]),
jsx:encode(#{id => InstId, resource_type => ResourceType,
config => emqx_resource:call_jsonify(ResourceType, Config)})).
emqx_data_dir() ->
"data".
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%------------------------------------------------------------------------------
-spec init({atom(), integer()}) ->
{ok, State :: state()} | {ok, State :: state(), timeout() | hibernate | {continue, term()}} |
{stop, Reason :: term()} | ignore.
init({Pool, Id}) ->
true = gproc_pool:connect_worker(Pool, {Pool, Id}),
{ok, #state{worker_pool = Pool, worker_id = Id}}.
handle_call({create, InstId, ResourceType, Config}, _From, State) ->
{reply, do_create(InstId, ResourceType, Config), State};
handle_call({create_dry_run, InstId, ResourceType, Config}, _From, State) ->
{reply, do_create_dry_run(InstId, ResourceType, Config), State};
handle_call({update, InstId, ResourceType, Config, Params}, _From, State) ->
{reply, do_update(InstId, ResourceType, Config, Params), State};
handle_call({remove, InstId}, _From, State) ->
{reply, do_remove(InstId), State};
handle_call({restart, InstId}, _From, State) ->
{reply, do_restart(InstId), State};
handle_call({stop, InstId}, _From, State) ->
{reply, do_stop(InstId), State};
handle_call({health_check, InstId}, _From, State) ->
{reply, do_health_check(InstId), State};
handle_call(Req, _From, State) ->
logger:error("Received unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, #state{worker_pool = Pool, worker_id = Id}) ->
gproc_pool:disconnect_worker(Pool, {Pool, Id}).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%------------------------------------------------------------------------------
%% suppress the race condition check, as these functions are protected in gproc workers
-dialyzer({nowarn_function, [do_update/4, do_create/3, do_restart/1, do_stop/1, do_health_check/1]}).
do_update(InstId, ResourceType, NewConfig, Params) ->
case lookup(InstId) of
{ok, #{mod := ResourceType, state := ResourceState, config := OldConfig}} ->
Config = emqx_resource:call_config_merge(ResourceType, OldConfig,
NewConfig, Params),
case do_create_dry_run(InstId, ResourceType, Config) of
ok ->
do_remove(ResourceType, InstId, ResourceState),
do_create(InstId, ResourceType, Config);
Error ->
Error
end;
{ok, #{mod := Mod}} when Mod =/= ResourceType ->
{error, updating_to_incorrect_resource_type};
{error, not_found} ->
do_create(InstId, ResourceType, NewConfig)
end.
do_create(InstId, ResourceType, Config) ->
case lookup(InstId) of
{ok, _} -> {error, already_created};
_ ->
case emqx_resource:call_start(InstId, ResourceType, Config) of
{ok, ResourceState} ->
ets:insert(emqx_resource_instance, {InstId,
#{mod => ResourceType, config => Config,
state => ResourceState, status => stopped}}),
_ = do_health_check(InstId),
case save_config_to_disk(InstId, ResourceType, Config) of
ok -> {ok, force_lookup(InstId)};
{error, Reason} ->
logger:error("save config for ~p resource ~p to disk failed: ~p",
[ResourceType, InstId, Reason]),
{error, Reason}
end;
{error, Reason} ->
logger:error("start ~s resource ~s failed: ~p", [ResourceType, InstId, Reason]),
{error, Reason}
end
end.
do_create_dry_run(InstId, ResourceType, Config) ->
case emqx_resource:call_start(InstId, ResourceType, Config) of
{ok, ResourceState0} ->
Return = case emqx_resource:call_health_check(InstId, ResourceType, ResourceState0) of
{ok, ResourceState1} -> ok;
{error, Reason, ResourceState1} ->
{error, Reason}
end,
_ = emqx_resource:call_stop(InstId, ResourceType, ResourceState1),
Return;
{error, Reason} ->
{error, Reason}
end.
do_remove(InstId) ->
case lookup(InstId) of
{ok, #{mod := Mod, state := ResourceState}} ->
do_remove(Mod, InstId, ResourceState);
Error ->
Error
end.
do_remove(Mod, InstId, ResourceState) ->
_ = emqx_resource:call_stop(InstId, Mod, ResourceState),
ets:delete(emqx_resource_instance, InstId),
ok.
do_restart(InstId) ->
case lookup(InstId) of
{ok, #{mod := Mod, state := ResourceState, config := Config} = Data} ->
_ = emqx_resource:call_stop(InstId, Mod, ResourceState),
case emqx_resource:call_start(InstId, Mod, Config) of
{ok, ResourceState} ->
ets:insert(emqx_resource_instance,
{InstId, Data#{state => ResourceState, status => started}}),
ok;
{error, Reason} ->
ets:insert(emqx_resource_instance, {InstId, Data#{status => stopped}}),
{error, Reason}
end;
Error ->
Error
end.
do_stop(InstId) ->
case lookup(InstId) of
{ok, #{mod := Mod, state := ResourceState} = Data} ->
_ = emqx_resource:call_stop(InstId, Mod, ResourceState),
ets:insert(emqx_resource_instance, {InstId, Data#{status => stopped}}),
ok;
Error ->
Error
end.
do_health_check(InstId) ->
case lookup(InstId) of
{ok, #{mod := Mod, state := ResourceState0} = Data} ->
case emqx_resource:call_health_check(InstId, Mod, ResourceState0) of
{ok, ResourceState1} ->
ets:insert(emqx_resource_instance,
{InstId, Data#{status => started, state => ResourceState1}}),
ok;
{error, Reason, ResourceState1} ->
logger:error("health check for ~p failed: ~p", [InstId, Reason]),
ets:insert(emqx_resource_instance,
{InstId, Data#{status => stopped, state => ResourceState1}}),
{error, Reason}
end;
Error ->
Error
end.
%%------------------------------------------------------------------------------
%% internal functions
%%------------------------------------------------------------------------------
proc_name(Mod, Id) ->
list_to_atom(lists:concat([Mod, "_", Id])).
pick(InstId) ->
gproc_pool:pick_worker(emqx_resource_instance, InstId).

View File

@ -0,0 +1,58 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-module(emqx_resource_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-define(RESOURCE_INST_MOD, emqx_resource_instance).
-define(POOL_SIZE, 64). %% set a very large pool size in case all the workers busy
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
TabOpts = [named_table, set, public, {read_concurrency, true}],
_ = ets:new(emqx_resource_instance, TabOpts),
SupFlags = #{strategy => one_for_one, intensity => 10, period => 10},
Pool = ?RESOURCE_INST_MOD,
Mod = ?RESOURCE_INST_MOD,
ensure_pool(Pool, hash, [{size, ?POOL_SIZE}]),
{ok, {SupFlags, [
begin
ensure_pool_worker(Pool, {Pool, Idx}, Idx),
#{id => {Mod, Idx},
start => {Mod, start_link, [Pool, Idx]},
restart => transient,
shutdown => 5000, type => worker, modules => [Mod]}
end || Idx <- lists:seq(1, ?POOL_SIZE)]}}.
%% internal functions
ensure_pool(Pool, Type, Opts) ->
try gproc_pool:new(Pool, Type, Opts)
catch
error:exists -> ok
end.
ensure_pool_worker(Pool, Name, Slot) ->
try gproc_pool:add_worker(Pool, Name, Slot)
catch
error:exists -> ok
end.

View File

@ -0,0 +1,114 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-module(emqx_resource_transform).
-include_lib("syntax_tools/include/merl.hrl").
-export([parse_transform/2]).
parse_transform(Forms, _Opts) ->
Mod = hd([M || {attribute, _, module, M} <- Forms]),
AST = trans(Mod, proplists:delete(eof, Forms)),
debug_print(Mod, AST),
AST.
-ifdef(RESOURCE_DEBUG).
debug_print(Mod, Ts) ->
{ok, Io} = file:open("./" ++ atom_to_list(Mod) ++ ".trans.erl", [write]),
_ = do_debug_print(Io, Ts),
file:close(Io).
do_debug_print(Io, Ts) when is_list(Ts) ->
lists:foreach(fun(T) -> do_debug_print(Io, T) end, Ts);
do_debug_print(Io, T) ->
io:put_chars(Io, erl_prettypr:format(merl:tree(T))),
io:nl(Io).
-else.
debug_print(_Mod, _AST) ->
ok.
-endif.
trans(Mod, Forms) ->
forms(Mod, Forms) ++ [erl_syntax:revert(erl_syntax:eof_marker())].
forms(Mod, [F0 | Fs0]) ->
case form(Mod, F0) of
{CurrForm, AppendedForms} ->
CurrForm ++ forms(Mod, Fs0) ++ AppendedForms;
{AHeadForms, CurrForm, AppendedForms} ->
AHeadForms ++ CurrForm ++ forms(Mod, Fs0) ++ AppendedForms
end;
forms(_, []) -> [].
form(Mod, Form) ->
case Form of
?Q("-emqx_resource_api_path('@Path').") ->
{fix_spec_attrs() ++ fix_api_attrs(Mod, erl_syntax:concrete(Path))
++ fix_api_exports(),
[],
fix_spec_funcs(Mod) ++ fix_api_funcs(Mod)};
_ ->
%io:format("---other form: ~p~n", [Form]),
{[], [Form], []}
end.
fix_spec_attrs() ->
[ ?Q("-export([emqx_resource_schema/0]).")
, ?Q("-export([structs/0]).")
, ?Q("-behaviour(hocon_schema).")
].
fix_spec_funcs(_Mod) ->
[ (?Q("emqx_resource_schema() -> <<\"demo_swagger_schema\">>."))
, ?Q("structs() -> [\"config\"].")
].
fix_api_attrs(Mod, Path) ->
BaseName = atom_to_list(Mod),
[erl_syntax:revert(
erl_syntax:attribute(?Q("rest_api"), [
erl_syntax:abstract(#{
name => list_to_atom(Act ++ "_" ++ BaseName),
method => Method,
path => mk_path(Path, WithId),
func => Func,
descr => Act ++ " the " ++ BaseName})]))
|| {Act, Method, WithId, Func} <- [
{"list", 'GET', noid, api_get_all},
{"get", 'GET', id, api_get},
{"update", 'PUT', id, api_put},
{"delete", 'DELETE', id, api_delete}]].
fix_api_exports() ->
[?Q("-export([api_get_all/2, api_get/2, api_put/2, api_delete/2]).")].
fix_api_funcs(Mod) ->
[erl_syntax:revert(?Q(
"api_get_all(Binding, Params) ->
emqx_resource_api:get_all('@Mod@', Binding, Params).")),
erl_syntax:revert(?Q(
"api_get(Binding, Params) ->
emqx_resource_api:get('@Mod@', Binding, Params).")),
erl_syntax:revert(?Q(
"api_put(Binding, Params) ->
emqx_resource_api:put('@Mod@', Binding, Params).")),
erl_syntax:revert(?Q(
"api_delete(Binding, Params) ->
emqx_resource_api:delete('@Mod@', Binding, Params)."))
].
mk_path(Path, id) -> string:trim(Path, trailing, "/") ++ "/:bin:id";
mk_path(Path, noid) -> Path.

View File

@ -0,0 +1,16 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-module(emqx_resource_uitils).

View File

@ -0,0 +1,63 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%%--------------------------------------------------------------------
-module(emqx_resource_validator).
-export([ min/2
, max/2
, equals/2
, enum/1
, required/1
]).
max(Type, Max) ->
limit(Type, '=<', Max).
min(Type, Min) ->
limit(Type, '>=', Min).
equals(Type, Expected) ->
limit(Type, '==', Expected).
enum(Items) ->
fun(Value) ->
return(lists:member(Value, Items),
err_limit({enum, {is_member_of, Items}, {got, Value}}))
end.
required(ErrMsg) ->
fun(undefined) -> {error, ErrMsg};
(_) -> ok
end.
limit(Type, Op, Expected) ->
L = len(Type),
fun(Value) ->
Got = L(Value),
return(erlang:Op(Got, Expected),
err_limit({Type, {Op, Expected}, {got, Got}}))
end.
len(array) -> fun erlang:length/1;
len(string) -> fun string:length/1;
len(_Type) -> fun(Val) -> Val end.
err_limit({Type, {Op, Expected}, {got, Got}}) ->
io_lib:format("Expect the ~s value ~s ~p but got: ~p", [Type, Op, Expected, Got]).
return(true, _) -> ok;
return(false, Error) ->
{error, Error}.

View File

@ -37,5 +37,5 @@ retainer.max_payload_size = 1MB
## - 30m: 30 minutes ## - 30m: 30 minutes
## - 20s: 20 seconds ## - 20s: 20 seconds
## ##
## Defaut: 0 ## Default: 0
retainer.expiry_interval = 0 retainer.expiry_interval = 0

View File

@ -18,7 +18,7 @@
{profiles, {profiles,
[{test, [{test,
[{deps, [{deps,
[{emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.2.2"}}}, [
{emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.3"}}}]} {emqtt, {git, "https://github.com/emqx/emqtt", {tag, "1.2.3"}}}]}
]} ]}
]}. ]}.

View File

@ -32,7 +32,7 @@ rule_engine.ignore_sys_message = on
## ##
## QoS-Level: qos0/qos1/qos2 ## QoS-Level: qos0/qos1/qos2
#rule_engine.events.client_connected = on, qos1 #rule_engine.events.client_connected = "on, qos1"
rule_engine.events.client_connected = off rule_engine.events.client_connected = off
rule_engine.events.client_disconnected = off rule_engine.events.client_disconnected = off
rule_engine.events.session_subscribed = off rule_engine.events.session_subscribed = off

View File

@ -2554,23 +2554,15 @@ start_apps() ->
[start_apps(App, SchemaFile, ConfigFile) || [start_apps(App, SchemaFile, ConfigFile) ||
{App, SchemaFile, ConfigFile} {App, SchemaFile, ConfigFile}
<- [{emqx, deps_path(emqx, "priv/emqx.schema"), <- [{emqx, deps_path(emqx, "priv/emqx.schema"),
deps_path(emqx, "etc/emqx.conf")}, deps_path(emqx, "etc/emqx.conf.rendered")},
{emqx_rule_engine, local_path("priv/emqx_rule_engine.schema"), {emqx_rule_engine, local_path("priv/emqx_rule_engine.schema"),
local_path("etc/emqx_rule_engine.conf")}]]. local_path("etc/emqx_rule_engine.conf")}]].
start_apps(App, SchemaFile, ConfigFile) -> start_apps(App, SchemaFile, ConfigFile) ->
read_schema_configs(App, SchemaFile, ConfigFile), emqx_ct_helpers:read_schema_configs(SchemaFile, ConfigFile),
set_special_configs(App), set_special_configs(App),
{ok, _} = application:ensure_all_started(App). {ok, _} = application:ensure_all_started(App).
read_schema_configs(App, SchemaFile, ConfigFile) ->
ct:pal("Read configs - SchemaFile: ~p, ConfigFile: ~p", [SchemaFile, ConfigFile]),
Schema = cuttlefish_schema:files([SchemaFile]),
Conf = conf_parse:file(ConfigFile),
NewConfig = cuttlefish_generator:map(Schema, Conf),
Vals = proplists:get_value(App, NewConfig, []),
[application:set_env(App, Par, Value) || {Par, Value} <- Vals].
deps_path(App, RelativePath) -> deps_path(App, RelativePath) ->
%% Note: not lib_dir because etc dir is not sym-link-ed to _build dir %% Note: not lib_dir because etc dir is not sym-link-ed to _build dir
%% but priv dir is %% but priv dir is

View File

@ -6,7 +6,7 @@
## ##
## Value: IP:Port | Port ## Value: IP:Port | Port
## ##
## Examples: 1884, 127.0.0.1:1884, ::1:1884 ## Examples: 1884, "127.0.0.1:1884", "::1:1884"
mqtt.sn.port = 1884 mqtt.sn.port = 1884
## The duration that emqx-sn broadcast ADVERTISE message through. ## The duration that emqx-sn broadcast ADVERTISE message through.
@ -37,8 +37,8 @@ mqtt.sn.idle_timeout = 30s
## The pre-defined topic name corresponding to the pre-defined topic id of N. ## The pre-defined topic name corresponding to the pre-defined topic id of N.
## Note that the pre-defined topic id of 0 is reserved. ## Note that the pre-defined topic id of 0 is reserved.
mqtt.sn.predefined.topic.0 = reserved mqtt.sn.predefined.topic.0 = reserved
mqtt.sn.predefined.topic.1 = /predefined/topic/name/hello mqtt.sn.predefined.topic.1 = "/predefined/topic/name/hello"
mqtt.sn.predefined.topic.2 = /predefined/topic/name/nice mqtt.sn.predefined.topic.2 = "/predefined/topic/name/nice"
## Default username for MQTT-SN. This parameter is optional. If specified, ## Default username for MQTT-SN. This parameter is optional. If specified,
## emq-sn will connect EMQ core with this username. It is useful if any auth ## emq-sn will connect EMQ core with this username. It is useful if any auth

View File

@ -1,23 +1,19 @@
%%-*- mode: erlang -*- %%-*- mode: erlang -*-
%% emqx_sn config mapping %% emqx_sn config mapping
{mapping, "mqtt.sn.port", "emqx_sn.port", [ {mapping, "mqtt.sn.port", "emqx_sn.port", [
{default, "1884"}, {default, 1884},
{datatype, string} {datatype, [integer, ip]}
]}. ]}.
{translation, "emqx_sn.port", fun(Conf) -> {translation, "emqx_sn.port", fun(Conf) ->
case re:split(cuttlefish:conf_get("mqtt.sn.port", Conf, ""), ":", [{return, list}]) of case cuttlefish:conf_get("mqtt.sn.port", Conf, undefined) of
[Port] -> Port when is_integer(Port) ->
{{0,0,0,0}, list_to_integer(Port)}; {{0,0,0,0}, Port};
Tokens -> {Ip, Port} ->
Port = lists:last(Tokens), case inet:parse_address(Ip) of
IP = case inet:parse_address(lists:flatten(lists:join(":", Tokens -- [Port]))) of {ok ,R} -> {R, Port};
{error, Reason} -> _ -> {Ip, Port}
throw({invalid_ip_address, Reason}); end
{ok, X} -> X
end,
Port1 = list_to_integer(Port),
{IP, Port1}
end end
end}. end}.

View File

@ -2,8 +2,7 @@
{plugins, [rebar3_proper]}. {plugins, [rebar3_proper]}.
{deps, {deps,
[{esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.4"}}}, [{esockd, {git, "https://github.com/emqx/esockd", {tag, "5.7.4"}}}
{cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.0.0"}}}
]}. ]}.
{edoc_opts, [{preprocess, true}]}. {edoc_opts, [{preprocess, true}]}.

View File

@ -8,7 +8,7 @@
## The Port that stomp listener will bind. ## The Port that stomp listener will bind.
## ##
## Value: Port ## Value: Port
stomp.listener = 61613 stomp.listener.port = 61613
## The acceptor pool for stomp listener. ## The acceptor pool for stomp listener.
## ##
@ -28,22 +28,22 @@ stomp.listener.max_connections = 512
## Path to the file containing the user's private PEM-encoded key. ## Path to the file containing the user's private PEM-encoded key.
## ##
## Value: File ## Value: File
## stomp.listener.keyfile = etc/certs/key.pem ## stomp.listener.keyfile = "etc/certs/key.pem"
## Path to a file containing the user certificate. ## Path to a file containing the user certificate.
## ##
## Value: File ## Value: File
## stomp.listener.certfile = etc/certs/cert.pem ## stomp.listener.certfile = "etc/certs/cert.pem"
## Path to the file containing PEM-encoded CA certificates. ## Path to the file containing PEM-encoded CA certificates.
## ##
## Value: File ## Value: File
## stomp.listener.cacertfile = etc/certs/cacert.pem ## stomp.listener.cacertfile = "etc/certs/cacert.pem"
## See: 'listener.ssl.<name>.dhfile' in emq.conf ## See: 'listener.ssl.<name>.dhfile' in emq.conf
## ##
## Value: File ## Value: File
## stomp.listener.dhfile = etc/certs/dh-params.pem ## stomp.listener.dhfile = "etc/certs/dh-params.pem"
## See: 'listener.ssl.<name>.verify' in emq.conf ## See: 'listener.ssl.<name>.verify' in emq.conf
## ##
@ -59,7 +59,7 @@ stomp.listener.max_connections = 512
## ##
## Value: String, seperated by ',' ## Value: String, seperated by ','
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier ## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
## stomp.listener.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 ## stomp.listener.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## SSL Handshake timeout. ## SSL Handshake timeout.
## ##
@ -69,7 +69,7 @@ stomp.listener.max_connections = 512
## See: 'listener.ssl.<name>.ciphers' in emq.conf ## See: 'listener.ssl.<name>.ciphers' in emq.conf
## ##
## Value: Ciphers ## Value: Ciphers
## stomp.listener.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA ## stomp.listener.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## See: 'listener.ssl.<name>.secure_renegotiate' in emq.conf ## See: 'listener.ssl.<name>.secure_renegotiate' in emq.conf
## ##

View File

@ -1,7 +1,7 @@
%%-*- mode: erlang -*- %%-*- mode: erlang -*-
%% emqx_stomp config mapping %% emqx_stomp config mapping
{mapping, "stomp.listener", "emqx_stomp.listener", [ {mapping, "stomp.listener.port", "emqx_stomp.listener", [
{default, 61613}, {default, 61613},
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
@ -72,7 +72,7 @@
]}. ]}.
{translation, "emqx_stomp.listener", fun(Conf) -> {translation, "emqx_stomp.listener", fun(Conf) ->
Port = cuttlefish:conf_get("stomp.listener", Conf), Port = cuttlefish:conf_get("stomp.listener.port", Conf),
Acceptors = cuttlefish:conf_get("stomp.listener.acceptors", Conf), Acceptors = cuttlefish:conf_get("stomp.listener.acceptors", Conf),
MaxConnections = cuttlefish:conf_get("stomp.listener.max_connections", Conf), MaxConnections = cuttlefish:conf_get("stomp.listener.max_connections", Conf),
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,

View File

@ -1,6 +1,6 @@
{application, emqx_stomp, {application, emqx_stomp,
[{description, "EMQ X Stomp Protocol Plugin"}, [{description, "EMQ X Stomp Protocol Plugin"},
{vsn, "4.3.0"}, % strict semver, bump manually! {vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_stomp_sup]}, {registered, [emqx_stomp_sup]},
{applications, [kernel,stdlib]}, {applications, [kernel,stdlib]},

View File

@ -5,16 +5,16 @@
## Webhook URL ## Webhook URL
## ##
## Value: String ## Value: String
web.hook.url = http://127.0.0.1:80 web.hook.url = "http://127.0.0.1:80"
## HTTP Headers ## HTTP Headers
## ##
## Example: ## Example:
## 1. web.hook.headers.content-type = application/json ## 1. web.hook.headers.content-type = "application/json"
## 2. web.hook.headers.accept = * ## 2. web.hook.headers.accept = "*"
## ##
## Value: String ## Value: String
web.hook.headers.content-type = application/json web.hook.headers.content-type = "application/json"
## The encoding format of the payload field in the HTTP body ## The encoding format of the payload field in the HTTP body
## The payload field only appears in the on_message_publish and on_message_delivered actions ## The payload field only appears in the on_message_publish and on_message_delivered actions
@ -63,15 +63,15 @@ web.hook.pool_size = 32
## ##
## Format: ## Format:
## web.hook.rule.<HookName>.<No> = <Spec> ## web.hook.rule.<HookName>.<No> = <Spec>
#web.hook.rule.client.connect.1 = {"action": "on_client_connect"} #web.hook.rule.client.connect.1 = "{"action": "on_client_connect"}"
#web.hook.rule.client.connack.1 = {"action": "on_client_connack"} #web.hook.rule.client.connack.1 = "{"action": "on_client_connack"}"
#web.hook.rule.client.connected.1 = {"action": "on_client_connected"} #web.hook.rule.client.connected.1 = "{"action": "on_client_connected"}"
#web.hook.rule.client.disconnected.1 = {"action": "on_client_disconnected"} #web.hook.rule.client.disconnected.1 = "{"action": "on_client_disconnected"}"
#web.hook.rule.client.subscribe.1 = {"action": "on_client_subscribe"} #web.hook.rule.client.subscribe.1 = "{"action": "on_client_subscribe"}"
#web.hook.rule.client.unsubscribe.1 = {"action": "on_client_unsubscribe"} #web.hook.rule.client.unsubscribe.1 = "{"action": "on_client_unsubscribe"}"
#web.hook.rule.session.subscribed.1 = {"action": "on_session_subscribed"} #web.hook.rule.session.subscribed.1 = "{"action": "on_session_subscribed"}"
#web.hook.rule.session.unsubscribed.1 = {"action": "on_session_unsubscribed"} #web.hook.rule.session.unsubscribed.1 = "{"action": "on_session_unsubscribed"}"
#web.hook.rule.session.terminated.1 = {"action": "on_session_terminated"} #web.hook.rule.session.terminated.1 = "{"action": "on_session_terminated"}"
#web.hook.rule.message.publish.1 = {"action": "on_message_publish"} #web.hook.rule.message.publish.1 = "{"action": "on_message_publish"}"
#web.hook.rule.message.delivered.1 = {"action": "on_message_delivered"} #web.hook.rule.message.delivered.1 = "{"action": "on_message_delivered"}"
#web.hook.rule.message.acked.1 = {"action": "on_message_acked"} #web.hook.rule.message.acked.1 = ""{"action": "on_message_acked"}"

View File

@ -15,4 +15,4 @@
warnings_as_errors, deprecated_functions]}. warnings_as_errors, deprecated_functions]}.
{cover_enabled, true}. {cover_enabled, true}.
{cover_opts, [verbose]}. {cover_opts, [verbose]}.
{cover_export_enabled, true}. {cover_export_enabled, true}.

View File

@ -20,8 +20,8 @@ mkdir -p "$RUNNER_LOG_DIR"
# Make sure data directory exists # Make sure data directory exists
mkdir -p "$RUNNER_DATA_DIR" mkdir -p "$RUNNER_DATA_DIR"
# cuttlefish try to read environment variables starting with "EMQX_" # hocon try to read environment variables starting with "EMQX_"
export CUTTLEFISH_ENV_OVERRIDE_PREFIX='EMQX_' export HOCON_ENV_OVERRIDE_PREFIX='EMQX_'
relx_usage() { relx_usage() {
command="$1" command="$1"
@ -123,9 +123,6 @@ fi
# Echo to stderr on errors # Echo to stderr on errors
echoerr() { echo "$@" 1>&2; } echoerr() { echo "$@" 1>&2; }
# By default, use cuttlefish to generate app.config and vm.args
CUTTLEFISH="${USE_CUTTLEFISH:-yes}"
SED_REPLACE="sed -i " SED_REPLACE="sed -i "
case $(sed --help 2>&1) in case $(sed --help 2>&1) in
*GNU*) SED_REPLACE="sed -i ";; *GNU*) SED_REPLACE="sed -i ";;
@ -202,53 +199,45 @@ generate_config() {
## changing the config 'log.rotation.size' ## changing the config 'log.rotation.size'
rm -rf "${RUNNER_LOG_DIR}"/*.siz rm -rf "${RUNNER_LOG_DIR}"/*.siz
if [ "$CUTTLEFISH" != "yes" ]; then ## todo: include license conf option to hocon escript
# Note: we have added a parameter '-vm_args' to this. It ## EMQX_LICENSE_CONF_OPTION=""
# appears redundant but it is not! the erlang vm allows us to ## if [ "${EMQX_LICENSE_CONF:-}" != "" ]; then
# access all arguments to the erl command EXCEPT '-args_file', ## EMQX_LICENSE_CONF_OPTION="-i ${EMQX_LICENSE_CONF}"
# so in order to get access to this file location from within ## fi
# the vm, we need to pass it in twice.
CONFIG_ARGS=" -config $RUNNER_ETC_DIR/app.config -args_file $RUNNER_ETC_DIR/vm.args -vm_args $RUNNER_ETC_DIR/vm.args "
else
EMQX_LICENSE_CONF_OPTION=""
if [ "${EMQX_LICENSE_CONF:-}" != "" ]; then
EMQX_LICENSE_CONF_OPTION="-i ${EMQX_LICENSE_CONF}"
fi
set +e set +e
# shellcheck disable=SC2086 # shellcheck disable=SC2086
CUTTLEFISH_OUTPUT="$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/cuttlefish -v -i "$REL_DIR"/emqx.schema $EMQX_LICENSE_CONF_OPTION -c "$RUNNER_ETC_DIR"/emqx.conf -d "$RUNNER_DATA_DIR"/configs generate)" HOCON_OUTPUT="$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/hocon -s emqx_schema -c "$RUNNER_ETC_DIR"/emqx.conf -d "$RUNNER_DATA_DIR"/configs generate)"
# shellcheck disable=SC2181 # shellcheck disable=SC2181
RESULT=$? RESULT=$?
set -e set -e
if [ $RESULT -gt 0 ]; then if [ $RESULT -gt 0 ]; then
echo "$CUTTLEFISH_OUTPUT" echo "$HOCON_OUTPUT"
exit $RESULT exit $RESULT
fi
# print override from environment variables (EMQX_*)
echo "$CUTTLEFISH_OUTPUT" | sed -e '$d'
CONFIG_ARGS=$(echo "$CUTTLEFISH_OUTPUT" | tail -n 1)
## Merge cuttlefish generated *.args into the vm.args
CUTTLE_GEN_ARG_FILE=$(echo "$CONFIG_ARGS" | sed -n 's/^.*\(vm_args[[:space:]]\)//p' | awk '{print $1}')
TMP_ARG_FILE="$RUNNER_DATA_DIR/configs/vm.args.tmp"
cp "$RUNNER_ETC_DIR/vm.args" "$TMP_ARG_FILE"
echo "" >> "$TMP_ARG_FILE"
echo "-pa ${REL_DIR}/consolidated" >> "$TMP_ARG_FILE"
sed '/^#/d' "$CUTTLE_GEN_ARG_FILE" | sed '/^$/d' | while IFS='' read -r ARG_LINE || [ -n "$ARG_LINE" ]; do
ARG_KEY=$(echo "$ARG_LINE" | awk '{$NF="";print}')
ARG_VALUE=$(echo "$ARG_LINE" | awk '{print $NF}')
TMP_ARG_VALUE=$(grep "^$ARG_KEY" "$TMP_ARG_FILE" | awk '{print $NF}')
if [ "$ARG_VALUE" != "$TMP_ARG_VALUE" ] ; then
if [ -n "$TMP_ARG_VALUE" ]; then
sh -c "$SED_REPLACE 's/^$ARG_KEY.*$/$ARG_LINE/' $TMP_ARG_FILE"
else
echo "$ARG_LINE" >> "$TMP_ARG_FILE"
fi
fi
done
mv -f "$TMP_ARG_FILE" "$CUTTLE_GEN_ARG_FILE"
fi fi
# print override from environment variables (EMQX_*)
echo "$HOCON_OUTPUT" | sed -e '$d'
CONFIG_ARGS=$(echo "$HOCON_OUTPUT" | tail -n 1)
## Merge hocon generated *.args into the vm.args
HOCON_GEN_ARG_FILE=$(echo "$CONFIG_ARGS" | sed -n 's/^.*\(vm_args[[:space:]]\)//p' | awk '{print $1}')
TMP_ARG_FILE="$RUNNER_DATA_DIR/configs/vm.args.tmp"
cp "$RUNNER_ETC_DIR/vm.args" "$TMP_ARG_FILE"
echo "" >> "$TMP_ARG_FILE"
echo "-pa ${REL_DIR}/consolidated" >> "$TMP_ARG_FILE"
sed '/^#/d' "$HOCON_GEN_ARG_FILE" | sed '/^$/d' | while IFS='' read -r ARG_LINE || [ -n "$ARG_LINE" ]; do
ARG_KEY=$(echo "$ARG_LINE" | awk '{$NF="";print}')
ARG_VALUE=$(echo "$ARG_LINE" | awk '{print $NF}')
TMP_ARG_VALUE=$(grep "^$ARG_KEY" "$TMP_ARG_FILE" | awk '{print $NF}')
if [ "$ARG_VALUE" != "$TMP_ARG_VALUE" ] ; then
if [ -n "$TMP_ARG_VALUE" ]; then
sh -c "$SED_REPLACE 's/^$ARG_KEY.*$/$ARG_LINE/' $TMP_ARG_FILE"
else
echo "$ARG_LINE" >> "$TMP_ARG_FILE"
fi
fi
done
mv -f "$TMP_ARG_FILE" "$HOCON_GEN_ARG_FILE"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if ! relx_nodetool chkconfig $CONFIG_ARGS; then if ! relx_nodetool chkconfig $CONFIG_ARGS; then
@ -303,7 +292,8 @@ if [ -z "$NAME_ARG" ]; then
NODENAME="$(grep -E '^-name' "$LATEST_VM_ARGS" | awk '{print $2}')" NODENAME="$(grep -E '^-name' "$LATEST_VM_ARGS" | awk '{print $2}')"
else else
# for boot commands, inspect emqx.conf for node name # for boot commands, inspect emqx.conf for node name
NODENAME=$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/cuttlefish -i "$REL_DIR"/emqx.schema -c "$RUNNER_ETC_DIR"/emqx.conf get node.name) # todo: use get command from hocon escript
NODENAME="$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/hocon -s emqx_schema -c "$RUNNER_ETC_DIR"/emqx.conf get node.name | tr -d \")"
fi fi
fi fi
if [ -z "$NODENAME" ]; then if [ -z "$NODENAME" ]; then
@ -329,7 +319,7 @@ PIPE_DIR="${PIPE_DIR:-/$RUNNER_DATA_DIR/${WHOAMI}_erl_pipes/$NAME/}"
COOKIE="${EMQX_NODE_COOKIE:-}" COOKIE="${EMQX_NODE_COOKIE:-}"
if [ -z "$COOKIE" ]; then if [ -z "$COOKIE" ]; then
if [ "$IS_BOOT_COMMAND" = 'yes' ]; then if [ "$IS_BOOT_COMMAND" = 'yes' ]; then
COOKIE=$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/cuttlefish -i "$REL_DIR"/emqx.schema -c "$RUNNER_ETC_DIR"/emqx.conf get node.cookie) COOKIE="$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/hocon -s emqx_schema -c "$RUNNER_ETC_DIR"/emqx.conf get node.cookie | tr -d \")"
else else
# shellcheck disable=SC2012,SC2086 # shellcheck disable=SC2012,SC2086
LATEST_VM_ARGS="$(ls -t $RUNNER_DATA_DIR/configs/vm.*.args | head -1)" LATEST_VM_ARGS="$(ls -t $RUNNER_DATA_DIR/configs/vm.*.args | head -1)"

View File

@ -5,4 +5,5 @@
{emqx_retainer, {{enable_plugin_emqx_retainer}}}. {emqx_retainer, {{enable_plugin_emqx_retainer}}}.
{emqx_telemetry, {{enable_plugin_emqx_telemetry}}}. {emqx_telemetry, {{enable_plugin_emqx_telemetry}}}.
{emqx_rule_engine, {{enable_plugin_emqx_rule_engine}}}. {emqx_rule_engine, {{enable_plugin_emqx_rule_engine}}}.
{emqx_resource, {{enable_plugin_emqx_resource}}}.
{emqx_bridge_mqtt, {{enable_plugin_emqx_bridge_mqtt}}}. {emqx_bridge_mqtt, {{enable_plugin_emqx_bridge_mqtt}}}.

View File

@ -56,7 +56,7 @@ cluster.autoclean = 5m
## Node list of the cluster. ## Node list of the cluster.
## ##
## Value: String ## Value: String
## cluster.static.seeds = emqx1@127.0.0.1,emqx2@127.0.0.1 ## cluster.static.seeds = "emqx1@127.0.0.1,emqx2@127.0.0.1"
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Cluster using IP Multicast. ## Cluster using IP Multicast.
@ -64,19 +64,19 @@ cluster.autoclean = 5m
## IP Multicast Address. ## IP Multicast Address.
## ##
## Value: IP Address ## Value: IP Address
## cluster.mcast.addr = 239.192.0.1 ## cluster.mcast.addr = "239.192.0.1"
## Multicast Ports. ## Multicast Ports.
## ##
## Value: Port List ## Value: Port List
## cluster.mcast.ports = 4369,4370 ## cluster.mcast.ports = "4369,4370"
## Multicast Iface. ## Multicast Iface.
## ##
## Value: Iface Address ## Value: Iface Address
## ##
## Default: 0.0.0.0 ## Default: "0.0.0.0"
## cluster.mcast.iface = 0.0.0.0 ## cluster.mcast.iface = "0.0.0.0"
## Multicast Ttl. ## Multicast Ttl.
## ##
@ -107,7 +107,14 @@ cluster.autoclean = 5m
## Etcd server list, seperated by ','. ## Etcd server list, seperated by ','.
## ##
## Value: String ## Value: String
## cluster.etcd.server = http://127.0.0.1:2379 ## cluster.etcd.server = "http://127.0.0.1:2379"
## Etcd api version
##
## Value: Enum
## - v2
## - v3
## cluster.etcd.version = v3
## The prefix helps build nodes path in etcd. Each node in the cluster ## The prefix helps build nodes path in etcd. Each node in the cluster
## will create a path in etcd: v2/keys/<prefix>/<cluster.name>/<node.name> ## will create a path in etcd: v2/keys/<prefix>/<cluster.name>/<node.name>
@ -125,18 +132,18 @@ cluster.autoclean = 5m
## Path to a file containing the client's private PEM-encoded key. ## Path to a file containing the client's private PEM-encoded key.
## ##
## Value: File ## Value: File
## cluster.etcd.ssl.keyfile = {{ platform_etc_dir }}/certs/client-key.pem ## cluster.etcd.ssl.keyfile = "{{ platform_etc_dir }}/certs/client-key.pem"
## The path to a file containing the client's certificate. ## The path to a file containing the client's certificate.
## ##
## Value: File ## Value: File
## cluster.etcd.ssl.certfile = {{ platform_etc_dir }}/certs/client.pem ## cluster.etcd.ssl.certfile = "{{ platform_etc_dir }}/certs/client.pem"
## Path to the file containing PEM-encoded CA certificates. The CA certificates ## Path to the file containing PEM-encoded CA certificates. The CA certificates
## are used during server authentication and when building the client certificate chain. ## are used during server authentication and when building the client certificate chain.
## ##
## Value: File ## Value: File
## cluster.etcd.ssl.cacertfile = {{ platform_etc_dir }}/certs/ca.pem ## cluster.etcd.ssl.cacertfile = "{{ platform_etc_dir }}/certs/ca.pem"
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Cluster using Kubernetes ## Cluster using Kubernetes
@ -144,7 +151,7 @@ cluster.autoclean = 5m
## Kubernetes API server list, seperated by ','. ## Kubernetes API server list, seperated by ','.
## ##
## Value: String ## Value: String
## cluster.k8s.apiserver = http://10.110.111.204:8080 ## cluster.k8s.apiserver = "http://10.110.111.204:8080"
## The service name helps lookup EMQ nodes in the cluster. ## The service name helps lookup EMQ nodes in the cluster.
## ##
@ -184,17 +191,17 @@ cluster.autoclean = 5m
## Value: <name>@<host> ## Value: <name>@<host>
## ##
## Default: emqx@127.0.0.1 ## Default: emqx@127.0.0.1
node.name = emqx@127.0.0.1 node.name = "emqx@127.0.0.1"
## Cookie for distributed node communication. ## Cookie for distributed node communication.
## ##
## Value: String ## Value: String
node.cookie = emqxsecretcookie node.cookie = "emqxsecretcookie"
## Data dir for the node ## Data dir for the node
## ##
## Value: Folder ## Value: Folder
node.data_dir = {{ platform_data_dir }} node.data_dir = "{{ platform_data_dir }}"
## Heartbeat monitoring of an Erlang runtime system. Comment the line to disable ## Heartbeat monitoring of an Erlang runtime system. Comment the line to disable
## heartbeat, or set the value as 'on' ## heartbeat, or set the value as 'on'
@ -271,14 +278,14 @@ node.global_gc_interval = 15m
## Crash dump log file. ## Crash dump log file.
## ##
## Value: Log file ## Value: Log file
node.crash_dump = {{ platform_log_dir }}/crash.dump node.crash_dump = "{{ platform_log_dir }}/crash.dump"
## Specify SSL Options in the file if using SSL for Erlang Distribution. ## Specify SSL Options in the file if using SSL for Erlang Distribution.
## ##
## Value: File ## Value: File
## ##
## vm.args: -ssl_dist_optfile <File> ## vm.args: -ssl_dist_optfile <File>
## node.ssl_dist_optfile = {{ platform_etc_dir }}/ssl_dist.conf ## node.ssl_dist_optfile = "{{ platform_etc_dir }}/ssl_dist.conf"
## Sets the net_kernel tick time. TickTime is specified in seconds. ## Sets the net_kernel tick time. TickTime is specified in seconds.
## Notice that all communicating nodes are to have the same TickTime ## Notice that all communicating nodes are to have the same TickTime
@ -419,10 +426,17 @@ log.to = file
## Default: warning ## Default: warning
log.level = warning log.level = warning
## Timezone offset to display in logs
## Value:
## - "system" use system zone
## - "utc" for Universal Coordinated Time (UTC)
## - "+hh:mm" or "-hh:mm" for a specified offset
log.time_offset = system
## The dir for log files. ## The dir for log files.
## ##
## Value: Folder ## Value: Folder
log.dir = {{ platform_log_dir }} log.dir = "{{ platform_log_dir }}"
## The log filename for logs of level specified in "log.level". ## The log filename for logs of level specified in "log.level".
## ##
@ -460,7 +474,7 @@ log.file = emqx.log
## ##
## Value: on | off ## Value: on | off
## Default: on ## Default: on
log.rotation = on log.rotation.enable = on
## Maximum size of each log file. ## Maximum size of each log file.
## ##
@ -579,7 +593,7 @@ log.rotation.count = 5
## Value: MaxBurstCount,TimeWindow ## Value: MaxBurstCount,TimeWindow
## Default: disabled ## Default: disabled
## ##
#log.burst_limit = 20000, 1s #log.burst_limit = "20000, 1s"
## CONFIG_SECTION_END=logger =================================================== ## CONFIG_SECTION_END=logger ===================================================
@ -591,42 +605,42 @@ log.rotation.count = 5
## Notice: Disable the option in production deployment! ## Notice: Disable the option in production deployment!
## ##
## Value: true | false ## Value: true | false
allow_anonymous = true acl.allow_anonymous = true
## Allow or deny if no ACL rules matched. ## Allow or deny if no ACL rules matched.
## ##
## Value: allow | deny ## Value: allow | deny
acl_nomatch = allow acl.acl_nomatch = allow
## Default ACL File. ## Default ACL File.
## ##
## Value: File Name ## Value: File Name
acl_file = {{ platform_etc_dir }}/acl.conf acl.acl_file = "{{ platform_etc_dir }}/acl.conf"
## Whether to enable ACL cache. ## Whether to enable ACL cache.
## ##
## If enabled, ACLs roles for each client will be cached in the memory ## If enabled, ACLs roles for each client will be cached in the memory
## ##
## Value: on | off ## Value: on | off
enable_acl_cache = on acl.enable_acl_cache = on
## The maximum count of ACL entries can be cached for a client. ## The maximum count of ACL entries can be cached for a client.
## ##
## Value: Integer greater than 0 ## Value: Integer greater than 0
## Default: 32 ## Default: 32
acl_cache_max_size = 32 acl.acl_cache_max_size = 32
## The time after which an ACL cache entry will be deleted ## The time after which an ACL cache entry will be deleted
## ##
## Value: Duration ## Value: Duration
## Default: 1 minute ## Default: 1 minute
acl_cache_ttl = 1m acl.acl_cache_ttl = 1m
## The action when acl check reject current operation ## The action when acl check reject current operation
## ##
## Value: ignore | disconnect ## Value: ignore | disconnect
## Default: ignore ## Default: ignore
acl_deny_action = ignore acl.acl_deny_action = ignore
## Specify the global flapping detect policy. ## Specify the global flapping detect policy.
## The value is a string composed of flapping threshold, duration and banned interval. ## The value is a string composed of flapping threshold, duration and banned interval.
@ -635,7 +649,7 @@ acl_deny_action = ignore
## 3. banned interval: the banned interval if a flapping is detected. ## 3. banned interval: the banned interval if a flapping is detected.
## ##
## Value: Integer,Duration,Duration ## Value: Integer,Duration,Duration
flapping_detect_policy = 30, 1m, 5m acl.flapping_detect_policy = "30, 1m, 5m"
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## MQTT Protocol ## MQTT Protocol
@ -732,7 +746,7 @@ zone.external.acl_deny_action = ignore
## messages | bytes passed through. ## messages | bytes passed through.
## ##
## Numbers delimited by `|'. Zero or negative is to disable. ## Numbers delimited by `|'. Zero or negative is to disable.
zone.external.force_gc_policy = 16000|16MB zone.external.force_gc_policy = "16000|16MB"
## Max message queue length and total heap size to force shutdown ## Max message queue length and total heap size to force shutdown
## connection/session process. ## connection/session process.
@ -742,9 +756,9 @@ zone.external.force_gc_policy = 16000|16MB
## Numbers delimited by `|'. Zero or negative is to disable. ## Numbers delimited by `|'. Zero or negative is to disable.
## ##
## Default: ## Default:
## - 10000|64MB on ARCH_64 system ## - "10000|64MB" on ARCH_64 system
## - 1000|32MB on ARCH_32 sytem ## - "1000|32MB" on ARCH_32 sytem
#zone.external.force_shutdown_policy = 10000|64MB #zone.external.force_shutdown_policy = "10000|64MB"
## Maximum MQTT packet size allowed. ## Maximum MQTT packet size allowed.
## ##
@ -850,7 +864,7 @@ zone.external.max_mqueue_len = 1000
## are treated equal ## are treated equal
## ##
## Priority number [1-255] ## Priority number [1-255]
## Example: topic/1=10,topic/2=8 ## Example: "topic/1=10,topic/2=8"
## NOTE: comma and equal signs are not allowed for priority topic names ## NOTE: comma and equal signs are not allowed for priority topic names
## NOTE: messages for topics not in the priority table are treated as ## NOTE: messages for topics not in the priority table are treated as
## either highest or lowest priority depending on the configured ## either highest or lowest priority depending on the configured
@ -877,13 +891,13 @@ zone.external.enable_flapping_detect = off
## ##
## Value: Number,Duration ## Value: Number,Duration
## Example: 100 messages per 10 seconds. ## Example: 100 messages per 10 seconds.
#zone.external.rate_limit.conn_messages_in = 100,10s #zone.external.rate_limit.conn_messages_in = "100,10s"
## Bytes limit for a external MQTT connections. ## Bytes limit for a external MQTT connections.
## ##
## Value: Number,Duration ## Value: Number,Duration
## Example: 100KB incoming per 10 seconds. ## Example: 100KB incoming per 10 seconds.
#zone.external.rate_limit.conn_bytes_in = 100KB,10s #zone.external.rate_limit.conn_bytes_in = "100KB,10s"
## Whether to alarm the congested connections. ## Whether to alarm the congested connections.
## ##
@ -914,16 +928,16 @@ zone.external.enable_flapping_detect = off
## ##
## Value: Number, Duration ## Value: Number, Duration
## ##
## Example: 100 messaegs per 1s ## Example: 100 messages per 1s
#zone.external.quota.conn_messages_routing = 100,1s #zone.external.quota.conn_messages_routing = "100,1s"
## Messages quota for the all of external MQTT connections. ## Messages quota for the all of external MQTT connections.
## This value consumed by the number of recipient on a message. ## This value consumed by the number of recipient on a message.
## ##
## Value: Number, Duration ## Value: Number, Duration
## ##
## Example: 200000 messaegs per 1s ## Example: 200000 messages per 1s
#zone.external.quota.overall_messages_routing = 200000,1s #zone.external.quota.overall_messages_routing = "200000,1s"
## All the topics will be prefixed with the mountpoint path if this option is enabled. ## All the topics will be prefixed with the mountpoint path if this option is enabled.
## ##
@ -932,7 +946,7 @@ zone.external.enable_flapping_detect = off
## - %u: username ## - %u: username
## ##
## Value: String ## Value: String
## zone.external.mountpoint = devicebound/ ## zone.external.mountpoint = "devicebound/"
## Whether use username replace client id ## Whether use username replace client id
## ##
@ -977,7 +991,7 @@ zone.internal.enable_acl = off
zone.internal.acl_deny_action = ignore zone.internal.acl_deny_action = ignore
## See zone.$name.force_gc_policy ## See zone.$name.force_gc_policy
## zone.internal.force_gc_policy = 128000|128MB ## zone.internal.force_gc_policy = "128000|128MB"
## See zone.$name.wildcard_subscription. ## See zone.$name.wildcard_subscription.
## ##
@ -1022,8 +1036,8 @@ zone.internal.enable_flapping_detect = off
## See zone.$name.force_shutdown_policy ## See zone.$name.force_shutdown_policy
## ##
## Default: ## Default:
## - 10000|64MB on ARCH_64 system ## - "10000|64MB" on ARCH_64 system
## - 1000|32MB on ARCH_32 sytem ## - "1000|32MB" on ARCH_32 sytem
#zone.internal.force_shutdown_policy = 10000|64MB #zone.internal.force_shutdown_policy = 10000|64MB
## All the topics will be prefixed with the mountpoint path if this option is enabled. ## All the topics will be prefixed with the mountpoint path if this option is enabled.
@ -1033,7 +1047,7 @@ zone.internal.enable_flapping_detect = off
## - %u: username ## - %u: username
## ##
## Value: String ## Value: String
## zone.internal.mountpoint = cloudbound/ ## zone.internal.mountpoint = "cloudbound/"
## Whether to ignore loop delivery of messages.(for mqtt v3.1.1) ## Whether to ignore loop delivery of messages.(for mqtt v3.1.1)
## ##
@ -1067,8 +1081,8 @@ zone.internal.bypass_auth_plugins = true
## ##
## Value: IP:Port | Port ## Value: IP:Port | Port
## ##
## Examples: 1883, 127.0.0.1:1883, ::1:1883 ## Examples: 1883, "127.0.0.1:1883", "::1:1883"
listener.tcp.external = 0.0.0.0:1883 listener.tcp.external.endpoint = "0.0.0.0:1883"
## The acceptor pool for external MQTT/TCP listener. ## The acceptor pool for external MQTT/TCP listener.
## ##
@ -1103,8 +1117,8 @@ listener.tcp.external.zone = external
## ##
## Value: ACL Rule ## Value: ACL Rule
## ##
## Example: allow 192.168.0.0/24 ## Example: "allow 192.168.0.0/24"
listener.tcp.external.access.1 = allow all listener.tcp.external.access.1 = "allow all"
## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed ## Enable the Proxy Protocol V1/2 if the EMQ X cluster is deployed
## behind HAProxy or Nginx. ## behind HAProxy or Nginx.
@ -1207,8 +1221,8 @@ listener.tcp.external.reuseaddr = true
## ##
## Value: IP:Port, Port ## Value: IP:Port, Port
## ##
## Examples: 11883, 127.0.0.1:11883, ::1:11883 ## Examples: 11883, "127.0.0.1:11883", "::1:11883"
listener.tcp.internal = 127.0.0.1:11883 listener.tcp.internal.endpoint = "127.0.0.1:11883"
## The acceptor pool for internal MQTT/TCP listener. ## The acceptor pool for internal MQTT/TCP listener.
## ##
@ -1304,8 +1318,8 @@ listener.tcp.internal.reuseaddr = true
## ##
## Value: IP:Port | Port ## Value: IP:Port | Port
## ##
## Examples: 8883, 127.0.0.1:8883, ::1:8883 ## Examples: 8883, "127.0.0.1:8883", "::1:8883"
listener.ssl.external = 8883 listener.ssl.external.endpoint = 8883
## The acceptor pool for external MQTT/SSL listener. ## The acceptor pool for external MQTT/SSL listener.
## ##
@ -1337,7 +1351,7 @@ listener.ssl.external.zone = external
## See: listener.tcp.$name.access ## See: listener.tcp.$name.access
## ##
## Value: ACL Rule ## Value: ACL Rule
listener.ssl.external.access.1 = allow all listener.ssl.external.access.1 = "allow all"
## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind ## Enable the Proxy Protocol V1/2 if the EMQ cluster is deployed behind
## HAProxy or Nginx. ## HAProxy or Nginx.
@ -1360,7 +1374,7 @@ listener.ssl.external.access.1 = allow all
## ##
## Value: String, seperated by ',' ## Value: String, seperated by ','
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier ## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
## listener.ssl.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 ## listener.ssl.external.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## TLS Handshake timeout. ## TLS Handshake timeout.
## ##
@ -1384,20 +1398,20 @@ listener.ssl.external.handshake_timeout = 15s
## See: http://erlang.org/doc/man/ssl.html ## See: http://erlang.org/doc/man/ssl.html
## ##
## Value: File ## Value: File
listener.ssl.external.keyfile = {{ platform_etc_dir }}/certs/key.pem listener.ssl.external.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
## Path to a file containing the user certificate. ## Path to a file containing the user certificate.
## ##
## See: http://erlang.org/doc/man/ssl.html ## See: http://erlang.org/doc/man/ssl.html
## ##
## Value: File ## Value: File
listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem listener.ssl.external.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
## Path to the file containing PEM-encoded CA certificates. The CA certificates ## Path to the file containing PEM-encoded CA certificates. The CA certificates
## are used during server authentication and when building the client certificate chain. ## are used during server authentication and when building the client certificate chain.
## ##
## Value: File ## Value: File
## listener.ssl.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem ## listener.ssl.external.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
## The Ephemeral Diffie-Helman key exchange is a very effective way of ## The Ephemeral Diffie-Helman key exchange is a very effective way of
## ensuring Forward Secrecy by exchanging a set of keys that never hit ## ensuring Forward Secrecy by exchanging a set of keys that never hit
@ -1414,7 +1428,7 @@ listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
## openssl dhparam -out dh-params.pem 2048 ## openssl dhparam -out dh-params.pem 2048
## ##
## Value: File ## Value: File
## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem ## listener.ssl.external.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
## A server only does x509-path validation in mode verify_peer, ## A server only does x509-path validation in mode verify_peer,
## as it then sends a certificate request to the client (this ## as it then sends a certificate request to the client (this
@ -1449,14 +1463,13 @@ listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
## Most of it was copied from Mozillas Server Side TLS article ## Most of it was copied from Mozillas Server Side TLS article
## ##
## Value: Ciphers ## Value: Ciphers
listener.ssl.external.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA listener.ssl.external.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## Ciphers for TLS PSK. ## Ciphers for TLS PSK.
## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot ## Note that 'listener.ssl.external.ciphers' and 'listener.ssl.external.psk_ciphers' cannot
## be configured at the same time. ## be configured at the same time.
## See 'https://tools.ietf.org/html/rfc4279#section-2'. ## See 'https://tools.ietf.org/html/rfc4279#section-2'.
#listener.ssl.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA #listener.ssl.external.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
## SSL parameter renegotiation is a feature that allows a client and a server ## SSL parameter renegotiation is a feature that allows a client and a server
## to renegotiate the parameters of the SSL connection on the fly. ## to renegotiate the parameters of the SSL connection on the fly.
@ -1566,13 +1579,13 @@ listener.ssl.external.reuseaddr = true
## ##
## Value: IP:Port | Port ## Value: IP:Port | Port
## ##
## Examples: 8083, 127.0.0.1:8083, ::1:8083 ## Examples: 8083, "127.0.0.1:8083", "::1:8083"
listener.ws.external = 8083 listener.ws.external.endpoint = 8083
## The path of WebSocket MQTT endpoint ## The path of WebSocket MQTT endpoint
## ##
## Value: URL Path ## Value: URL Path
listener.ws.external.mqtt_path = /mqtt listener.ws.external.mqtt_path = "/mqtt"
## The acceptor pool for external MQTT/WebSocket listener. ## The acceptor pool for external MQTT/WebSocket listener.
## ##
@ -1604,7 +1617,7 @@ listener.ws.external.zone = external
## See: listener.ws.$name.access ## See: listener.ws.$name.access
## ##
## Value: ACL Rule ## Value: ACL Rule
listener.ws.external.access.1 = allow all listener.ws.external.access.1 = "allow all"
## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send. ## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send.
## Set to false for WeChat MiniApp. ## Set to false for WeChat MiniApp.
@ -1615,7 +1628,7 @@ listener.ws.external.access.1 = allow all
## Supported subprotocols ## Supported subprotocols
## ##
## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 ## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
## listener.ws.external.supported_subprotocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 ## listener.ws.external.supported_subprotocols = "mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5"
## Specify which HTTP header for real source IP if the EMQ X cluster is ## Specify which HTTP header for real source IP if the EMQ X cluster is
## deployed behind NGINX or HAProxy. ## deployed behind NGINX or HAProxy.
@ -1823,7 +1836,7 @@ listener.ws.external.allow_origin_absence = true
## Comma separated list of allowed origin in header for websocket connection ## Comma separated list of allowed origin in header for websocket connection
## ##
## Value: http://url eg. local http dashboard url - http://localhost:18083, http://127.0.0.1:18083 ## Value: http://url eg. local http dashboard url - http://localhost:18083, http://127.0.0.1:18083
listener.ws.external.check_origins = http://localhost:18083, http://127.0.0.1:18083 listener.ws.external.check_origins = "http://localhost:18083, http://127.0.0.1:18083"
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## External WebSocket/SSL listener for MQTT Protocol ## External WebSocket/SSL listener for MQTT Protocol
@ -1833,13 +1846,13 @@ listener.ws.external.check_origins = http://localhost:18083, http://127.0.0.1:18
## ##
## Value: IP:Port | Port ## Value: IP:Port | Port
## ##
## Examples: 8084, 127.0.0.1:8084, ::1:8084 ## Examples: 8084, "127.0.0.1:8084", "::1:8084"
listener.wss.external = 8084 listener.wss.external.endpoint = 8084
## The path of WebSocket MQTT endpoint ## The path of WebSocket MQTT endpoint
## ##
## Value: URL Path ## Value: URL Path
listener.wss.external.mqtt_path = /mqtt listener.wss.external.mqtt_path = "/mqtt"
## The acceptor pool for external MQTT/WebSocket/SSL listener. ## The acceptor pool for external MQTT/WebSocket/SSL listener.
## ##
@ -1873,7 +1886,7 @@ listener.wss.external.zone = external
## See: listener.tcp.$name.access.<no> ## See: listener.tcp.$name.access.<no>
## ##
## Value: ACL Rule ## Value: ACL Rule
listener.wss.external.access.1 = allow all listener.wss.external.access.1 = "allow all"
## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send. ## If set to true, the server fails if the client does not have a Sec-WebSocket-Protocol to send.
## Set to false for WeChat MiniApp. ## Set to false for WeChat MiniApp.
@ -1884,7 +1897,7 @@ listener.wss.external.access.1 = allow all
## Supported subprotocols ## Supported subprotocols
## ##
## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 ## Default: mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5
## listener.wss.external.supported_subprotocols = mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5 ## listener.wss.external.supported_subprotocols = "mqtt, mqtt-v3, mqtt-v3.1.1, mqtt-v5"
## Specify which HTTP header for real source IP if the EMQ X cluster is ## Specify which HTTP header for real source IP if the EMQ X cluster is
## deployed behind NGINX or HAProxy. ## deployed behind NGINX or HAProxy.
@ -1918,28 +1931,28 @@ listener.wss.external.access.1 = allow all
## ##
## Value: String, seperated by ',' ## Value: String, seperated by ','
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier ## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
## listener.wss.external.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 ## listener.wss.external.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## Path to the file containing the user's private PEM-encoded key. ## Path to the file containing the user's private PEM-encoded key.
## ##
## See: listener.ssl.$name.keyfile ## See: listener.ssl.$name.keyfile
## ##
## Value: File ## Value: File
listener.wss.external.keyfile = {{ platform_etc_dir }}/certs/key.pem listener.wss.external.keyfile = "{{ platform_etc_dir }}/certs/key.pem"
## Path to a file containing the user certificate. ## Path to a file containing the user certificate.
## ##
## See: listener.ssl.$name.certfile ## See: listener.ssl.$name.certfile
## ##
## Value: File ## Value: File
listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem listener.wss.external.certfile = "{{ platform_etc_dir }}/certs/cert.pem"
## Path to the file containing PEM-encoded CA certificates. ## Path to the file containing PEM-encoded CA certificates.
## ##
## See: listener.ssl.$name.cacert ## See: listener.ssl.$name.cacert
## ##
## Value: File ## Value: File
## listener.wss.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem ## listener.wss.external.cacertfile = "{{ platform_etc_dir }}/certs/cacert.pem"
## Maximum number of non-self-issued intermediate certificates that ## Maximum number of non-self-issued intermediate certificates that
## can follow the peer certificate in a valid certification path. ## can follow the peer certificate in a valid certification path.
@ -1960,7 +1973,7 @@ listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
## See: listener.ssl.$name.dhfile ## See: listener.ssl.$name.dhfile
## ##
## Value: File ## Value: File
## listener.ssl.external.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem ## listener.ssl.external.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
## See: listener.ssl.$name.verify ## See: listener.ssl.$name.verify
## ##
@ -1975,13 +1988,13 @@ listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
## See: listener.ssl.$name.ciphers ## See: listener.ssl.$name.ciphers
## ##
## Value: Ciphers ## Value: Ciphers
listener.wss.external.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA listener.wss.external.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## Ciphers for TLS PSK. ## Ciphers for TLS PSK.
## Note that 'listener.wss.external.ciphers' and 'listener.wss.external.psk_ciphers' cannot ## Note that 'listener.wss.external.ciphers' and 'listener.wss.external.psk_ciphers' cannot
## be configured at the same time. ## be configured at the same time.
## See 'https://tools.ietf.org/html/rfc4279#section-2'. ## See 'https://tools.ietf.org/html/rfc4279#section-2'.
## listener.wss.external.psk_ciphers = PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA ## listener.wss.external.psk_ciphers = "PSK-AES128-CBC-SHA,PSK-AES256-CBC-SHA,PSK-3DES-EDE-CBC-SHA,PSK-RC4-SHA"
## See: listener.ssl.$name.secure_renegotiate ## See: listener.ssl.$name.secure_renegotiate
## ##
@ -2140,7 +2153,7 @@ listener.wss.external.allow_origin_absence = true
## Comma separated list of allowed origin in header for secure websocket connection ## Comma separated list of allowed origin in header for secure websocket connection
## ##
## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084 ## Value: http://url eg. https://localhost:8084, https://127.0.0.1:8084
listener.wss.external.check_origins = https://localhost:8084, https://127.0.0.1:8084 listener.wss.external.check_origins = "https://localhost:8084, https://127.0.0.1:8084"
## CONFIG_SECTION_END=listeners ================================================ ## CONFIG_SECTION_END=listeners ================================================
@ -2149,7 +2162,7 @@ listener.wss.external.check_origins = https://localhost:8084, https://127.0.0.1:
## The file to store loaded module names. ## The file to store loaded module names.
## ##
## Value: File ## Value: File
modules.loaded_file = {{ platform_data_dir }}/loaded_modules module.loaded_file = "{{ platform_data_dir }}/loaded_modules"
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Presence Module ## Presence Module
@ -2165,7 +2178,7 @@ module.presence.qos = 1
## Subscribe the Topics automatically when client connected. ## Subscribe the Topics automatically when client connected.
## ##
## Value: String ## Value: String
## module.subscription.1.topic = connected/%c/%u ## module.subscription.1.topic = "connected/%c/%u"
## Qos of the proxy subscription. ## Qos of the proxy subscription.
## ##
@ -2198,8 +2211,8 @@ module.presence.qos = 1
## Rewrite Module ## Rewrite Module
## {rewrite, Topic, Re, Dest} ## {rewrite, Topic, Re, Dest}
## module.rewrite.pub.rule.1 = x/# ^x/y/(.+)$ z/y/$1 ## module.rewrite.pub_rule.1 = "x/# ^x/y/(.+)$ z/y/$1"
## module.rewrite.sub.rule.1 = y/+/z/# ^y/(.+)/z/(.+)$ y/z/$2 ## module.rewrite.sub_rule.1 = "y/+/z/# ^y/(.+)/z/(.+)$ y/z/$2"
## CONFIG_SECTION_END=modules ================================================== ## CONFIG_SECTION_END=modules ==================================================
@ -2210,17 +2223,17 @@ module.presence.qos = 1
## The etc dir for plugins' config. ## The etc dir for plugins' config.
## ##
## Value: Folder ## Value: Folder
plugins.etc_dir = {{ platform_etc_dir }}/plugins/ plugins.etc_dir = "{{ platform_etc_dir }}/plugins/"
## The file to store loaded plugin names. ## The file to store loaded plugin names.
## ##
## Value: File ## Value: File
plugins.loaded_file = {{ platform_data_dir }}/loaded_plugins plugins.loaded_file = "{{ platform_data_dir }}/loaded_plugins"
## The directory of extension plugins. ## The directory of extension plugins.
## ##
## Value: File ## Value: File
plugins.expand_plugins_dir = {{ platform_plugins_dir }}/ plugins.expand_plugins_dir = "{{ platform_plugins_dir }}/"
##-------------------------------------------------------------------- ##--------------------------------------------------------------------
## Broker ## Broker
@ -2334,7 +2347,6 @@ sysmon.long_gc = 0
## Examples: ## Examples:
## - 2h: 2 hours ## - 2h: 2 hours
## - 30m: 30 minutes ## - 30m: 30 minutes
## - 0.1s: 0.1 seconds
## - 100ms: 100 milliseconds ## - 100ms: 100 milliseconds
## ##
## Default: 0ms ## Default: 0ms
@ -2426,8 +2438,8 @@ vm_mon.process_low_watermark = 60%
## - log ## - log
## - publish ## - publish
## ##
## Default: log,publish ## Default: "log,publish"
alarm.actions = log,publish alarm.actions = "log,publish"
## The maximum number of deactivated alarms ## The maximum number of deactivated alarms
## ##

View File

@ -29,7 +29,7 @@
-ifndef(EMQX_ENTERPRISE). -ifndef(EMQX_ENTERPRISE).
-define(EMQX_RELEASE, {opensource, "4.3.3"}). -define(EMQX_RELEASE, {opensource, "5.0-pre"}).
-else. -else.

View File

@ -20,7 +20,7 @@ dashboard.default_user.password = public
## Value: Port ## Value: Port
## ##
## Examples: 18083 ## Examples: 18083
dashboard.listener.http = 18083 dashboard.listener.http.port = 18083
## The acceptor pool for external Dashboard HTTP listener. ## The acceptor pool for external Dashboard HTTP listener.
## ##
@ -50,7 +50,7 @@ dashboard.listener.http.ipv6_v6only = false
## Value: Port ## Value: Port
## ##
## Examples: 18084 ## Examples: 18084
## dashboard.listener.https = 18084 ## dashboard.listener.https.port = 18084
## The acceptor pool for external Dashboard HTTPS listener. ## The acceptor pool for external Dashboard HTTPS listener.
## ##
@ -75,22 +75,22 @@ dashboard.listener.http.ipv6_v6only = false
## Path to the file containing the user's private PEM-encoded key. ## Path to the file containing the user's private PEM-encoded key.
## ##
## Value: File ## Value: File
## dashboard.listener.https.keyfile = etc/certs/key.pem ## dashboard.listener.https.keyfile = "etc/certs/key.pem"
## Path to a file containing the user certificate. ## Path to a file containing the user certificate.
## ##
## Value: File ## Value: File
## dashboard.listener.https.certfile = etc/certs/cert.pem ## dashboard.listener.https.certfile = "etc/certs/cert.pem"
## Path to the file containing PEM-encoded CA certificates. ## Path to the file containing PEM-encoded CA certificates.
## ##
## Value: File ## Value: File
## dashboard.listener.https.cacertfile = etc/certs/cacert.pem ## dashboard.listener.https.cacertfile = "etc/certs/cacert.pem"
## See: 'listener.ssl.<name>.dhfile' in emq.conf ## See: 'listener.ssl.<name>.dhfile' in emq.conf
## ##
## Value: File ## Value: File
## dashboard.listener.https.dhfile = {{ platform_etc_dir }}/certs/dh-params.pem ## dashboard.listener.https.dhfile = "{{ platform_etc_dir }}/certs/dh-params.pem"
## See: 'listener.ssl.<name>.verify' in emq.conf ## See: 'listener.ssl.<name>.verify' in emq.conf
## ##
@ -106,12 +106,12 @@ dashboard.listener.http.ipv6_v6only = false
## ##
## Value: String, seperated by ',' ## Value: String, seperated by ','
## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier ## NOTE: Do not use tlsv1.3 if emqx is running on OTP-22 or earlier
## dashboard.listener.https.tls_versions = tlsv1.3,tlsv1.2,tlsv1.1,tlsv1 ## dashboard.listener.https.tls_versions = "tlsv1.3,tlsv1.2,tlsv1.1,tlsv1"
## See: 'listener.ssl.<name>.ciphers' in emq.conf ## See: 'listener.ssl.<name>.ciphers' in emq.conf
## ##
## Value: Ciphers ## Value: Ciphers
## dashboard.listener.https.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA ## dashboard.listener.https.ciphers = "TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_CHACHA20_POLY1305_SHA256,TLS_AES_128_CCM_SHA256,TLS_AES_128_CCM_8_SHA256,ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES256-SHA384,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-DES-CBC3-SHA,ECDH-ECDSA-AES256-GCM-SHA384,ECDH-RSA-AES256-GCM-SHA384,ECDH-ECDSA-AES256-SHA384,ECDH-RSA-AES256-SHA384,DHE-DSS-AES256-GCM-SHA384,DHE-DSS-AES256-SHA256,AES256-GCM-SHA384,AES256-SHA256,ECDHE-ECDSA-AES128-GCM-SHA256,ECDHE-RSA-AES128-GCM-SHA256,ECDHE-ECDSA-AES128-SHA256,ECDHE-RSA-AES128-SHA256,ECDH-ECDSA-AES128-GCM-SHA256,ECDH-RSA-AES128-GCM-SHA256,ECDH-ECDSA-AES128-SHA256,ECDH-RSA-AES128-SHA256,DHE-DSS-AES128-GCM-SHA256,DHE-DSS-AES128-SHA256,AES128-GCM-SHA256,AES128-SHA256,ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-DSS-AES256-SHA,ECDH-ECDSA-AES256-SHA,ECDH-RSA-AES256-SHA,AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-DSS-AES128-SHA,ECDH-ECDSA-AES128-SHA,ECDH-RSA-AES128-SHA,AES128-SHA"
## See: 'listener.ssl.<name>.secure_renegotiate' in emq.conf ## See: 'listener.ssl.<name>.secure_renegotiate' in emq.conf
## ##

View File

@ -10,7 +10,7 @@
{override_env, "ADMIN_PASSWORD"} {override_env, "ADMIN_PASSWORD"}
]}. ]}.
{mapping, "dashboard.listener.http", "emqx_dashboard.listeners", [ {mapping, "dashboard.listener.http.port", "emqx_dashboard.listeners", [
{datatype, integer} {datatype, integer}
]}. ]}.
@ -38,7 +38,7 @@
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{mapping, "dashboard.listener.https", "emqx_dashboard.listeners", [ {mapping, "dashboard.listener.https.port", "emqx_dashboard.listeners", [
{datatype, integer} {datatype, integer}
]}. ]}.
@ -139,7 +139,7 @@
lists:map( lists:map(
fun(Proto) -> fun(Proto) ->
Prefix = "dashboard.listener." ++ atom_to_list(Proto), Prefix = "dashboard.listener." ++ atom_to_list(Proto),
case cuttlefish:conf_get(Prefix, Conf, undefined) of case cuttlefish:conf_get(Prefix ++ ".port", Conf, undefined) of
undefined -> []; undefined -> [];
Port -> Port ->
[{Proto, Port, case Proto of [{Proto, Port, case Proto of

View File

@ -1,6 +1,6 @@
{application, emqx_dashboard, {application, emqx_dashboard,
[{description, "EMQ X Web Dashboard"}, [{description, "EMQ X Web Dashboard"},
{vsn, "4.3.0"}, % strict semver, bump manually! {vsn, "4.4.0"}, % strict semver, bump manually!
{modules, []}, {modules, []},
{registered, [emqx_dashboard_sup]}, {registered, [emqx_dashboard_sup]},
{applications, [kernel,stdlib,mnesia,minirest]}, {applications, [kernel,stdlib,mnesia,minirest]},

View File

@ -13,8 +13,8 @@ telemetry.enabled = true
## ##
## Value: String ## Value: String
## ##
## Default: https://telemetry.emqx.io/api/telemetry ## Default: "https://telemetry.emqx.io/api/telemetry"
telemetry.url = https://telemetry.emqx.io/api/telemetry telemetry.url = "https://telemetry.emqx.io/api/telemetry"
## Interval for reporting telemetry data ## Interval for reporting telemetry data
## ##
@ -25,4 +25,4 @@ telemetry.url = https://telemetry.emqx.io/api/telemetry
## -s: second ## -s: second
## ##
## Default: 7d ## Default: 7d
telemetry.report_interval = 7d telemetry.report_interval = 7d

View File

@ -103,6 +103,10 @@
{datatype, string} {datatype, string}
]}. ]}.
{mapping, "cluster.etcd.version", "ekka.cluster_discovery", [
{datatype, {enum, [v2, v3]}}
]}.
{mapping, "cluster.etcd.prefix", "ekka.cluster_discovery", [ {mapping, "cluster.etcd.prefix", "ekka.cluster_discovery", [
{datatype, string} {datatype, string}
]}. ]}.
@ -180,6 +184,7 @@
end, Options) end, Options)
end, end,
[{server, string:tokens(cuttlefish:conf_get("cluster.etcd.server", Conf), ",")}, [{server, string:tokens(cuttlefish:conf_get("cluster.etcd.server", Conf), ",")},
{version, cuttlefish:conf_get("cluster.etcd.version", Conf, v3)},
{prefix, cuttlefish:conf_get("cluster.etcd.prefix", Conf, "emqcl")}, {prefix, cuttlefish:conf_get("cluster.etcd.prefix", Conf, "emqcl")},
{node_ttl, cuttlefish:conf_get("cluster.etcd.node_ttl", Conf, 60)}, {node_ttl, cuttlefish:conf_get("cluster.etcd.node_ttl", Conf, 60)},
{ssl_options, SslOpts(Conf)}]; {ssl_options, SslOpts(Conf)}];
@ -467,6 +472,15 @@ end}.
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, all]}} {datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, all]}}
]}. ]}.
%% @doc Timezone offset to display in logs,
%% "system" use system time zone
%% "utc" for Universal Coordinated Time (UTC)
%% "+hh:mm" or "-hh:mm" for a specified offset
{mapping, "log.time_offset", "kernel.logger", [
{default, "system"},
{datatype, string}
]}.
{mapping, "log.primary_log_level", "kernel.logger_level", [ {mapping, "log.primary_log_level", "kernel.logger_level", [
{default, warning}, {default, warning},
{datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, all]}} {datatype, {enum, [debug, info, notice, warning, error, critical, alert, emergency, all]}}
@ -512,7 +526,7 @@ end}.
{datatype, {enum, [true, false]}} {datatype, {enum, [true, false]}}
]}. ]}.
{mapping, "log.rotation", "kernel.logger", [ {mapping, "log.rotation.enable", "kernel.logger", [
{default, on}, {default, on},
{datatype, flag} {datatype, flag}
]}. ]}.
@ -600,7 +614,27 @@ end}.
{translation, "kernel.logger", fun(Conf) -> {translation, "kernel.logger", fun(Conf) ->
LogTo = cuttlefish:conf_get("log.to", Conf), LogTo = cuttlefish:conf_get("log.to", Conf),
LogLevel = cuttlefish:conf_get("log.level", Conf), LogLevel = cuttlefish:conf_get("log.level", Conf),
LogType = case cuttlefish:conf_get("log.rotation", Conf) of LogTimeoffset =
case cuttlefish:conf_get("log.time_offset", Conf) of
"system" -> "";
"utc" -> "0";
[S, H1, H2, $:, M1, M2] = HHMM ->
(S =:= $+ orelse S =:= $-) andalso
try
begin
H = list_to_integer([H1, H2]),
M = list_to_integer([M1, M2]),
H >=0 andalso H =< 14 andalso
M >= 0 andalso M =< 59
end
catch
_ : _ ->
error({"invalid_log_time_offset", HHMM})
end andalso HHMM;
Other ->
error({"invalid_log_time_offset", Other})
end,
LogType = case cuttlefish:conf_get("log.rotation.enable", Conf) of
true -> wrap; true -> wrap;
false -> halt false -> halt
end, end,
@ -1229,7 +1263,7 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% TCP Listeners %% TCP Listeners
{mapping, "listener.tcp.$name", "emqx.listeners", [ {mapping, "listener.tcp.$name.endpoint", "emqx.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
@ -1336,7 +1370,7 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% SSL Listeners %% SSL Listeners
{mapping, "listener.ssl.$name", "emqx.listeners", [ {mapping, "listener.ssl.$name.endpoint", "emqx.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
@ -1504,7 +1538,7 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% MQTT/WebSocket Listeners %% MQTT/WebSocket Listeners
{mapping, "listener.ws.$name", "emqx.listeners", [ {mapping, "listener.ws.$name.endpoint", "emqx.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
@ -1698,7 +1732,7 @@ end}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% MQTT/WebSocket/SSL Listeners %% MQTT/WebSocket/SSL Listeners
{mapping, "listener.wss.$name", "emqx.listeners", [ {mapping, "listener.wss.$name.endpoint", "emqx.listeners", [
{datatype, [integer, ip]} {datatype, [integer, ip]}
]}. ]}.
@ -1954,7 +1988,6 @@ end}.
]}. ]}.
{translation, "emqx.listeners", fun(Conf) -> {translation, "emqx.listeners", fun(Conf) ->
Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end, Filter = fun(Opts) -> [{K, V} || {K, V} <- Opts, V =/= undefined] end,
Atom = fun(undefined) -> undefined; (S) -> list_to_atom(S) end, Atom = fun(undefined) -> undefined; (S) -> list_to_atom(S) end,
@ -2103,7 +2136,7 @@ end}.
TcpListeners = fun(Type, Name) -> TcpListeners = fun(Type, Name) ->
Prefix = string:join(["listener", Type, Name], "."), Prefix = string:join(["listener", Type, Name], "."),
ListenOnN = case cuttlefish:conf_get(Prefix, Conf, undefined) of ListenOnN = case cuttlefish:conf_get(Prefix ++ ".endpoint", Conf, undefined) of
undefined -> []; undefined -> [];
ListenOn -> Listen_fix(ListenOn) ListenOn -> Listen_fix(ListenOn)
end, end,
@ -2119,7 +2152,7 @@ end}.
end, end,
SslListeners = fun(Type, Name) -> SslListeners = fun(Type, Name) ->
Prefix = string:join(["listener", Type, Name], "."), Prefix = string:join(["listener", Type, Name], "."),
case cuttlefish:conf_get(Prefix, Conf, undefined) of case cuttlefish:conf_get(Prefix ++ ".endpoint", Conf, undefined) of
undefined -> undefined ->
[]; [];
ListenOn -> ListenOn ->
@ -2135,12 +2168,11 @@ end}.
] ]
end end
end, end,
lists:flatten([TcpListeners(Type, Name) || {["listener", Type, Name, "endpoint"], ListenOn}
lists:flatten([TcpListeners(Type, Name) || {["listener", Type, Name], ListenOn}
<- cuttlefish_variable:filter_by_prefix("listener.tcp", Conf) <- cuttlefish_variable:filter_by_prefix("listener.tcp", Conf)
++ cuttlefish_variable:filter_by_prefix("listener.ws", Conf)] ++ cuttlefish_variable:filter_by_prefix("listener.ws", Conf)]
++ ++
[SslListeners(Type, Name) || {["listener", Type, Name], ListenOn} [SslListeners(Type, Name) || {["listener", Type, Name, "endpoint"], ListenOn}
<- cuttlefish_variable:filter_by_prefix("listener.ssl", Conf) <- cuttlefish_variable:filter_by_prefix("listener.ssl", Conf)
++ cuttlefish_variable:filter_by_prefix("listener.wss", Conf)]) ++ cuttlefish_variable:filter_by_prefix("listener.wss", Conf)])
end}. end}.

View File

@ -43,9 +43,9 @@
, {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}} , {jiffy, {git, "https://github.com/emqx/jiffy", {tag, "1.0.5"}}}
, {cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.8.2"}}} , {cowboy, {git, "https://github.com/emqx/cowboy", {tag, "2.8.2"}}}
, {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.8.0"}}} , {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.8.0"}}}
, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.8.1"}}} , {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.9.0"}}}
, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.1"}}} , {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.5.1"}}}
, {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v3.3.5"}}} , {cuttlefish, {git, "https://github.com/emqx/cuttlefish", {tag, "v4.0.1"}}} % TODO: delete when all apps moved to hocon
, {minirest, {git, "https://github.com/emqx/minirest", {tag, "0.3.5"}}} , {minirest, {git, "https://github.com/emqx/minirest", {tag, "0.3.5"}}}
, {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.1"}}} , {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.1"}}}
, {replayq, {git, "https://github.com/emqx/replayq", {tag, "0.3.2"}}} , {replayq, {git, "https://github.com/emqx/replayq", {tag, "0.3.2"}}}
@ -56,6 +56,7 @@
, {observer_cli, "1.6.1"} % NOTE: depends on recon 2.5.1 , {observer_cli, "1.6.1"} % NOTE: depends on recon 2.5.1
, {getopt, "1.0.1"} , {getopt, "1.0.1"}
, {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}} , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "0.13.0"}}}
, {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.4.0"}}}
]}. ]}.
{xref_ignores, {xref_ignores,

View File

@ -106,7 +106,7 @@ test_plugins() ->
test_deps() -> test_deps() ->
[ {bbmustache, "1.10.0"} [ {bbmustache, "1.10.0"}
, {emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {tag, "1.3.9"}}} , {emqx_ct_helpers, {git, "https://github.com/emqx/emqx-ct-helpers", {branch, "hocon"}}}
, meck , meck
]. ].
@ -188,6 +188,7 @@ overlay_vars_rel(RelType) ->
end, end,
[ {enable_plugin_emqx_rule_engine, RelType =:= cloud} [ {enable_plugin_emqx_rule_engine, RelType =:= cloud}
, {enable_plugin_emqx_bridge_mqtt, RelType =:= edge} , {enable_plugin_emqx_bridge_mqtt, RelType =:= edge}
, {enable_plugin_emqx_resource, true}
, {enable_plugin_emqx_modules, false} %% modules is not a plugin in ce , {enable_plugin_emqx_modules, false} %% modules is not a plugin in ce
, {enable_plugin_emqx_recon, true} , {enable_plugin_emqx_recon, true}
, {enable_plugin_emqx_retainer, true} , {enable_plugin_emqx_retainer, true}
@ -281,6 +282,7 @@ relx_plugin_apps(ReleaseType) ->
, emqx_auth_mnesia , emqx_auth_mnesia
, emqx_web_hook , emqx_web_hook
, emqx_recon , emqx_recon
, emqx_resource
, emqx_rule_engine , emqx_rule_engine
, emqx_sasl , emqx_sasl
] ]
@ -334,9 +336,8 @@ relx_overlay(ReleaseType) ->
, {template, "bin/emqx_ctl.cmd", "bin/emqx_ctl.cmd"} , {template, "bin/emqx_ctl.cmd", "bin/emqx_ctl.cmd"}
, {copy, "bin/nodetool", "bin/nodetool"} , {copy, "bin/nodetool", "bin/nodetool"}
, {copy, "bin/nodetool", "bin/nodetool-{{release_version}}"} , {copy, "bin/nodetool", "bin/nodetool-{{release_version}}"}
, {copy, "_build/default/lib/cuttlefish/cuttlefish", "bin/cuttlefish"} , {copy, "_build/default/lib/hocon/hocon", "bin/hocon"}
, {copy, "_build/default/lib/cuttlefish/cuttlefish", "bin/cuttlefish-{{release_version}}"} , {copy, "_build/default/lib/hocon/hocon", "bin/hocon-{{release_version}}"}
, {copy, "priv/emqx.schema", "releases/{{release_version}}/"}
] ++ case is_enterprise() of ] ++ case is_enterprise() of
true -> ee_etc_overlay(ReleaseType); true -> ee_etc_overlay(ReleaseType);
false -> etc_overlay(ReleaseType) false -> etc_overlay(ReleaseType)

View File

@ -6,22 +6,45 @@ latest_release=$(git describe --tags "$(git rev-list --tags --max-count=1 --remo
bad_app_count=0 bad_app_count=0
while read -r app; do get_vsn() {
if [ "$app" != "emqx" ]; then commit="$1"
app_path="$app" app_src_file="$2"
if [ "$commit" = 'HEAD' ]; then
if [ -f "$app_src_file" ]; then
grep vsn "$app_src_file" | grep -oE '"[0-9]+.[0-9]+.[0-9]+"' | tr -d '"' || true
fi
else else
app_path="." git show "$commit":"$app_src_file" 2>/dev/null | grep vsn | grep -oE '"[0-9]+.[0-9]+.[0-9]+"' | tr -d '"' || true
fi fi
src_file="$app_path/src/$(basename "$app").app.src" }
old_app_version="$(git show "$latest_release":"$src_file" | grep vsn | grep -oE '"[0-9]+.[0-9]+.[0-9]+"' | tr -d '"')"
now_app_version=$(grep -E 'vsn' "$src_file" | grep -oE '"[0-9]+\.[0-9]+\.[0-9]+"' | tr -d '"') while read -r app_path; do
if [ "$old_app_version" = "$now_app_version" ]; then app=$(basename "$app_path")
changed="$(git diff --name-only "$latest_release"...HEAD \ src_file="$app_path/src/$app.app.src"
old_app_version="$(get_vsn "$latest_release" "$src_file")"
## TODO: delete it after new version is released with emqx app in apps dir
if [ "$app" = 'emqx' ] && [ "$old_app_version" = '' ]; then
old_app_version="$(get_vsn "$latest_release" 'src/emqx.app.src')"
fi
now_app_version="$(get_vsn 'HEAD' "$src_file")"
## TODO: delete it after new version is released with emqx app in apps dir
if [ "$app" = 'emqx' ] && [ "$now_app_version" = '' ]; then
now_app_version="$(get_vsn 'HEAD' 'src/emqx.app.src')"
fi
if [ -z "$now_app_version" ]; then
echo "failed_to_get_new_app_vsn for $app"
exit 1
fi
if [ -z "${old_app_version:-}" ]; then
echo "skiped checking new app ${app}"
elif [ "$old_app_version" = "$now_app_version" ]; then
lines="$(git diff --name-only "$latest_release"...HEAD \
-- "$app_path/src" \ -- "$app_path/src" \
-- "$app_path/priv" \ -- "$app_path/priv" \
-- "$app_path/c_src" | wc -l)" -- "$app_path/c_src")"
if [ "$changed" -gt 0 ]; then if [ "$lines" != '' ]; then
echo "$src_file needs a vsn bump" echo "$src_file needs a vsn bump (old=$old_app_version)"
echo "changed: $lines"
bad_app_count=$(( bad_app_count + 1)) bad_app_count=$(( bad_app_count + 1))
fi fi
fi fi

View File

@ -32,6 +32,9 @@
-export([format/2]). -export([format/2]).
-ifdef(TEST). -ifdef(TEST).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
-export([report_cb_1/1, report_cb_2/2, report_cb_crash/2]). -export([report_cb_1/1, report_cb_2/2, report_cb_crash/2]).
-endif. -endif.
@ -220,8 +223,6 @@ json_key(Term) ->
end. end.
-ifdef(TEST). -ifdef(TEST).
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").
no_crash_test_() -> no_crash_test_() ->
Opts = [{numtests, 1000}, {to_file, user}], Opts = [{numtests, 1000}, {to_file, user}],

1230
src/emqx_schema.erl Normal file

File diff suppressed because it is too large Load Diff

View File

@ -65,16 +65,17 @@ mustache_vars() ->
]. ].
generate_config() -> generate_config() ->
Schema = cuttlefish_schema:files([local_path(["priv", "emqx.schema"])]),
ConfFile = render_config_file(), ConfFile = render_config_file(),
Conf = conf_parse:file(ConfFile), {ok, Conf} = hocon:load(ConfFile, #{format => richmap}),
cuttlefish_generator:map(Schema, Conf). hocon_schema:generate(emqx_schema, Conf).
set_app_env({App, Lists}) -> set_app_env({App, Lists}) ->
lists:foreach(fun({acl_file, _Var}) -> lists:foreach(fun({acl_file, _Var}) ->
application:set_env(App, acl_file, local_path(["etc", "acl.conf"])); application:set_env(App, acl_file, local_path(["etc", "acl.conf"]));
({plugins_loaded_file, _Var}) -> ({plugins_loaded_file, _Var}) ->
application:set_env(App, plugins_loaded_file, local_path(["test", "emqx_SUITE_data","loaded_plugins"])); application:set_env(App,
plugins_loaded_file,
local_path(["test", "emqx_SUITE_data","loaded_plugins"]));
({Par, Var}) -> ({Par, Var}) ->
application:set_env(App, Par, Var) application:set_env(App, Par, Var)
end, Lists). end, Lists).
@ -91,4 +92,4 @@ get_base_dir(Module) ->
get_base_dir() -> get_base_dir() ->
get_base_dir(?MODULE). get_base_dir(?MODULE).