diff --git a/.ci/docker-compose-file/Makefile.local b/.ci/docker-compose-file/Makefile.local index 14e4c95f7..8b8c6af68 100644 --- a/.ci/docker-compose-file/Makefile.local +++ b/.ci/docker-compose-file/Makefile.local @@ -19,18 +19,26 @@ up: docker-compose \ -f .ci/docker-compose-file/docker-compose.yaml \ -f .ci/docker-compose-file/docker-compose-mongo-single-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-mongo-single-tls.yaml \ -f .ci/docker-compose-file/docker-compose-mysql-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-mysql-tls.yaml \ -f .ci/docker-compose-file/docker-compose-pgsql-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-pgsql-tls.yaml \ -f .ci/docker-compose-file/docker-compose-redis-single-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-redis-single-tls.yaml \ up -d --build down: docker-compose \ -f .ci/docker-compose-file/docker-compose.yaml \ -f .ci/docker-compose-file/docker-compose-mongo-single-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-mongo-single-tls.yaml \ -f .ci/docker-compose-file/docker-compose-mysql-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-mysql-tls.yaml \ -f .ci/docker-compose-file/docker-compose-pgsql-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-pgsql-tls.yaml \ -f .ci/docker-compose-file/docker-compose-redis-single-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-redis-single-tls.yaml \ down ct: diff --git a/.ci/docker-compose-file/certs/ca.crt b/.ci/docker-compose-file/certs/ca.crt new file mode 100644 index 000000000..8a9dafccd --- /dev/null +++ b/.ci/docker-compose-file/certs/ca.crt @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE5DCCAswCCQCF3o0gIdaNDjANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlF +TVFYIFRlc3QxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMTEy +MzAwODQxMTFaFw00OTA1MTcwODQxMTFaMDQxEjAQBgNVBAoMCUVNUVggVGVzdDEe +MBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAqmqSrxyH16j63QhqGLT1UO8I+m6BM3HfnJQM8laQdtJ0 +WgHqCh0/OphH3S7v4SfF4fNJDEJWMWuuzJzU9cTqHPLzhvo3+ZHcMIENgtY2p2Cf +7AQjEqFViEDyv2ZWNEe76BJeShntdY5NZr4gIPar99YGG/Ln8YekspleV+DU38rE +EX9WzhgBr02NN9z4NzIxeB+jdvPnxcXs3WpUxzfnUjOQf/T1tManvSdRbFmKMbxl +A8NLYK3oAYm8EbljWUINUNN6loqYhbigKv8bvo5S4xvRqmX86XB7sc0SApngtNcg +O0EKn8z/KVPDskE+8lMfGMiU2e2Tzw6Rph57mQPOPtIp5hPiKRik7ST9n0p6piXW +zRLplJEzSjf40I1u+VHmpXlWI/Fs8b1UkDSMiMVJf0LyWb4ziBSZOY2LtZzWHbWj +LbNgxQcwSS29tKgUwfEFmFcm+iOM59cPfkl2IgqVLh5h4zmKJJbfQKSaYb5fcKRf +50b1qsN40VbR3Pk/0lJ0/WqgF6kZCExmT1qzD5HJES/5grjjKA4zIxmHOVU86xOF +ouWvtilVR4PGkzmkFvwK5yRhBUoGH/A9BurhqOc0QCGay1kqHQFA6se4JJS+9KOS +x8Rn1Nm6Pi7sd6Le3cKmHTlyl5a/ofKqTCX2Qh+v/7y62V1V1wnoh3ipRjdPTnMC +AwEAATANBgkqhkiG9w0BAQsFAAOCAgEARCqaocvlMFUQjtFtepO2vyG1krn11xJ0 +e7md26i+g8SxCCYqQ9IqGmQBg0Im8fyNDKRN/LZoj5+A4U4XkG1yya91ZIrPpWyF +KUiRAItchNj3g1kHmI2ckl1N//6Kpx3DPaS7qXZaN3LTExf6Ph+StE1FnS0wVF+s +tsNIf6EaQ+ZewW3pjdlLeAws3jvWKUkROc408Ngvx74zbbKo/zAC4tz8oH9ZcpsT +WD8enVVEeUQKI6ItcpZ9HgTI9TFWgfZ1vYwvkoRwNIeabYI62JKmLEo2vGfGwWKr +c+GjnJ/tlVI2DpPljfWOnQ037/7yyJI/zo65+HPRmGRD6MuW/BdPDYOvOZUTcQKh +kANi5THSbJJgZcG3jb1NLebaUQ1H0zgVjn0g3KhUV+NJQYk8RQ7rHtB+MySqTKlM +kRkRjfTfR0Ykxpks7Mjvsb6NcZENf08ZFPd45+e/ptsxpiKu4e4W4bV7NZDvNKf9 +0/aD3oGYNMiP7s+KJ1lRSAjnBuG21Yk8FpzG+yr8wvJhV8aFgNQ5wIH86SuUTmN0 +5bVzFEIcUejIwvGoQEctNHBlOwHrb7zmB6OwyZeMapdXBQ+9UDhYg8ehDqdDOdfn +wsBcnjD2MwNhlE1hjL+tZWLNwSHiD6xx3LvNoXZu2HK8Cp3SOrkE69cFghYMIZZb +T+fp6tNL6LE= +-----END CERTIFICATE----- diff --git a/.ci/docker-compose-file/certs/ca.key b/.ci/docker-compose-file/certs/ca.key new file mode 100644 index 000000000..16f9dd2b5 --- /dev/null +++ b/.ci/docker-compose-file/certs/ca.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/.ci/docker-compose-file/certs/server.crt b/.ci/docker-compose-file/certs/server.crt new file mode 100644 index 000000000..1fe7a516c --- /dev/null +++ b/.ci/docker-compose-file/certs/server.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEBDCCAeygAwIBAgIJAKTICmq1Lg6cMA0GCSqGSIb3DQEBCwUAMDQxEjAQBgNV +BAoMCUVNUVggVGVzdDEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X +DTIxMTIzMDA4NDExMloXDTQ5MDUxNzA4NDExMlowKzESMBAGA1UECgwJRU1RWCBU +ZXN0MRUwEwYDVQQDDAxhdXRobi1zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCdCXfM/j28fsi3vhxmHoy2UUz/VDcTJudadVNqTOQZPuqW5lex +309yYcZqThfT2ZSVIH92ags6aNxr4Uv9vGTkPW22kAiK41imeAj+HLmvByxqfv+s +JlB5YcHXMGQCcFZOaOtabuJ0nmqxO0OWU9CIeE5PWlnVyWM1cvYxtQQLg4BSP8X/ +ohFBERaBn0yU0IYTFxo+9A1LB5utnWiv7A/5fZVFBkAdrGMPxcuEF49oynbW4WpN +kn1jY+89BrBvLk+lMZCTI2dRnE5tqt+kD6Ejh3eWRiONoS6sm9rIrH/OMEqEXhfi +bgZZu8rL0o1YL7SATJERBNuvcJpQl7We5UCbAgMBAAGjIjAgMAsGA1UdDwQEAwIF +oDARBglghkgBhvhCAQEEBAMCBkAwDQYJKoZIhvcNAQELBQADggIBAAydWowM0rS5 +CgrVsuSUnUntXkIIu9YziI8mKWm8K5sp8lqtVovitVFuG19Y3Ve8r2pIibpBvOKZ +ocr+uUgrZrGGXU3x9/p+miTcHm5M9guPzmN6JbKZ65yIAN9po5CjrczFShqxIQly +ye+5C7/Metf6KM43lLKefDkUgccASKa4KhvP84/Jc8jEKP2cQ5I84yaRyeJgDnJ0 +XY6Nu1yn1BLrw9dq5ZcoBYR94aVPnSR63zE58cJ99r8AOSk/Tl7phKNAS7mP94NH +RVTW4R/xGMT/iVz4x9exfeVfAX5fVAPIOXV5VKownmM/WfhICHxNLi++m9nO9sn6 +tHT+3ViYUbilhcPlXVgTiVWJrFuoxbPTON4yIxgT3VQz47Oqnx37jeufbb7bGiJW +H/GEtn5pDPbiHbu6j+GK98uTN7OoTM5L81nbct6evEz6sK2T5Ve5Ro2IWWeG7xlB +3+FIK1pzl5OHpLJTED/DKNxt1qlhnjTGSz902fBORYvTCTdpSfGnrUMjJOP0rGHH +81WFMfc6ucsN4zGXVHHUNuNaUp1HprUy4g7ipTXkRn9oyOXkYKMGMX9T2aUeEnXO +U9ij61TrGA+lZENsbFKD/UcLRr4GY21TKj9dKjKyIoru/qDHrtJkSObQlcgOwS7D +ctaGcj4es0ByT2PX/mDqJoMip3E4E11O +-----END CERTIFICATE----- diff --git a/.ci/docker-compose-file/certs/server.key b/.ci/docker-compose-file/certs/server.key new file mode 100644 index 000000000..649b69428 --- /dev/null +++ b/.ci/docker-compose-file/certs/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAnQl3zP49vH7It74cZh6MtlFM/1Q3EybnWnVTakzkGT7qluZX +sd9PcmHGak4X09mUlSB/dmoLOmjca+FL/bxk5D1ttpAIiuNYpngI/hy5rwcsan7/ +rCZQeWHB1zBkAnBWTmjrWm7idJ5qsTtDllPQiHhOT1pZ1cljNXL2MbUEC4OAUj/F +/6IRQREWgZ9MlNCGExcaPvQNSwebrZ1or+wP+X2VRQZAHaxjD8XLhBePaMp21uFq +TZJ9Y2PvPQawby5PpTGQkyNnUZxObarfpA+hI4d3lkYjjaEurJvayKx/zjBKhF4X +4m4GWbvKy9KNWC+0gEyREQTbr3CaUJe1nuVAmwIDAQABAoIBABl6dMZ8pXWUuGof +XSowYLIf5Lc0aa8gy76AdKU1jniOHa+X9bh1O8WaGYAb5X/IuHOtjyCeOe4jH0gd +iJ/FVjU1xjwtiEVId5SiuwrHjFTafBlXO5IpsTrQYovQXRmMMmSMX0sP3IwBO9w/ +ekrElHvf0QzM4vBtuTvtyAXukZZwYWvdJK8GXc7NE0xTNSe0C+f2MS0ZAuWP7g8K +1WgRO+8pb11sK4CAl+yD6Lyf7JVlouTcsYdeRF5o7yuEQ2qlz3+vxPwfMIpONKel +kK5nUUc8OGhHQpkO+ZZXh5fIWkaKFJKMzoAh8pj2HFAfK93s64f3LHu75sum05Gc +RUCSafkCgYEAympLWe+cmq8XyUqQnsQ7hHfc5VKa33YDTEkO/ZncnnNA/k4yH+r7 +LGgMZD1zC5R3pRFEET9pUOrlx7Z489Bc1Z9Y+9dDpwg9DRrvkt1/MpxOI2Lk8tiJ +lLU/uRTQXQmHFoEBg6i2CDIZyP/qccCS0zIcMQJDq6WaTfXyJ5k0LOUCgYEAxpvi +l7t9RPIQXTEfWiD3iN11QwZYjZ3c6CfW2iaucPYJZDclk6BO1Chdw55cELbfj4bh +7lMxDYpyOQrEwIXYk1a8IY6VOFFMmOQfCfECm5XNTvz//5vYxYlB8ERdhM7opAYG +YsAyR/+BVEyhG6NXy4sh5Q49YgfrjVMdYmBSX38CgYEAx2BF0lNzNOXsjwgURV5S +pZuPCI8CH8PVYcnAq0lnhudNiHArbUb+mvHt6rqgXDKkWwITws1sBhkptjrlDnsZ +Rg3MD1wsthUmVYdHnajxBj/xs2dQzmc9tS2Gk96Nkma1GhR+EloW2yHGRjbVjbA6 +ry53mEp7r1HSGKJ+IEUGoIUCgYEAiRS7FyNPWTECXnAzRZAPiiXgc7yDjmtxN8OX +pcahDFKlNMhjZTt2bTTXUteQj/DI6VWdx1MgPkpagEiQeJlpXHi3LSoukEp85eI+ +EiyJMj35ERXK0/ALdHxCSMXHDo2JQPzvl2U0z0DpUPf7Ewpw5IpJgMGNWIZC7K57 +T5VQBZ0CgYAcAG1KYZYD+Sb14jJLSD6JqnJBrcv8e6wEAnA+0vuEv09FfgeB4MNZ +FwRR8FQDL8V2QcvsauwcwNOf9m9K8goCV9YKTcFw5Tl0m3uYzCIDVdyZI85NgBS0 +m//eODmUYg1gMOi9LfnKgtrW7EURrCNj3Pgt87g7WDiSY+qGB0IzzQ== +-----END RSA PRIVATE KEY----- diff --git a/.ci/docker-compose-file/docker-compose-mongo-single-tls.yaml b/.ci/docker-compose-file/docker-compose-mongo-single-tls.yaml index c4f162783..b02eb77de 100644 --- a/.ci/docker-compose-file/docker-compose-mongo-single-tls.yaml +++ b/.ci/docker-compose-file/docker-compose-mongo-single-tls.yaml @@ -1,23 +1,30 @@ version: '3.9' services: - mongo_server: - container_name: mongo + mongo_server_tls: + container_name: mongo-tls image: mongo:${MONGO_TAG} restart: always environment: MONGO_INITDB_DATABASE: mqtt volumes: - - ../../apps/emqx/etc/certs/cert.pem:/etc/certs/cert.pem - - ../../apps/emqx/etc/certs/key.pem:/etc/certs/key.pem + - ./certs/server.crt:/etc/certs/cert.pem + - ./certs/server.key:/etc/certs/key.pem + - ./certs/ca.crt:/etc/certs/cacert.pem networks: - emqx_bridge ports: - - "27017:27017" + - "27018:27017" command: - /bin/bash - -c - | - cat /etc/certs/key.pem /etc/certs/cert.pem > /etc/certs/mongodb.pem - mongod --ipv6 --bind_ip_all --sslMode requireSSL --sslPEMKeyFile /etc/certs/mongodb.pem + cat /etc/certs/key.pem /etc/certs/cert.pem > /etc/certs/mongodb.pem + mongod --ipv6 --bind_ip_all \ + --tlsOnNormalPorts \ + --tlsMode requireSSL \ + --tlsCertificateKeyFile /etc/certs/mongodb.pem \ + --tlsCAFile /etc/certs/cacert.pem \ + --tlsDisabledProtocols TLS1_0,TLS1_1 \ + --setParameter opensslCipherConfig='HIGH:!EXPORT:!aNULL:!DHE:!kDHE@STRENGTH' diff --git a/.ci/docker-compose-file/docker-compose-mysql-tls.yaml b/.ci/docker-compose-file/docker-compose-mysql-tls.yaml index 17dfdcc8e..47d9ecd83 100644 --- a/.ci/docker-compose-file/docker-compose-mysql-tls.yaml +++ b/.ci/docker-compose-file/docker-compose-mysql-tls.yaml @@ -1,47 +1,35 @@ version: '3.9' services: - mysql_server: - container_name: mysql + mysql_server_tls: + container_name: mysql-tls image: mysql:${MYSQL_TAG} restart: always environment: MYSQL_ROOT_PASSWORD: public MYSQL_DATABASE: mqtt - MYSQL_USER: ssluser + MYSQL_USER: user MYSQL_PASSWORD: public volumes: - - ../../apps/emqx/etc/certs/cacert.pem:/etc/certs/ca-cert.pem - - ../../apps/emqx/etc/certs/cert.pem:/etc/certs/server-cert.pem - - ../../apps/emqx/etc/certs/key.pem:/etc/certs/server-key.pem + - ./certs/ca.crt:/etc/certs/ca-cert.pem + - ./certs/server.crt:/etc/certs/server-cert.pem + - ./certs/server.key:/etc/certs/server-key.pem ports: - - "3306:3306" + - "3307:3306" networks: - emqx_bridge command: - --bind-address "::" - --character-set-server=utf8mb4 - --collation-server=utf8mb4_general_ci - --explicit_defaults_for_timestamp=true - --lower_case_table_names=1 - --max_allowed_packet=128M - --skip-symbolic-links - --ssl-ca=/etc/certs/ca-cert.pem - --ssl-cert=/etc/certs/server-cert.pem - --ssl-key=/etc/certs/server-key.pem + - --bind-address=0.0.0.0 + - --port=3306 + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_general_ci + - --explicit_defaults_for_timestamp=true + - --lower_case_table_names=1 + - --max_allowed_packet=128M + - --ssl-ca=/etc/certs/ca-cert.pem + - --ssl-cert=/etc/certs/server-cert.pem + - --ssl-key=/etc/certs/server-key.pem + - --require-secure-transport=ON + - --tls-version=TLSv1.2,TLSv1.3 + - --ssl-cipher=ECDHE-RSA-AES256-GCM-SHA384 - mysql_client: - container_name: mysql_client - image: mysql:${MYSQL_TAG} - networks: - - emqx_bridge - depends_on: - - mysql_server - command: - - /bin/bash - - -c - - | - service mysql start - echo "show tables;" | mysql -h mysql_server -u root -ppublic mqtt mqtt - while [[ $$? -ne 0 ]];do echo "show tables;" | mysql -h mysql_server -u root -ppublic mqtt; done - echo "ALTER USER 'ssluser'@'%' REQUIRE X509;" | mysql -h mysql_server -u root -ppublic mqtt diff --git a/.ci/docker-compose-file/docker-compose-pgsql-tls.yaml b/.ci/docker-compose-file/docker-compose-pgsql-tls.yaml index 72aceed69..29d520e29 100644 --- a/.ci/docker-compose-file/docker-compose-pgsql-tls.yaml +++ b/.ci/docker-compose-file/docker-compose-pgsql-tls.yaml @@ -1,11 +1,11 @@ version: '3.9' services: - pgsql_server: - container_name: pgsql + pgsql_server_tls: + container_name: pgsql-tls build: - context: ../.. - dockerfile: .ci/docker-compose-file/pgsql/Dockerfile + context: ./ + dockerfile: ./pgsql/Dockerfile args: POSTGRES_USER: postgres BUILD_FROM: postgres:${PGSQL_TAG} @@ -16,7 +16,7 @@ services: POSTGRES_USER: root POSTGRES_PASSWORD: public ports: - - "5432:5432" + - "5433:5432" command: - -c - ssl=on @@ -28,5 +28,7 @@ services: - ssl_ca_file=/var/lib/postgresql/root.crt - -c - hba_file=/var/lib/postgresql/pg_hba.conf + - -c + - ssl_min_protocol_version=TLSv1.2 networks: - emqx_bridge diff --git a/.ci/docker-compose-file/docker-compose-redis-single-tls.yaml b/.ci/docker-compose-file/docker-compose-redis-single-tls.yaml index bb6c3ff15..8f59e7a9e 100644 --- a/.ci/docker-compose-file/docker-compose-redis-single-tls.yaml +++ b/.ci/docker-compose-file/docker-compose-redis-single-tls.yaml @@ -1,13 +1,15 @@ version: '3.9' services: - redis_server: - container_name: redis + redis_server_tls: + container_name: redis-tls image: redis:${REDIS_TAG} volumes: - - ../../apps/emqx/etc/certs/cacert.pem:/etc/certs/ca.crt - - ../../apps/emqx/etc/certs/cert.pem:/etc/certs/redis.crt - - ../../apps/emqx/etc/certs/key.pem:/etc/certs/redis.key + - ./certs/server.crt:/etc/certs/redis.crt + - ./certs/server.key:/etc/certs/redis.key + - ./certs/ca.crt:/etc/certs/ca.crt + ports: + - "6380:6380" command: - redis-server - "--bind 0.0.0.0 ::" @@ -16,6 +18,8 @@ services: - --tls-cert-file /etc/certs/redis.crt - --tls-key-file /etc/certs/redis.key - --tls-ca-cert-file /etc/certs/ca.crt + - --tls-protocols "TLSv1.3" + - --tls-ciphersuites "TLS_CHACHA20_POLY1305_SHA256" restart: always networks: - - emqx_bridge + emqx_bridge: diff --git a/.ci/docker-compose-file/pgsql/Dockerfile b/.ci/docker-compose-file/pgsql/Dockerfile index db2cd59fe..f26e18d0e 100644 --- a/.ci/docker-compose-file/pgsql/Dockerfile +++ b/.ci/docker-compose-file/pgsql/Dockerfile @@ -1,10 +1,10 @@ -ARG BUILD_FROM=postgres:11 +ARG BUILD_FROM=postgres:13 FROM ${BUILD_FROM} ARG POSTGRES_USER=postgres -COPY --chown=$POSTGRES_USER .ci/docker-compose-file/pgsql/pg_hba.conf /var/lib/postgresql/pg_hba.conf -COPY --chown=$POSTGRES_USER apps/emqx/etc/certs/key.pem /var/lib/postgresql/server.key -COPY --chown=$POSTGRES_USER apps/emqx/etc/certs/cert.pem /var/lib/postgresql/server.crt -COPY --chown=$POSTGRES_USER apps/emqx/etc/certs/cacert.pem /var/lib/postgresql/root.crt +COPY --chown=$POSTGRES_USER ./pgsql/pg_hba.conf /var/lib/postgresql/pg_hba.conf +COPY --chown=$POSTGRES_USER certs/server.key /var/lib/postgresql/server.key +COPY --chown=$POSTGRES_USER certs/server.crt /var/lib/postgresql/server.crt +COPY --chown=$POSTGRES_USER certs/ca.crt /var/lib/postgresql/root.crt RUN chmod 600 /var/lib/postgresql/pg_hba.conf RUN chmod 600 /var/lib/postgresql/server.key RUN chmod 600 /var/lib/postgresql/server.crt diff --git a/.ci/docker-compose-file/pgsql/postgresql.conf b/.ci/docker-compose-file/pgsql/postgresql.conf new file mode 100644 index 000000000..b28b04f64 --- /dev/null +++ b/.ci/docker-compose-file/pgsql/postgresql.conf @@ -0,0 +1,3 @@ + + + diff --git a/.github/workflows/run_test_cases.yaml b/.github/workflows/run_test_cases.yaml index bb0fb1c82..a88d6888b 100644 --- a/.github/workflows/run_test_cases.yaml +++ b/.github/workflows/run_test_cases.yaml @@ -63,9 +63,13 @@ jobs: run: | docker-compose \ -f .ci/docker-compose-file/docker-compose-mongo-single-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-mongo-single-tls.yaml \ -f .ci/docker-compose-file/docker-compose-mysql-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-mysql-tls.yaml \ -f .ci/docker-compose-file/docker-compose-pgsql-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-pgsql-tls.yaml \ -f .ci/docker-compose-file/docker-compose-redis-single-tcp.yaml \ + -f .ci/docker-compose-file/docker-compose-redis-single-tls.yaml \ -f .ci/docker-compose-file/docker-compose.yaml \ up -d --build - name: run eunit diff --git a/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl b/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl index 9295e5c7e..a1fcbe0d6 100644 --- a/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl +++ b/apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl @@ -114,8 +114,8 @@ certfile(_) -> undefined. keyfile(type) -> string(); keyfile(_) -> undefined. -verify(type) -> boolean(); -verify(default) -> false; +verify(type) -> hoconsc:enum([verify_peer, verify_none]); +verify(default) -> verify_none; verify(_) -> undefined. server_name_indication(type) -> string(); diff --git a/apps/emqx_authn/test/data/certs/ca.crt b/apps/emqx_authn/test/data/certs/ca.crt new file mode 100644 index 000000000..8a9dafccd --- /dev/null +++ b/apps/emqx_authn/test/data/certs/ca.crt @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE5DCCAswCCQCF3o0gIdaNDjANBgkqhkiG9w0BAQsFADA0MRIwEAYDVQQKDAlF +TVFYIFRlc3QxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMTEy +MzAwODQxMTFaFw00OTA1MTcwODQxMTFaMDQxEjAQBgNVBAoMCUVNUVggVGVzdDEe +MBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAqmqSrxyH16j63QhqGLT1UO8I+m6BM3HfnJQM8laQdtJ0 +WgHqCh0/OphH3S7v4SfF4fNJDEJWMWuuzJzU9cTqHPLzhvo3+ZHcMIENgtY2p2Cf +7AQjEqFViEDyv2ZWNEe76BJeShntdY5NZr4gIPar99YGG/Ln8YekspleV+DU38rE +EX9WzhgBr02NN9z4NzIxeB+jdvPnxcXs3WpUxzfnUjOQf/T1tManvSdRbFmKMbxl +A8NLYK3oAYm8EbljWUINUNN6loqYhbigKv8bvo5S4xvRqmX86XB7sc0SApngtNcg +O0EKn8z/KVPDskE+8lMfGMiU2e2Tzw6Rph57mQPOPtIp5hPiKRik7ST9n0p6piXW +zRLplJEzSjf40I1u+VHmpXlWI/Fs8b1UkDSMiMVJf0LyWb4ziBSZOY2LtZzWHbWj +LbNgxQcwSS29tKgUwfEFmFcm+iOM59cPfkl2IgqVLh5h4zmKJJbfQKSaYb5fcKRf +50b1qsN40VbR3Pk/0lJ0/WqgF6kZCExmT1qzD5HJES/5grjjKA4zIxmHOVU86xOF +ouWvtilVR4PGkzmkFvwK5yRhBUoGH/A9BurhqOc0QCGay1kqHQFA6se4JJS+9KOS +x8Rn1Nm6Pi7sd6Le3cKmHTlyl5a/ofKqTCX2Qh+v/7y62V1V1wnoh3ipRjdPTnMC +AwEAATANBgkqhkiG9w0BAQsFAAOCAgEARCqaocvlMFUQjtFtepO2vyG1krn11xJ0 +e7md26i+g8SxCCYqQ9IqGmQBg0Im8fyNDKRN/LZoj5+A4U4XkG1yya91ZIrPpWyF +KUiRAItchNj3g1kHmI2ckl1N//6Kpx3DPaS7qXZaN3LTExf6Ph+StE1FnS0wVF+s +tsNIf6EaQ+ZewW3pjdlLeAws3jvWKUkROc408Ngvx74zbbKo/zAC4tz8oH9ZcpsT +WD8enVVEeUQKI6ItcpZ9HgTI9TFWgfZ1vYwvkoRwNIeabYI62JKmLEo2vGfGwWKr +c+GjnJ/tlVI2DpPljfWOnQ037/7yyJI/zo65+HPRmGRD6MuW/BdPDYOvOZUTcQKh +kANi5THSbJJgZcG3jb1NLebaUQ1H0zgVjn0g3KhUV+NJQYk8RQ7rHtB+MySqTKlM +kRkRjfTfR0Ykxpks7Mjvsb6NcZENf08ZFPd45+e/ptsxpiKu4e4W4bV7NZDvNKf9 +0/aD3oGYNMiP7s+KJ1lRSAjnBuG21Yk8FpzG+yr8wvJhV8aFgNQ5wIH86SuUTmN0 +5bVzFEIcUejIwvGoQEctNHBlOwHrb7zmB6OwyZeMapdXBQ+9UDhYg8ehDqdDOdfn +wsBcnjD2MwNhlE1hjL+tZWLNwSHiD6xx3LvNoXZu2HK8Cp3SOrkE69cFghYMIZZb +T+fp6tNL6LE= +-----END CERTIFICATE----- diff --git a/apps/emqx_authn/test/data/certs/cacert.pem b/apps/emqx_authn/test/data/certs/cacert.pem deleted file mode 100644 index 604fd2362..000000000 --- a/apps/emqx_authn/test/data/certs/cacert.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDUTCCAjmgAwIBAgIJAPPYCjTmxdt/MA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV -BAYTAkNOMREwDwYDVQQIDAhoYW5nemhvdTEMMAoGA1UECgwDRU1RMQ8wDQYDVQQD -DAZSb290Q0EwHhcNMjAwNTA4MDgwNjUyWhcNMzAwNTA2MDgwNjUyWjA/MQswCQYD -VQQGEwJDTjERMA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UE -AwwGUm9vdENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzcgVLex1 -EZ9ON64EX8v+wcSjzOZpiEOsAOuSXOEN3wb8FKUxCdsGrsJYB7a5VM/Jot25Mod2 -juS3OBMg6r85k2TWjdxUoUs+HiUB/pP/ARaaW6VntpAEokpij/przWMPgJnBF3Ur -MjtbLayH9hGmpQrI5c2vmHQ2reRZnSFbY+2b8SXZ+3lZZgz9+BaQYWdQWfaUWEHZ -uDaNiViVO0OT8DRjCuiDp3yYDj3iLWbTA/gDL6Tf5XuHuEwcOQUrd+h0hyIphO8D -tsrsHZ14j4AWYLk1CPA6pq1HIUvEl2rANx2lVUNv+nt64K/Mr3RnVQd9s8bK+TXQ -KGHd2Lv/PALYuwIDAQABo1AwTjAdBgNVHQ4EFgQUGBmW+iDzxctWAWxmhgdlE8Pj -EbQwHwYDVR0jBBgwFoAUGBmW+iDzxctWAWxmhgdlE8PjEbQwDAYDVR0TBAUwAwEB -/zANBgkqhkiG9w0BAQsFAAOCAQEAGbhRUjpIred4cFAFJ7bbYD9hKu/yzWPWkMRa -ErlCKHmuYsYk+5d16JQhJaFy6MGXfLgo3KV2itl0d+OWNH0U9ULXcglTxy6+njo5 -CFqdUBPwN1jxhzo9yteDMKF4+AHIxbvCAJa17qcwUKR5MKNvv09C6pvQDJLzid7y -E2dkgSuggik3oa0427KvctFf8uhOV94RvEDyqvT5+pgNYZ2Yfga9pD/jjpoHEUlo -88IGU8/wJCx3Ds2yc8+oBg/ynxG8f/HmCC1ET6EHHoe2jlo8FpU/SgGtghS1YL30 -IWxNsPrUP+XsZpBJy/mvOhE5QXo6Y35zDqqj8tI7AGmAWu22jg== ------END CERTIFICATE----- diff --git a/apps/emqx_authn/test/data/certs/cert.pem b/apps/emqx_authn/test/data/certs/cert.pem deleted file mode 100644 index 092390b1d..000000000 --- a/apps/emqx_authn/test/data/certs/cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDEzCCAfugAwIBAgIBAjANBgkqhkiG9w0BAQsFADA/MQswCQYDVQQGEwJDTjER -MA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UEAwwGUm9vdENB -MB4XDTIwMDUwODA4MDcwNVoXDTMwMDUwNjA4MDcwNVowPzELMAkGA1UEBhMCQ04x -ETAPBgNVBAgMCGhhbmd6aG91MQwwCgYDVQQKDANFTVExDzANBgNVBAMMBlNlcnZl -cjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALNeWT3pE+QFfiRJzKmn -AMUrWo3K2j/Tm3+Xnl6WLz67/0rcYrJbbKvS3uyRP/stXyXEKw9CepyQ1ViBVFkW -Aoy8qQEOWFDsZc/5UzhXUnb6LXr3qTkFEjNmhj+7uzv/lbBxlUG1NlYzSeOB6/RT -8zH/lhOeKhLnWYPXdXKsa1FL6ij4X8DeDO1kY7fvAGmBn/THh1uTpDizM4YmeI+7 -4dmayA5xXvARte5h4Vu5SIze7iC057N+vymToMk2Jgk+ZZFpyXrnq+yo6RaD3ANc -lrc4FbeUQZ5a5s5Sxgs9a0Y3WMG+7c5VnVXcbjBRz/aq2NtOnQQjikKKQA8GF080 -BQkCAwEAAaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL -BQADggEBAJefnMZpaRDHQSNUIEL3iwGXE9c6PmIsQVE2ustr+CakBp3TZ4l0enLt -iGMfEVFju69cO4oyokWv+hl5eCMkHBf14Kv51vj448jowYnF1zmzn7SEzm5Uzlsa -sqjtAprnLyof69WtLU1j5rYWBuFX86yOTwRAFNjm9fvhAcrEONBsQtqipBWkMROp -iUYMkRqbKcQMdwxov+lHBYKq9zbWRoqLROAn54SRqgQk6c15JdEfgOOjShbsOkIH -UhqcwRkQic7n1zwHVGVDgNIZVgmJ2IdIWBlPEC7oLrRrBD/X1iEEXtKab6p5o22n -KB5mN+iQaE+Oe2cpGKZJiJRdM+IqDDQ= ------END CERTIFICATE----- diff --git a/apps/emqx_authn/test/data/certs/client-cert.pem b/apps/emqx_authn/test/data/certs/client-cert.pem deleted file mode 100644 index 09d855221..000000000 --- a/apps/emqx_authn/test/data/certs/client-cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDEzCCAfugAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MQswCQYDVQQGEwJDTjER -MA8GA1UECAwIaGFuZ3pob3UxDDAKBgNVBAoMA0VNUTEPMA0GA1UEAwwGUm9vdENB -MB4XDTIwMDUwODA4MDY1N1oXDTMwMDUwNjA4MDY1N1owPzELMAkGA1UEBhMCQ04x -ETAPBgNVBAgMCGhhbmd6aG91MQwwCgYDVQQKDANFTVExDzANBgNVBAMMBkNsaWVu -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMy4hoksKcZBDbY680u6 -TS25U51nuB1FBcGMlF9B/t057wPOlxF/OcmbxY5MwepS41JDGPgulE1V7fpsXkiW -1LUimYV/tsqBfymIe0mlY7oORahKji7zKQ2UBIVFhdlvQxunlIDnw6F9popUgyHt -dMhtlgZK8oqRwHxO5dbfoukYd6J/r+etS5q26sgVkf3C6dt0Td7B25H9qW+f7oLV -PbcHYCa+i73u9670nrpXsC+Qc7Mygwa2Kq/jwU+ftyLQnOeW07DuzOwsziC/fQZa -nbxR+8U9FNftgRcC3uP/JMKYUqsiRAuaDokARZxVTV5hUElfpO6z6/NItSDvvh3i -eikCAwEAAaMaMBgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL -BQADggEBABchYxKo0YMma7g1qDswJXsR5s56Czx/I+B41YcpMBMTrRqpUC0nHtLk -M7/tZp592u/tT8gzEnQjZLKBAhFeZaR3aaKyknLqwiPqJIgg0pgsBGITrAK3Pv4z -5/YvAJJKgTe5UdeTz6U4lvNEux/4juZ4pmqH4qSFJTOzQS7LmgSmNIdd072rwXBd -UzcSHzsJgEMb88u/LDLjj1pQ7AtZ4Tta8JZTvcgBFmjB0QUi6fgkHY6oGat/W4kR -jSRUBlMUbM/drr2PVzRc2dwbFIl3X+ZE6n5Sl3ZwRAC/s92JU6CPMRW02muVu6xl -goraNgPISnrbpR6KjxLZkVembXzjNNc= ------END CERTIFICATE----- diff --git a/apps/emqx_authn/test/data/certs/client-key.pem b/apps/emqx_authn/test/data/certs/client-key.pem deleted file mode 100644 index 2b3f30cf6..000000000 --- a/apps/emqx_authn/test/data/certs/client-key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAzLiGiSwpxkENtjrzS7pNLblTnWe4HUUFwYyUX0H+3TnvA86X -EX85yZvFjkzB6lLjUkMY+C6UTVXt+mxeSJbUtSKZhX+2yoF/KYh7SaVjug5FqEqO -LvMpDZQEhUWF2W9DG6eUgOfDoX2milSDIe10yG2WBkryipHAfE7l1t+i6Rh3on+v -561LmrbqyBWR/cLp23RN3sHbkf2pb5/ugtU9twdgJr6Lve73rvSeulewL5BzszKD -BrYqr+PBT5+3ItCc55bTsO7M7CzOIL99BlqdvFH7xT0U1+2BFwLe4/8kwphSqyJE -C5oOiQBFnFVNXmFQSV+k7rPr80i1IO++HeJ6KQIDAQABAoIBAGWgvPjfuaU3qizq -uti/FY07USz0zkuJdkANH6LiSjlchzDmn8wJ0pApCjuIE0PV/g9aS8z4opp5q/gD -UBLM/a8mC/xf2EhTXOMrY7i9p/I3H5FZ4ZehEqIw9sWKK9YzC6dw26HabB2BGOnW -5nozPSQ6cp2RGzJ7BIkxSZwPzPnVTgy3OAuPOiJytvK+hGLhsNaT+Y9bNDvplVT2 -ZwYTV8GlHZC+4b2wNROILm0O86v96O+Qd8nn3fXjGHbMsAnONBq10bZS16L4fvkH -5G+W/1PeSXmtZFppdRRDxIW+DWcXK0D48WRliuxcV4eOOxI+a9N2ZJZZiNLQZGwg -w3A8+mECgYEA8HuJFrlRvdoBe2U/EwUtG74dcyy30L4yEBnN5QscXmEEikhaQCfX -Wm6EieMcIB/5I5TQmSw0cmBMeZjSXYoFdoI16/X6yMMuATdxpvhOZGdUGXxhAH+x -xoTUavWZnEqW3fkUU71kT5E2f2i+0zoatFESXHeslJyz85aAYpP92H0CgYEA2e5A -Yozt5eaA1Gyhd8SeptkEU4xPirNUnVQHStpMWUb1kzTNXrPmNWccQ7JpfpG6DcYl -zUF6p6mlzY+zkMiyPQjwEJlhiHM2NlL1QS7td0R8ewgsFoyn8WsBI4RejWrEG9td -EDniuIw+pBFkcWthnTLHwECHdzgquToyTMjrBB0CgYEA28tdGbrZXhcyAZEhHAZA -Gzog+pKlkpEzeonLKIuGKzCrEKRecIK5jrqyQsCjhS0T7ZRnL4g6i0s+umiV5M5w -fcc292pEA1h45L3DD6OlKplSQVTv55/OYS4oY3YEJtf5mfm8vWi9lQeY8sxOlQpn -O+VZTdBHmTC8PGeTAgZXHZUCgYA6Tyv88lYowB7SN2qQgBQu8jvdGtqhcs/99GCr -H3N0I69LPsKAR0QeH8OJPXBKhDUywESXAaEOwS5yrLNP1tMRz5Vj65YUCzeDG3kx -gpvY4IMp7ArX0bSRvJ6mYSFnVxy3k174G3TVCfksrtagHioVBGQ7xUg5ltafjrms -n8l55QKBgQDVzU8tQvBVqY8/1lnw11Vj4fkE/drZHJ5UkdC1eenOfSWhlSLfUJ8j -ds7vEWpRPPoVuPZYeR1y78cyxKe1GBx6Wa2lF5c7xjmiu0xbRnrxYeLolce9/ntp -asClqpnHT8/VJYTD7Kqj0fouTTZf0zkig/y+2XERppd8k+pSKjUCPQ== ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_authn/test/data/certs/client.crt b/apps/emqx_authn/test/data/certs/client.crt new file mode 100644 index 000000000..a198faf61 --- /dev/null +++ b/apps/emqx_authn/test/data/certs/client.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIID/jCCAeagAwIBAgIJAKTICmq1Lg6dMA0GCSqGSIb3DQEBCwUAMDQxEjAQBgNV +BAoMCUVNUVggVGVzdDEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X +DTIxMTIzMDA4NDExMloXDTQ5MDUxNzA4NDExMlowJTESMBAGA1UECgwJRU1RWCBU +ZXN0MQ8wDQYDVQQDDAZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDzrujfx6XZTH0MWqLO6kNAeHndUZ+OGaURXvxKMPMF5dA40lxNG6cEzzlq +0Rm61adlv8tF4kRJrs6EnRjEVoMImrdh07vGFdOTYqP01LjiBhErAzyRtSn2X8FT +Te8ExoCRs3x61SPebGY2hOvFxuO6YDPVOSDvbbxvRgqIlM1ZXC8dOvPSSGZ+P8hV +56EPayRthfu1FVptnkW9CyZCRI0gg95Hv8RC7bGG+tuWpkN9ZrRvohhgGR1+bDUi +BNBpncEsSh+UgWaj8KRN8D16H6m/Im6ty467j0at49FvPx5nACL48/ghtYvzgKLc +uKHtokKUuuzebDK/hQxN3mUSAJStAgMBAAGjIjAgMAsGA1UdDwQEAwIFoDARBglg +hkgBhvhCAQEEBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAIlVyPhOpkz3MNzQmjX7 +xgJ3vGPK5uK11n/wfjRwe2qXwZbrI2sYLVtTpUgvLDuP0gB73Vwfu7xAMdue6TRm +CKr9z0lkQsVBtgoqzZCjd4PYLfHm4EhsOMi98OGKU5uOGD4g3yLwQWXHhbYtiZMO +Jsj0hebYveYJt/BYTd1syGQcIcYCyVExWvSWjidfpAqjT6EF7whdubaFtuF2kaGF +IO9yn9rWtXB5yK99uCguEmKhx3fAQxomzqweTu3WRvy9axsUH3WAUW9a4DIBSz2+ +ZSJNheFn5GktgggygJUGYqpSZHooUJW0UBs/8vX6AP+8MtINmqOGZUawmNwLWLOq +wHyVt2YGD5TXjzzsWNSQ4mqXxM6AXniZVZK0yYNjA4ATikX1AtwunyWBR4IjyE/D +FxYPORdZCOtywRFE1R5KLTUq/C8BNGCkYnoO78DJBO+pT0oagkQGQb0CnmC6C1db +4lWzA9K0i4B0PyooZA+gp+5FFgaLuX1DkyeaY1J204QhHR1z/Vcyl5dpqR9hqnYP +t8raLk9ogMDKqKA9iG0wc3CBNckD4sjVWAEeovXhElG55fD21wwhF+AnDCvX8iVK +cBfKV6z6uxfKjGIxc2I643I5DiIn+V3DnPxYyY74Ln1lWFYmt5JREhAxPu42zq74 +e6+eIMYFszB+5gKgt6pa6ZNI +-----END CERTIFICATE----- diff --git a/apps/emqx_authn/test/data/certs/client.key b/apps/emqx_authn/test/data/certs/client.key new file mode 100644 index 000000000..2f0af5d41 --- /dev/null +++ b/apps/emqx_authn/test/data/certs/client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA867o38el2Ux9DFqizupDQHh53VGfjhmlEV78SjDzBeXQONJc +TRunBM85atEZutWnZb/LReJESa7OhJ0YxFaDCJq3YdO7xhXTk2Kj9NS44gYRKwM8 +kbUp9l/BU03vBMaAkbN8etUj3mxmNoTrxcbjumAz1Tkg7228b0YKiJTNWVwvHTrz +0khmfj/IVeehD2skbYX7tRVabZ5FvQsmQkSNIIPeR7/EQu2xhvrblqZDfWa0b6IY +YBkdfmw1IgTQaZ3BLEoflIFmo/CkTfA9eh+pvyJurcuOu49GrePRbz8eZwAi+PP4 +IbWL84Ci3Lih7aJClLrs3mwyv4UMTd5lEgCUrQIDAQABAoIBAQDwEbBgznrIwn8r +jZt5x/brbAV7Ea/kOcWSgIaCvQifFdJ2OGAwov5/UXwajNgRZe2d4z7qoUhvYuUY +ZwCAZU6ASpRBr2v9cYFYYURvrqZaHmoJew3P6q/lhl6aqFvC06DUagRHqvXEafyk +13zEAvZVpfNKrBaTawPKiDFWb2qDDc9D6hC07EuJ/DNeehiHvzHrSZSDVV5Ut7Bw +YDm33XygheUPAlHfeCnaixzcs3osiVyFEmVjxcIaM0ZS1NgcSaohSpJHMzvEaohX +e+v9vccraSVlw01AlvFwI2vHYUV8jT6HwglTPKKGOCzK/ace3wPdYSU9qLcqfuHn +EFhNc3tNAoGBAPugLMgbReJg2gpbIPUkYyoMMAAU7llFU1WvPWwXzo1a9EBjBACw +WfCZISNtANXR38zIYXzoH547uXi4YPks1Nne3sYuCDpvuX+iz7fIo4zHf1nFmxH7 +eE6GtQr2ubmuuipTc28S0wBMGT1/KybH0e2NKL6GaOkNDmAI0IbEMBrvAoGBAPfr +Y1QYLhPhan6m5g/5s+bQpKtHfNH9TNkk13HuYu72zNuY3qL2GC7oSadR8vTbRXZg +KQqfaO0IGRcdkSFTq/AEhSSqr2Ld5nPadMbKvSGrSCc1s8rFH97jRVQY56yhM7ti +IW4+6cE8ylCMbdYB6wuduK/GIgNpqoF4xs1i2XojAoGACacBUMPLEH4Kny8TupOk +wi4pgTdMVVxVcAoC3yyincWJbRbfRm99Y79cCBHcYFdmsGJXawU0gUtlN/5KqgRQ +PfNQtGV7p1I12XGTakdmDrZwai8sXao52TlNpJgGU9siBRGicfZU5cQFi9he/WPY +57XshDJ/v8DidkigRysrdT0CgYEA5iuO22tblC+KvK1dGOXeZWO+DhrfwuGlcFBp +CaimB2/w/8vsn2VVTG9yujo2E6hj1CQw1mDrfG0xRim4LTXOgpbfugwRqvuTUmo2 +Ur21XEX2RhjwpEfhcACWxB4fMUG0krrniMA2K6axupi1/KNpQi6bYe3UdFCs8Wld +QSAOAvsCgYBk/X5PmD44DvndE5FShM2w70YOoMr3Cgl5sdwAFUFE9yDuC14UhVxk +oxnYxwtVI9uVVirET+LczP9JEvcvxnN/Xg3tH/qm0WlIxmTxyYrFFIK9j0rqeu9z +blPu56OzNI2VMrR1GbOBLxQINLTIpaacjNJAlr8XOlegdUJsW/Jwqw== +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_authn/test/data/certs/key.pem b/apps/emqx_authn/test/data/certs/key.pem deleted file mode 100644 index 6c338216e..000000000 --- a/apps/emqx_authn/test/data/certs/key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAs15ZPekT5AV+JEnMqacAxStajcraP9Obf5eeXpYvPrv/Stxi -sltsq9Le7JE/+y1fJcQrD0J6nJDVWIFUWRYCjLypAQ5YUOxlz/lTOFdSdvotevep -OQUSM2aGP7u7O/+VsHGVQbU2VjNJ44Hr9FPzMf+WE54qEudZg9d1cqxrUUvqKPhf -wN4M7WRjt+8AaYGf9MeHW5OkOLMzhiZ4j7vh2ZrIDnFe8BG17mHhW7lIjN7uILTn -s36/KZOgyTYmCT5lkWnJeuer7KjpFoPcA1yWtzgVt5RBnlrmzlLGCz1rRjdYwb7t -zlWdVdxuMFHP9qrY206dBCOKQopADwYXTzQFCQIDAQABAoIBAQCuvCbr7Pd3lvI/ -n7VFQG+7pHRe1VKwAxDkx2t8cYos7y/QWcm8Ptwqtw58HzPZGWYrgGMCRpzzkRSF -V9g3wP1S5Scu5C6dBu5YIGc157tqNGXB+SpdZddJQ4Nc6yGHXYERllT04ffBGc3N -WG/oYS/1cSteiSIrsDy/91FvGRCi7FPxH3wIgHssY/tw69s1Cfvaq5lr2NTFzxIG -xCvpJKEdSfVfS9I7LYiymVjst3IOR/w76/ZFY9cRa8ZtmQSWWsm0TUpRC1jdcbkm -ZoJptYWlP+gSwx/fpMYftrkJFGOJhHJHQhwxT5X/ajAISeqjjwkWSEJLwnHQd11C -Zy2+29lBAoGBANlEAIK4VxCqyPXNKfoOOi5dS64NfvyH4A1v2+KaHWc7lqaqPN49 -ezfN2n3X+KWx4cviDD914Yc2JQ1vVJjSaHci7yivocDo2OfZDmjBqzaMp/y+rX1R -/f3MmiTqMa468rjaxI9RRZu7vDgpTR+za1+OBCgMzjvAng8dJuN/5gjlAoGBANNY -uYPKtearBmkqdrSV7eTUe49Nhr0XotLaVBH37TCW0Xv9wjO2xmbm5Ga/DCtPIsBb -yPeYwX9FjoasuadUD7hRvbFu6dBa0HGLmkXRJZTcD7MEX2Lhu4BuC72yDLLFd0r+ -Ep9WP7F5iJyagYqIZtz+4uf7gBvUDdmvXz3sGr1VAoGAdXTD6eeKeiI6PlhKBztF -zOb3EQOO0SsLv3fnodu7ZaHbUgLaoTMPuB17r2jgrYM7FKQCBxTNdfGZmmfDjlLB -0xZ5wL8ibU30ZXL8zTlWPElST9sto4B+FYVVF/vcG9sWeUUb2ncPcJ/Po3UAktDG -jYQTTyuNGtSJHpad/YOZctkCgYBtWRaC7bq3of0rJGFOhdQT9SwItN/lrfj8hyHA -OjpqTV4NfPmhsAtu6j96OZaeQc+FHvgXwt06cE6Rt4RG4uNPRluTFgO7XYFDfitP -vCppnoIw6S5BBvHwPP+uIhUX2bsi/dm8vu8tb+gSvo4PkwtFhEr6I9HglBKmcmog -q6waEQKBgHyecFBeM6Ls11Cd64vborwJPAuxIW7HBAFj/BS99oeG4TjBx4Sz2dFd -rzUibJt4ndnHIvCN8JQkjNG14i9hJln+H3mRss8fbZ9vQdqG+2vOWADYSzzsNI55 -RFY7JjluKcVkp/zCDeUxTU3O6sS+v6/3VE11Cob6OYQx3lN5wrZ3 ------END RSA PRIVATE KEY----- diff --git a/apps/emqx_authn/test/data/certs/server.crt b/apps/emqx_authn/test/data/certs/server.crt new file mode 100644 index 000000000..1fe7a516c --- /dev/null +++ b/apps/emqx_authn/test/data/certs/server.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEBDCCAeygAwIBAgIJAKTICmq1Lg6cMA0GCSqGSIb3DQEBCwUAMDQxEjAQBgNV +BAoMCUVNUVggVGVzdDEeMBwGA1UEAwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X +DTIxMTIzMDA4NDExMloXDTQ5MDUxNzA4NDExMlowKzESMBAGA1UECgwJRU1RWCBU +ZXN0MRUwEwYDVQQDDAxhdXRobi1zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCdCXfM/j28fsi3vhxmHoy2UUz/VDcTJudadVNqTOQZPuqW5lex +309yYcZqThfT2ZSVIH92ags6aNxr4Uv9vGTkPW22kAiK41imeAj+HLmvByxqfv+s +JlB5YcHXMGQCcFZOaOtabuJ0nmqxO0OWU9CIeE5PWlnVyWM1cvYxtQQLg4BSP8X/ +ohFBERaBn0yU0IYTFxo+9A1LB5utnWiv7A/5fZVFBkAdrGMPxcuEF49oynbW4WpN +kn1jY+89BrBvLk+lMZCTI2dRnE5tqt+kD6Ejh3eWRiONoS6sm9rIrH/OMEqEXhfi +bgZZu8rL0o1YL7SATJERBNuvcJpQl7We5UCbAgMBAAGjIjAgMAsGA1UdDwQEAwIF +oDARBglghkgBhvhCAQEEBAMCBkAwDQYJKoZIhvcNAQELBQADggIBAAydWowM0rS5 +CgrVsuSUnUntXkIIu9YziI8mKWm8K5sp8lqtVovitVFuG19Y3Ve8r2pIibpBvOKZ +ocr+uUgrZrGGXU3x9/p+miTcHm5M9guPzmN6JbKZ65yIAN9po5CjrczFShqxIQly +ye+5C7/Metf6KM43lLKefDkUgccASKa4KhvP84/Jc8jEKP2cQ5I84yaRyeJgDnJ0 +XY6Nu1yn1BLrw9dq5ZcoBYR94aVPnSR63zE58cJ99r8AOSk/Tl7phKNAS7mP94NH +RVTW4R/xGMT/iVz4x9exfeVfAX5fVAPIOXV5VKownmM/WfhICHxNLi++m9nO9sn6 +tHT+3ViYUbilhcPlXVgTiVWJrFuoxbPTON4yIxgT3VQz47Oqnx37jeufbb7bGiJW +H/GEtn5pDPbiHbu6j+GK98uTN7OoTM5L81nbct6evEz6sK2T5Ve5Ro2IWWeG7xlB +3+FIK1pzl5OHpLJTED/DKNxt1qlhnjTGSz902fBORYvTCTdpSfGnrUMjJOP0rGHH +81WFMfc6ucsN4zGXVHHUNuNaUp1HprUy4g7ipTXkRn9oyOXkYKMGMX9T2aUeEnXO +U9ij61TrGA+lZENsbFKD/UcLRr4GY21TKj9dKjKyIoru/qDHrtJkSObQlcgOwS7D +ctaGcj4es0ByT2PX/mDqJoMip3E4E11O +-----END CERTIFICATE----- diff --git a/apps/emqx_authn/test/data/certs/server.key b/apps/emqx_authn/test/data/certs/server.key new file mode 100644 index 000000000..649b69428 --- /dev/null +++ b/apps/emqx_authn/test/data/certs/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAnQl3zP49vH7It74cZh6MtlFM/1Q3EybnWnVTakzkGT7qluZX +sd9PcmHGak4X09mUlSB/dmoLOmjca+FL/bxk5D1ttpAIiuNYpngI/hy5rwcsan7/ +rCZQeWHB1zBkAnBWTmjrWm7idJ5qsTtDllPQiHhOT1pZ1cljNXL2MbUEC4OAUj/F +/6IRQREWgZ9MlNCGExcaPvQNSwebrZ1or+wP+X2VRQZAHaxjD8XLhBePaMp21uFq +TZJ9Y2PvPQawby5PpTGQkyNnUZxObarfpA+hI4d3lkYjjaEurJvayKx/zjBKhF4X +4m4GWbvKy9KNWC+0gEyREQTbr3CaUJe1nuVAmwIDAQABAoIBABl6dMZ8pXWUuGof +XSowYLIf5Lc0aa8gy76AdKU1jniOHa+X9bh1O8WaGYAb5X/IuHOtjyCeOe4jH0gd +iJ/FVjU1xjwtiEVId5SiuwrHjFTafBlXO5IpsTrQYovQXRmMMmSMX0sP3IwBO9w/ +ekrElHvf0QzM4vBtuTvtyAXukZZwYWvdJK8GXc7NE0xTNSe0C+f2MS0ZAuWP7g8K +1WgRO+8pb11sK4CAl+yD6Lyf7JVlouTcsYdeRF5o7yuEQ2qlz3+vxPwfMIpONKel +kK5nUUc8OGhHQpkO+ZZXh5fIWkaKFJKMzoAh8pj2HFAfK93s64f3LHu75sum05Gc +RUCSafkCgYEAympLWe+cmq8XyUqQnsQ7hHfc5VKa33YDTEkO/ZncnnNA/k4yH+r7 +LGgMZD1zC5R3pRFEET9pUOrlx7Z489Bc1Z9Y+9dDpwg9DRrvkt1/MpxOI2Lk8tiJ +lLU/uRTQXQmHFoEBg6i2CDIZyP/qccCS0zIcMQJDq6WaTfXyJ5k0LOUCgYEAxpvi +l7t9RPIQXTEfWiD3iN11QwZYjZ3c6CfW2iaucPYJZDclk6BO1Chdw55cELbfj4bh +7lMxDYpyOQrEwIXYk1a8IY6VOFFMmOQfCfECm5XNTvz//5vYxYlB8ERdhM7opAYG +YsAyR/+BVEyhG6NXy4sh5Q49YgfrjVMdYmBSX38CgYEAx2BF0lNzNOXsjwgURV5S +pZuPCI8CH8PVYcnAq0lnhudNiHArbUb+mvHt6rqgXDKkWwITws1sBhkptjrlDnsZ +Rg3MD1wsthUmVYdHnajxBj/xs2dQzmc9tS2Gk96Nkma1GhR+EloW2yHGRjbVjbA6 +ry53mEp7r1HSGKJ+IEUGoIUCgYEAiRS7FyNPWTECXnAzRZAPiiXgc7yDjmtxN8OX +pcahDFKlNMhjZTt2bTTXUteQj/DI6VWdx1MgPkpagEiQeJlpXHi3LSoukEp85eI+ +EiyJMj35ERXK0/ALdHxCSMXHDo2JQPzvl2U0z0DpUPf7Ewpw5IpJgMGNWIZC7K57 +T5VQBZ0CgYAcAG1KYZYD+Sb14jJLSD6JqnJBrcv8e6wEAnA+0vuEv09FfgeB4MNZ +FwRR8FQDL8V2QcvsauwcwNOf9m9K8goCV9YKTcFw5Tl0m3uYzCIDVdyZI85NgBS0 +m//eODmUYg1gMOi9LfnKgtrW7EURrCNj3Pgt87g7WDiSY+qGB0IzzQ== +-----END RSA PRIVATE KEY----- diff --git a/apps/emqx_authn/test/emqx_authn_http_SUITE.erl b/apps/emqx_authn/test/emqx_authn_http_SUITE.erl index 2c0716e8b..b52588124 100644 --- a/apps/emqx_authn/test/emqx_authn_http_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_http_SUITE.erl @@ -57,11 +57,11 @@ init_per_testcase(_Case, Config) -> emqx_authn_test_lib:delete_authenticators( [authentication], ?GLOBAL), - emqx_authn_http_test_server:start(?HTTP_PORT, ?HTTP_PATH), + {ok, _} = emqx_authn_http_test_server:start_link(?HTTP_PORT, ?HTTP_PATH), Config. end_per_testcase(_Case, _Config) -> - ok = emqx_authn_http_test_server:stop() . + ok = emqx_authn_http_test_server:stop(). %%------------------------------------------------------------------------------ %% Tests @@ -118,7 +118,7 @@ test_user_auth(#{handler := Handler, ?PATH, {create_authenticator, ?GLOBAL, AuthConfig}), - emqx_authn_http_test_server:set_handler(Handler), + ok = emqx_authn_http_test_server:set_handler(Handler), ?assertEqual(Result, emqx_access_control:authenticate(?CREDENTIALS)), @@ -221,15 +221,15 @@ test_is_superuser({Kind, Value, ExpectedValue}) -> iolist_to_binary([<<"is_superuser=">>, Value])} end, - emqx_authn_http_test_server:set_handler( - fun(Req0, State) -> - Req = cowboy_req:reply( - 200, - #{<<"content-type">> => ContentType}, - Res, - Req0), - {ok, Req, State} - end), + ok = emqx_authn_http_test_server:set_handler( + fun(Req0, State) -> + Req = cowboy_req:reply( + 200, + #{<<"content-type">> => ContentType}, + Res, + Req0), + {ok, Req, State} + end), ?assertMatch( {ok, #{is_superuser := ExpectedValue}}, diff --git a/apps/emqx_authn/test/emqx_authn_http_test_server.erl b/apps/emqx_authn/test/emqx_authn_http_test_server.erl index 4896a8b13..2e88dc83b 100644 --- a/apps/emqx_authn/test/emqx_authn_http_test_server.erl +++ b/apps/emqx_authn/test/emqx_authn_http_test_server.erl @@ -16,20 +16,18 @@ -module(emqx_authn_http_test_server). --behaviour(gen_server). +-behaviour(supervisor). -behaviour(cowboy_handler). % cowboy_server callbacks -export([init/2]). -% gen_server callbacks --export([init/1, - handle_call/3, - handle_cast/2 - ]). +% supervisor callbacks +-export([init/1]). % API --export([start/2, +-export([start_link/2, + start_link/3, stop/0, set_handler/1 ]). @@ -38,52 +36,70 @@ %% API %%------------------------------------------------------------------------------ -start(Port, Path) -> - Dispatch = cowboy_router:compile([ - {'_', [{Path, ?MODULE, []}]} - ]), - {ok, _} = cowboy:start_clear(?MODULE, - [{port, Port}], - #{env => #{dispatch => Dispatch}} - ), - {ok, _} = gen_server:start_link({local, ?MODULE}, ?MODULE, [], []), - ok. +start_link(Port, Path) -> + start_link(Port, Path, false). + +start_link(Port, Path, SSLOpts) -> + supervisor:start_link({local, ?MODULE}, ?MODULE, [Port, Path, SSLOpts]). stop() -> - gen_server:stop(?MODULE), - cowboy:stop_listener(?MODULE). + gen_server:stop(?MODULE). set_handler(F) when is_function(F, 2) -> - gen_server:call(?MODULE, {set_handler, F}). + true = ets:insert(?MODULE, {handler, F}), + ok. %%------------------------------------------------------------------------------ -%% gen_server API +%% supervisor API %%------------------------------------------------------------------------------ -init([]) -> - F = fun(Req0, State) -> - Req = cowboy_req:reply( - 400, - #{<<"content-type">> => <<"text/plain">>}, - <<"">>, - Req0), - {ok, Req, State} - end, - {ok, F}. +init([Port, Path, SSLOpts]) -> + Dispatch = cowboy_router:compile( + [ + {'_', [{Path, ?MODULE, []}]} + ]), + + ProtoOpts = #{env => #{dispatch => Dispatch}}, -handle_cast(_, F) -> - {noreply, F}. + Tab = ets:new(?MODULE, [set, named_table, public]), + ets:insert(Tab, {handler, fun default_handler/2}), -handle_call({set_handler, F}, _From, _F) -> - {reply, ok, F}; + {Transport, TransOpts, CowboyModule} = transport_settings(Port, SSLOpts), -handle_call(get_handler, _From, F) -> - {reply, F, F}. + ChildSpec = ranch:child_spec(?MODULE, Transport, TransOpts, CowboyModule, ProtoOpts), + + {ok, {#{}, [ChildSpec]}}. %%------------------------------------------------------------------------------ %% cowboy_server API %%------------------------------------------------------------------------------ init(Req, State) -> - Handler = gen_server:call(?MODULE, get_handler), + [{handler, Handler}] = ets:lookup(?MODULE, handler), Handler(Req, State). + +%%------------------------------------------------------------------------------ +%% Internal functions +%%------------------------------------------------------------------------------ + +transport_settings(Port, false) -> + TransOpts = #{socket_opts => [{port, Port}], + connection_type => supervisor}, + {ranch_tcp, TransOpts, cowboy_clear}; + +transport_settings(Port, SSLOpts) -> + TransOpts = #{socket_opts => [{port, Port}, + {next_protocols_advertised, [<<"h2">>, <<"http/1.1">>]}, + {alpn_preferred_protocols, [<<"h2">>, <<"http/1.1">>]} + | SSLOpts], + connection_type => supervisor}, + {ranch_ssl, TransOpts, cowboy_tls}. + +default_handler(Req0, State) -> + Req = cowboy_req:reply( + 400, + #{<<"content-type">> => <<"text/plain">>}, + <<"">>, + Req0), + {ok, Req, State}. + diff --git a/apps/emqx_authn/test/emqx_authn_https_SUITE.erl b/apps/emqx_authn/test/emqx_authn_https_SUITE.erl new file mode 100644 index 000000000..af3982280 --- /dev/null +++ b/apps/emqx_authn/test/emqx_authn_https_SUITE.erl @@ -0,0 +1,161 @@ +%%-------------------------------------------------------------------- +%% 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_authn_https_SUITE). + +-compile(nowarn_export_all). +-compile(export_all). + +-include("emqx_authn.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("emqx/include/emqx_placeholder.hrl"). + +-define(PATH, [?CONF_NS_ATOM]). + +-define(HTTPS_PORT, 33333). +-define(HTTPS_PATH, "/auth"). +-define(CREDENTIALS, #{username => <<"plain">>, + password => <<"plain">>, + listener => 'tcp:default', + protocol => mqtt + }). + + +all() -> + emqx_common_test_helpers:all(?MODULE). + +init_per_suite(Config) -> + _ = application:load(emqx_conf), + emqx_common_test_helpers:start_apps([emqx_authn]), + application:ensure_all_started(cowboy), + Config. + +end_per_suite(_) -> + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + emqx_common_test_helpers:stop_apps([emqx_authn]), + application:stop(cowboy), + ok. + +init_per_testcase(_Case, Config) -> + {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + {ok, _} = emqx_authn_http_test_server:start_link(?HTTPS_PORT, ?HTTPS_PATH, server_ssl_opts()), + ok = emqx_authn_http_test_server:set_handler(fun cowboy_handler/2), + Config. + +end_per_testcase(_Case, _Config) -> + ok = emqx_authn_http_test_server:stop(). + +%%------------------------------------------------------------------------------ +%% Tests +%%------------------------------------------------------------------------------ + +t_create(_Config) -> + {ok, _} = create_https_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]}), + + ?assertMatch( + {ok, _}, + emqx_access_control:authenticate(?CREDENTIALS)). + +t_create_invalid_domain(_Config) -> + {ok, _} = create_https_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]}), + + ?assertEqual( + {error, not_authorized}, + emqx_access_control:authenticate(?CREDENTIALS)). + +t_create_invalid_version(_Config) -> + {ok, _} = create_https_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>]}), + + ?assertEqual( + {error, not_authorized}, + emqx_access_control:authenticate(?CREDENTIALS)). + +t_create_invalid_ciphers(_Config) -> + {ok, _} = create_https_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-ECDSA-AES256-SHA384">>]}), + + ?assertEqual( + {error, not_authorized}, + emqx_access_control:authenticate(?CREDENTIALS)). + +%%------------------------------------------------------------------------------ +%% Helpers +%%------------------------------------------------------------------------------ + +create_https_auth_with_ssl_opts(SpecificSSLOpts) -> + AuthConfig = raw_https_auth_config(SpecificSSLOpts), + emqx:update_config(?PATH, {create_authenticator, ?GLOBAL, AuthConfig}). + +raw_https_auth_config(SpecificSSLOpts) -> + SSLOpts = maps:merge( + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>}), + #{ + mechanism => <<"password-based">>, + enable => <<"true">>, + + backend => <<"http">>, + method => <<"get">>, + url => <<"https://127.0.0.1:33333/auth">>, + body => #{<<"username">> => ?PH_USERNAME, <<"password">> => ?PH_PASSWORD}, + headers => #{<<"X-Test-Header">> => <<"Test Value">>}, + ssl => maps:merge(SSLOpts, SpecificSSLOpts) + }. + +start_apps(Apps) -> + lists:foreach(fun application:ensure_all_started/1, Apps). + +stop_apps(Apps) -> + lists:foreach(fun application:stop/1, Apps). + +cert_path(FileName) -> + Dir = code:lib_dir(emqx_authn, test), + filename:join([Dir, <<"data/certs">>, FileName]). + +cowboy_handler(Req0, State) -> + Req = cowboy_req:reply( + 200, + Req0), + {ok, Req, State}. + +server_ssl_opts() -> + [{keyfile, cert_path("server.key")}, + {certfile, cert_path("server.crt")}, + {cacertfile, cert_path("ca.crt")}, + {verify, verify_none}, + {versions, ['tlsv1.2', 'tlsv1.3']}, + {ciphers, ["ECDHE-RSA-AES256-GCM-SHA384", "TLS_CHACHA20_POLY1305_SHA256"]} + ]. diff --git a/apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl b/apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl index 0a166616a..dd2ec670e 100644 --- a/apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl @@ -23,8 +23,6 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("snabbkaffe/include/snabbkaffe.hrl"). --include("emqx_authn.hrl"). - -define(AUTHN_ID, <<"mechanism:jwt">>). -define(JWKS_PORT, 33333). @@ -156,7 +154,7 @@ t_jwt_authenticator_public_key(_) -> ok. t_jwks_renewal(_Config) -> - ok = emqx_authn_http_test_server:start(?JWKS_PORT, ?JWKS_PATH), + {ok, _} = emqx_authn_http_test_server:start_link(?JWKS_PORT, ?JWKS_PATH, server_ssl_opts()), ok = emqx_authn_http_test_server:set_handler(fun jwks_handler/2), PrivateKey = test_rsa_key(private), @@ -164,45 +162,63 @@ t_jwks_renewal(_Config) -> JWS = generate_jws('public-key', Payload, PrivateKey), Credential = #{username => <<"myuser">>, password => JWS}, + + BadConfig0 = #{mechanism => jwt, + algorithm => 'public-key', + ssl => #{enable => false}, + verify_claims => [], - BadConfig = #{mechanism => jwt, - algorithm => 'public-key', - ssl => #{enable => false}, - verify_claims => [], - - use_jwks => true, - endpoint => "http://127.0.0.1:" ++ integer_to_list(?JWKS_PORT + 1) ++ ?JWKS_PATH, - refresh_interval => 1000 - }, + use_jwks => true, + endpoint => "https://127.0.0.1:" ++ integer_to_list(?JWKS_PORT + 1) ++ ?JWKS_PATH, + refresh_interval => 1000 + }, ok = snabbkaffe:start_trace(), {{ok, State0}, _} = ?wait_async_action( - emqx_authn_jwt:create(?AUTHN_ID, BadConfig), + emqx_authn_jwt:create(?AUTHN_ID, BadConfig0), #{?snk_kind := jwks_endpoint_response}, - 1000), + 10000), ok = snabbkaffe:stop(), ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential, State0)), ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State0)), - GoodConfig = BadConfig#{endpoint => - "http://127.0.0.1:" ++ integer_to_list(?JWKS_PORT) ++ ?JWKS_PATH}, + ClientSSLOpts = client_ssl_opts(), + BadClientSSLOpts = ClientSSLOpts#{server_name_indication => "authn-server-unknown-host"}, + + BadConfig1 = BadConfig0#{endpoint => + "https://127.0.0.1:" ++ integer_to_list(?JWKS_PORT) ++ ?JWKS_PATH, + ssl => BadClientSSLOpts}, ok = snabbkaffe:start_trace(), {{ok, State1}, _} = ?wait_async_action( - emqx_authn_jwt:update(GoodConfig, State0), + emqx_authn_jwt:create(?AUTHN_ID, BadConfig1), #{?snk_kind := jwks_endpoint_response}, - 1000), + 10000), ok = snabbkaffe:stop(), - ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential, State1)), - ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State1)), + ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential, State1)), + ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State0)), - ?assertEqual(ok, emqx_authn_jwt:destroy(State1)), + GoodConfig = BadConfig1#{ssl => ClientSSLOpts}, + + ok = snabbkaffe:start_trace(), + + {{ok, State2}, _} = ?wait_async_action( + emqx_authn_jwt:update(GoodConfig, State1), + #{?snk_kind := jwks_endpoint_response}, + 10000), + + ok = snabbkaffe:stop(), + + ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential, State2)), + ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State2)), + + ?assertEqual(ok, emqx_authn_jwt:destroy(State2)), ok = emqx_authn_http_test_server:stop(). %%------------------------------------------------------------------------------ @@ -220,12 +236,17 @@ jwks_handler(Req0, State) -> {ok, Req, State}. test_rsa_key(public) -> - Dir = code:lib_dir(emqx_authn, test), - list_to_binary(filename:join([Dir, "data/public_key.pem"])); + data_file("public_key.pem"); test_rsa_key(private) -> + data_file("private_key.pem"). + +data_file(Name) -> Dir = code:lib_dir(emqx_authn, test), - list_to_binary(filename:join([Dir, "data/private_key.pem"])). + list_to_binary(filename:join([Dir, "data", Name])). + +cert_file(Name) -> + data_file(filename:join(["certs", Name])). generate_jws('hmac-based', Payload, Secret) -> JWK = jose_jwk:from_oct(Secret), @@ -243,3 +264,18 @@ generate_jws('public-key', Payload, PrivateKey) -> Signed = jose_jwt:sign(JWK, Header, Payload), {_, JWS} = jose_jws:compact(Signed), JWS. + +client_ssl_opts() -> + maps:merge( + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => true, + verify => verify_peer, + server_name_indication => "authn-server" + }). + +server_ssl_opts() -> + [{keyfile, cert_file("server.key")}, + {certfile, cert_file("server.crt")}, + {cacertfile, cert_file("ca.crt")}, + {verify, verify_none} + ]. diff --git a/apps/emqx_authn/test/emqx_authn_mongo_tls_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mongo_tls_SUITE.erl new file mode 100644 index 000000000..e62f895a2 --- /dev/null +++ b/apps/emqx_authn/test/emqx_authn_mongo_tls_SUITE.erl @@ -0,0 +1,185 @@ +%%-------------------------------------------------------------------- +%% 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_authn_mongo_tls_SUITE). + +-compile(nowarn_export_all). +-compile(export_all). + +-include("emqx_authn.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). +-include_lib("snabbkaffe/include/snabbkaffe.hrl"). + + +-define(MONGO_HOST, "mongo-tls"). +-define(MONGO_PORT, 27017). + +-define(PATH, [authentication]). + +all() -> + emqx_common_test_helpers:all(?MODULE). + +init_per_testcase(_TestCase, Config) -> + {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), + emqx_authentication:initialize_authentication(?GLOBAL, []), + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + Config. + +init_per_suite(Config) -> + _ = application:load(emqx_conf), + case emqx_authn_test_lib:is_tcp_server_available(?MONGO_HOST, ?MONGO_PORT) of + true -> + ok = emqx_common_test_helpers:start_apps([emqx_authn]), + ok = start_apps([emqx_resource, emqx_connector]), + Config; + false -> + {skip, no_mongo} + end. + +end_per_suite(_Config) -> + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + ok = stop_apps([emqx_resource, emqx_connector]), + ok = emqx_common_test_helpers:stop_apps([emqx_authn]). + +%%------------------------------------------------------------------------------ +%% Tests +%%------------------------------------------------------------------------------ + +%% emqx_connector_mongo connects asyncronously, +%% so we check failure/success indirectly (through snabbkaffe). + +%% openssl s_client -tls1_2 -cipher ECDHE-RSA-AES256-GCM-SHA384 \ +%% -connect mongo-tls:27017 \ +%% -cert client.crt -key client.key -CAfile ca.crt + +t_create(_Config) -> + ?check_trace( + create_mongo_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]}), + fun({ok, _}, Trace) -> + ?assertEqual( + [ok], + ?projection( + status, + ?of_kind(emqx_connector_mongo_health_check, Trace))) + end). + + +t_create_invalid_server_name(_Config) -> + ?check_trace( + create_mongo_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">>}), + fun({ok, _}, Trace) -> + ?assertEqual( + [failed], + ?projection( + status, + ?of_kind(emqx_connector_mongo_health_check, Trace))) + end). + + +%% docker-compose-mongo-single-tls.yaml: +%% --tlsDisabledProtocols TLS1_0,TLS1_1 + +t_create_invalid_version(_Config) -> + ?check_trace( + create_mongo_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>]}), + fun({ok, _}, Trace) -> + ?assertEqual( + [failed], + ?projection( + status, + ?of_kind(emqx_connector_mongo_health_check, Trace))) + end). + + +%% docker-compose-mongo-single-tls.yaml: +%% --setParameter opensslCipherConfig='HIGH:!EXPORT:!aNULL:!DHE:!kDHE@STRENGTH' + +t_invalid_ciphers(_Config) -> + ?check_trace( + create_mongo_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"DHE-RSA-AES256-GCM-SHA384">>]}), + fun({ok, _}, Trace) -> + ?assertEqual( + [failed], + ?projection( + status, + ?of_kind(emqx_connector_mongo_health_check, Trace))) + end). + +%%------------------------------------------------------------------------------ +%% Helpers +%%------------------------------------------------------------------------------ + +create_mongo_auth_with_ssl_opts(SpecificSSLOpts) -> + AuthConfig = raw_mongo_auth_config(SpecificSSLOpts), + emqx:update_config(?PATH, {create_authenticator, ?GLOBAL, AuthConfig}). + +raw_mongo_auth_config(SpecificSSLOpts) -> + SSLOpts = maps:merge( + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>}), + #{ + mechanism => <<"password-based">>, + password_hash_algorithm => #{name => <<"plain">>, + salt_position => <<"suffix">>}, + enable => <<"true">>, + + backend => <<"mongodb">>, + pool_size => 2, + mongo_type => <<"single">>, + database => <<"mqtt">>, + collection => <<"users">>, + server => mongo_server(), + + selector => #{<<"username">> => <<"${username}">>}, + password_hash_field => <<"password_hash">>, + salt_field => <<"salt">>, + is_superuser_field => <<"is_superuser">>, + topology => #{ + server_selection_timeout_ms => <<"10000ms">> + }, + + ssl => maps:merge(SSLOpts, SpecificSSLOpts) + }. + +mongo_server() -> + iolist_to_binary( + io_lib:format( + "~s:~b", + [?MONGO_HOST, ?MONGO_PORT])). + +start_apps(Apps) -> + lists:foreach(fun application:ensure_all_started/1, Apps). + +stop_apps(Apps) -> + lists:foreach(fun application:stop/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl index 95eecdead..659596d39 100644 --- a/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_mysql_SUITE.erl @@ -22,8 +22,6 @@ -include("emqx_authn.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). --include_lib("emqx/include/emqx_placeholder.hrl"). - -define(MYSQL_HOST, "mysql"). -define(MYSQL_PORT, 3306). diff --git a/apps/emqx_authn/test/emqx_authn_mysql_tls_SUITE.erl b/apps/emqx_authn/test/emqx_authn_mysql_tls_SUITE.erl new file mode 100644 index 000000000..1c046e9bc --- /dev/null +++ b/apps/emqx_authn/test/emqx_authn_mysql_tls_SUITE.erl @@ -0,0 +1,144 @@ +%%-------------------------------------------------------------------- +%% 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_authn_mysql_tls_SUITE). + +-compile(nowarn_export_all). +-compile(export_all). + +-include("emqx_authn.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-define(MYSQL_HOST, "mysql-tls"). +-define(MYSQL_PORT, 3306). + +-define(PATH, [authentication]). + +all() -> + emqx_common_test_helpers:all(?MODULE). + +groups() -> + []. + +init_per_testcase(_, Config) -> + {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), + emqx_authentication:initialize_authentication(?GLOBAL, []), + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + Config. + +init_per_suite(Config) -> + _ = application:load(emqx_conf), + case emqx_authn_test_lib:is_tcp_server_available(?MYSQL_HOST, ?MYSQL_PORT) of + true -> + ok = emqx_common_test_helpers:start_apps([emqx_authn]), + ok = start_apps([emqx_resource, emqx_connector]), + Config; + false -> + {skip, no_mysql_tls} + end. + +end_per_suite(_Config) -> + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + ok = stop_apps([emqx_resource, emqx_connector]), + ok = emqx_common_test_helpers:stop_apps([emqx_authn]). + +%%------------------------------------------------------------------------------ +%% Tests +%%------------------------------------------------------------------------------ + +t_create(_Config) -> + %% openssl s_client -tls1_2 -cipher ECDHE-RSA-AES256-GCM-SHA384 \ + %% -connect authn-server:3306 -starttls mysql \ + %% -cert client.crt -key client.key -CAfile ca.crt + ?assertMatch( + {ok, _}, + create_mysql_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]})). + +t_create_invalid(_Config) -> + + %% invalid server_name + ?assertMatch( + {error, _}, + create_mysql_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">>})), + + %% incompatible versions + ?assertMatch( + {error, _}, + create_mysql_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>]})), + + %% incompatible ciphers + ?assertMatch( + {error, _}, + create_mysql_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-ECDSA-AES128-GCM-SHA256">>]})). + +%%------------------------------------------------------------------------------ +%% Helpers +%%------------------------------------------------------------------------------ + +create_mysql_auth_with_ssl_opts(SpecificSSLOpts) -> + AuthConfig = raw_mysql_auth_config(SpecificSSLOpts), + emqx:update_config(?PATH, {create_authenticator, ?GLOBAL, AuthConfig}). + +raw_mysql_auth_config(SpecificSSLOpts) -> + SSLOpts = maps:merge( + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>}), + #{ + mechanism => <<"password-based">>, + password_hash_algorithm => #{name => <<"plain">>, + salt_position => <<"suffix">>}, + enable => <<"true">>, + + backend => <<"mysql">>, + database => <<"mqtt">>, + username => <<"root">>, + password => <<"public">>, + + query => <<"SELECT password_hash, salt, is_superuser_str as is_superuser + FROM users where username = ${username} LIMIT 1">>, + server => mysql_server(), + ssl => maps:merge(SSLOpts, SpecificSSLOpts) + }. + +mysql_server() -> + iolist_to_binary( + io_lib:format( + "~s:~b", + [?MYSQL_HOST, ?MYSQL_PORT])). + +start_apps(Apps) -> + lists:foreach(fun application:ensure_all_started/1, Apps). + +stop_apps(Apps) -> + lists:foreach(fun application:stop/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl b/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl index 8f1f12690..5f1e630c8 100644 --- a/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_pgsql_SUITE.erl @@ -22,7 +22,6 @@ -include("emqx_authn.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). --include_lib("epgsql/include/epgsql.hrl"). -include_lib("emqx/include/emqx_placeholder.hrl"). -define(PGSQL_HOST, "pgsql"). diff --git a/apps/emqx_authn/test/emqx_authn_pgsql_tls_SUITE.erl b/apps/emqx_authn/test/emqx_authn_pgsql_tls_SUITE.erl new file mode 100644 index 000000000..4bf761d62 --- /dev/null +++ b/apps/emqx_authn/test/emqx_authn_pgsql_tls_SUITE.erl @@ -0,0 +1,145 @@ +%%-------------------------------------------------------------------- +%% 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_authn_pgsql_tls_SUITE). + +-compile(nowarn_export_all). +-compile(export_all). + +-include("emqx_authn.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-define(PGSQL_HOST, "pgsql-tls"). +-define(PGSQL_PORT, 5432). + +-define(PATH, [authentication]). + +all() -> + emqx_common_test_helpers:all(?MODULE). + +groups() -> + []. + +init_per_testcase(_, Config) -> + {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), + emqx_authentication:initialize_authentication(?GLOBAL, []), + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + Config. + +init_per_suite(Config) -> + _ = application:load(emqx_conf), + case emqx_authn_test_lib:is_tcp_server_available(?PGSQL_HOST, ?PGSQL_PORT) of + true -> + ok = emqx_common_test_helpers:start_apps([emqx_authn]), + ok = start_apps([emqx_resource, emqx_connector]), + Config; + false -> + {skip, no_pgsql_tls} + end. + +end_per_suite(_Config) -> + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + ok = stop_apps([emqx_resource, emqx_connector]), + ok = emqx_common_test_helpers:stop_apps([emqx_authn]). + +%%------------------------------------------------------------------------------ +%% Tests +%%------------------------------------------------------------------------------ + +t_create(_Config) -> + %% openssl s_client -tls1_2 -cipher ECDHE-RSA-AES256-GCM-SHA384 \ + %% -starttls postgres -connect authn-server:5432 \ + %% -cert client.crt -key client.key -CAfile ca.crt + ?assertMatch( + {ok, _}, + create_pgsql_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-RSA-AES256-GCM-SHA384">>]})). + +t_create_invalid(_Config) -> + + %% invalid server_name + ?assertMatch( + {error, _}, + create_pgsql_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">>})), + + %% incompatible versions + ?assertMatch( + {error, _}, + create_pgsql_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>]})), + + %% incompatible ciphers + ?assertMatch( + {error, _}, + create_pgsql_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.2">>], + <<"ciphers">> => [<<"ECDHE-ECDSA-AES128-GCM-SHA256">>]})). + +%%------------------------------------------------------------------------------ +%% Helpers +%%------------------------------------------------------------------------------ + +create_pgsql_auth_with_ssl_opts(SpecificSSLOpts) -> + AuthConfig = raw_pgsql_auth_config(SpecificSSLOpts), + emqx:update_config(?PATH, {create_authenticator, ?GLOBAL, AuthConfig}). + +raw_pgsql_auth_config(SpecificSSLOpts) -> + SSLOpts = maps:merge( + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>}), + #{ + mechanism => <<"password-based">>, + password_hash_algorithm => #{name => <<"plain">>, + salt_position => <<"suffix">>}, + enable => <<"true">>, + + backend => <<"postgresql">>, + database => <<"mqtt">>, + username => <<"root">>, + password => <<"public">>, + + query => <<"SELECT password_hash, salt, is_superuser_str as is_superuser + FROM users where username = ${username} LIMIT 1">>, + server => pgsql_server(), + ssl => maps:merge(SSLOpts, SpecificSSLOpts) + }. + +pgsql_server() -> + iolist_to_binary( + io_lib:format( + "~s:~b", + [?PGSQL_HOST, ?PGSQL_PORT])). + +start_apps(Apps) -> + lists:foreach(fun application:ensure_all_started/1, Apps). + +stop_apps(Apps) -> + lists:foreach(fun application:stop/1, Apps). + diff --git a/apps/emqx_authn/test/emqx_authn_redis_SUITE.erl b/apps/emqx_authn/test/emqx_authn_redis_SUITE.erl index de556a7bd..c4c7f22cf 100644 --- a/apps/emqx_authn/test/emqx_authn_redis_SUITE.erl +++ b/apps/emqx_authn/test/emqx_authn_redis_SUITE.erl @@ -52,7 +52,7 @@ end_per_group(require_seeds, Config) -> Config. init_per_suite(Config) -> - _ = application:load(emqx_conf), + _ = application:load(emqx_conf), case emqx_authn_test_lib:is_tcp_server_available(?REDIS_HOST, ?REDIS_PORT) of true -> ok = emqx_common_test_helpers:start_apps([emqx_authn]), diff --git a/apps/emqx_authn/test/emqx_authn_redis_tls_SUITE.erl b/apps/emqx_authn/test/emqx_authn_redis_tls_SUITE.erl new file mode 100644 index 000000000..0403482b1 --- /dev/null +++ b/apps/emqx_authn/test/emqx_authn_redis_tls_SUITE.erl @@ -0,0 +1,139 @@ +%%-------------------------------------------------------------------- +%% 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_authn_redis_tls_SUITE). + +-compile(nowarn_export_all). +-compile(export_all). + +-include("emqx_authn.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("common_test/include/ct.hrl"). + +-define(REDIS_HOST, "redis-tls"). +-define(REDIS_PORT, 6380). + +-define(PATH, [authentication]). + +all() -> + emqx_common_test_helpers:all(?MODULE). + +groups() -> + []. + +init_per_testcase(_, Config) -> + {ok, _} = emqx_cluster_rpc:start_link(node(), emqx_cluster_rpc, 1000), + emqx_authentication:initialize_authentication(?GLOBAL, []), + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + Config. + +init_per_suite(Config) -> + _ = application:load(emqx_conf), + case emqx_authn_test_lib:is_tcp_server_available(?REDIS_HOST, ?REDIS_PORT) of + true -> + ok = emqx_common_test_helpers:start_apps([emqx_authn]), + ok = start_apps([emqx_resource, emqx_connector]), + Config; + false -> + {skip, no_redis} + end. + +end_per_suite(_Config) -> + emqx_authn_test_lib:delete_authenticators( + [authentication], + ?GLOBAL), + ok = stop_apps([emqx_resource, emqx_connector]), + ok = emqx_common_test_helpers:stop_apps([emqx_authn]). + +%%------------------------------------------------------------------------------ +%% Tests +%%------------------------------------------------------------------------------ + +t_create(_Config) -> + ?assertMatch( + {ok, _}, + create_redis_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.3">>], + <<"ciphers">> => [<<"TLS_CHACHA20_POLY1305_SHA256">>]})). + +t_create_invalid(_Config) -> + %% invalid server_name + ?assertMatch( + {error, _}, + create_redis_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server-unknown-host">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.3">>], + <<"ciphers">> => [<<"TLS_CHACHA20_POLY1305_SHA256">>]})), + + %% incompatible versions + ?assertMatch( + {error, _}, + create_redis_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.1">>, <<"tlsv1.2">>]})), + + %% incompatible ciphers + ?assertMatch( + {error, _}, + create_redis_auth_with_ssl_opts( + #{<<"server_name_indication">> => <<"authn-server">>, + <<"verify">> => <<"verify_peer">>, + <<"versions">> => [<<"tlsv1.3">>], + <<"ciphers">> => [<<"TLS_AES_128_GCM_SHA256">>]})). + +%%------------------------------------------------------------------------------ +%% Helpers +%%------------------------------------------------------------------------------ + +create_redis_auth_with_ssl_opts(SpecificSSLOpts) -> + AuthConfig = raw_redis_auth_config(SpecificSSLOpts), + emqx:update_config(?PATH, {create_authenticator, ?GLOBAL, AuthConfig}). + +raw_redis_auth_config(SpecificSSLOpts) -> + SSLOpts = maps:merge( + emqx_authn_test_lib:client_ssl_cert_opts(), + #{enable => <<"true">>}), + #{ + mechanism => <<"password-based">>, + password_hash_algorithm => #{name => <<"plain">>, + salt_position => <<"suffix">>}, + enable => <<"true">>, + + backend => <<"redis">>, + cmd => <<"HMGET mqtt_user:${username} password_hash salt is_superuser">>, + database => <<"1">>, + password => <<"public">>, + server => redis_server(), + ssl => maps:merge(SSLOpts, SpecificSSLOpts) + }. + +redis_server() -> + iolist_to_binary( + io_lib:format( + "~s:~b", + [?REDIS_HOST, ?REDIS_PORT])). + +start_apps(Apps) -> + lists:foreach(fun application:ensure_all_started/1, Apps). + +stop_apps(Apps) -> + lists:foreach(fun application:stop/1, Apps). diff --git a/apps/emqx_authn/test/emqx_authn_test_lib.erl b/apps/emqx_authn/test/emqx_authn_test_lib.erl index b14821a9c..fb411d131 100644 --- a/apps/emqx_authn/test/emqx_authn_test_lib.erl +++ b/apps/emqx_authn/test/emqx_authn_test_lib.erl @@ -66,3 +66,8 @@ is_tcp_server_available(Host, Port) -> false end. +client_ssl_cert_opts() -> + Dir = code:lib_dir(emqx_authn, test), + #{keyfile => filename:join([Dir, "data/certs", "client.key"]), + certfile => filename:join([Dir, "data/certs", "client.crt"]), + cacertfile => filename:join([Dir, "data/certs", "ca.crt"])}. diff --git a/apps/emqx_connector/rebar.config b/apps/emqx_connector/rebar.config index f919fc943..561b22329 100644 --- a/apps/emqx_connector/rebar.config +++ b/apps/emqx_connector/rebar.config @@ -9,7 +9,7 @@ {mysql, {git, "https://github.com/emqx/mysql-otp", {tag, "1.7.1"}}}, {epgsql, {git, "https://github.com/emqx/epgsql", {tag, "4.6.0"}}}, %% NOTE: mind poolboy version when updating mongodb-erlang version - {mongodb, {git,"https://github.com/emqx/mongodb-erlang", {tag, "v3.0.10"}}}, + {mongodb, {git,"https://github.com/emqx/mongodb-erlang", {tag, "v3.0.11"}}}, %% NOTE: mind poolboy version when updating eredis_cluster version {eredis_cluster, {git, "https://github.com/emqx/eredis_cluster", {tag, "0.6.7"}}}, %% mongodb-erlang uses a special fork https://github.com/comtihon/poolboy.git diff --git a/apps/emqx_connector/src/emqx_connector_http.erl b/apps/emqx_connector/src/emqx_connector_http.erl index 2b9bd48aa..55de39ef5 100644 --- a/apps/emqx_connector/src/emqx_connector_http.erl +++ b/apps/emqx_connector/src/emqx_connector_http.erl @@ -203,10 +203,12 @@ on_query(InstId, {KeyOrNum, Method, Request, Timeout}, AfterQuery, request => Request, connector => InstId, state => State}), NRequest = update_path(BasePath, Request), - case Result = ehttpc:request(case KeyOrNum of - undefined -> PoolName; - _ -> {PoolName, KeyOrNum} - end, Method, NRequest, Timeout) of + Name = case KeyOrNum of + undefined -> PoolName; + _ -> {PoolName, KeyOrNum} + end, + Result = ehttpc:request(Name, Method, NRequest, Timeout), + case Result of {error, Reason} -> ?SLOG(error, #{msg => "http connector do reqeust failed", request => NRequest, reason => Reason, diff --git a/apps/emqx_connector/src/emqx_connector_mongo.erl b/apps/emqx_connector/src/emqx_connector_mongo.erl index 6a1b15e57..4eb8db611 100644 --- a/apps/emqx_connector/src/emqx_connector_mongo.erl +++ b/apps/emqx_connector/src/emqx_connector_mongo.erl @@ -18,6 +18,7 @@ -include("emqx_connector.hrl"). -include_lib("typerefl/include/types.hrl"). -include_lib("emqx/include/logger.hrl"). +-include_lib("snabbkaffe/include/snabbkaffe.hrl"). -type server() :: emqx_schema:ip_port(). -reflect_type([server/0]). @@ -37,7 +38,7 @@ -export([roots/0, fields/1]). --export([mongo_query/5]). +-export([mongo_query/5, check_worker_health/1]). %%===================================================================== roots() -> @@ -158,28 +159,39 @@ on_query(InstId, end. -dialyzer({nowarn_function, [on_health_check/2]}). -on_health_check(_InstId, #{poolname := PoolName} = State) -> +on_health_check(InstId, #{poolname := PoolName} = State) -> case health_check(PoolName) of - true -> {ok, State}; - false -> {error, health_check_failed, State} + true -> + ?tp(debug, emqx_connector_mongo_health_check, #{instance_id => InstId, + status => ok}), + {ok, State}; + false -> + ?tp(warning, emqx_connector_mongo_health_check, #{instance_id => InstId, + status => failed}), + {error, health_check_failed, State} end. health_check(PoolName) -> - Status = [begin - case ecpool_worker:client(Worker) of - {ok, Conn} -> - %% we don't care if this returns something or not, we just to test the connection - try mongo_api:find_one(Conn, <<"foo">>, {}, #{}) of - _ -> true - catch - _Class:_Error -> false - end; - _ -> false - end - end || {_WorkerName, Worker} <- ecpool:workers(PoolName)], - length(Status) > 0 andalso lists:all(fun(St) -> St =:= true end, Status). + Workers = [Worker || {_WorkerName, Worker} <- ecpool:workers(PoolName)], + Status = rpc:pmap({?MODULE, check_worker_health}, [], Workers), + length(Status) > 0 andalso lists:all(fun(St) -> St end, Status). %% =================================================================== + +check_worker_health(Worker) -> + case ecpool_worker:client(Worker) of + {ok, Conn} -> + %% we don't care if this returns something or not, we just to test the connection + try mongo_api:find_one(Conn, <<"foo">>, #{}, #{}) of + {error, _} -> false; + _ -> + true + catch + _Class:_Error -> false + end; + _ -> false + end. + connect(Opts) -> Type = proplists:get_value(mongo_type, Opts, single), Hosts = proplists:get_value(hosts, Opts, []), diff --git a/apps/emqx_connector/src/emqx_connector_mysql.erl b/apps/emqx_connector/src/emqx_connector_mysql.erl index c93a1e350..fad28232b 100644 --- a/apps/emqx_connector/src/emqx_connector_mysql.erl +++ b/apps/emqx_connector/src/emqx_connector_mysql.erl @@ -60,8 +60,7 @@ on_start(InstId, #{server := {Host, Port}, connector => InstId, config => Config}), SslOpts = case maps:get(enable, SSL) of true -> - [{ssl, [{server_name_indication, disable} | - emqx_plugin_libs_ssl:save_files_return_opts(SSL, "connectors", InstId)]}]; + [{ssl, emqx_plugin_libs_ssl:save_files_return_opts(SSL, "connectors", InstId)}]; false -> [] end, Options = [{host, Host}, diff --git a/apps/emqx_connector/src/emqx_connector_pgsql.erl b/apps/emqx_connector/src/emqx_connector_pgsql.erl index f42bed666..2f201ac94 100644 --- a/apps/emqx_connector/src/emqx_connector_pgsql.erl +++ b/apps/emqx_connector/src/emqx_connector_pgsql.erl @@ -59,10 +59,12 @@ on_start(InstId, #{server := {Host, Port}, ?SLOG(info, #{msg => "starting postgresql connector", connector => InstId, config => Config}), SslOpts = case maps:get(enable, SSL) of - true -> - [{ssl, [{server_name_indication, disable} | - emqx_plugin_libs_ssl:save_files_return_opts(SSL, "connectors", InstId)]}]; - false -> [] + true -> + [{ssl, true}, + {ssl_opts, + emqx_plugin_libs_ssl:save_files_return_opts(SSL, "connectors", InstId)}]; + false -> + [{ssl, false}] end, Options = [{host, Host}, {port, Port}, diff --git a/apps/emqx_plugin_libs/src/emqx_plugin_libs_ssl.erl b/apps/emqx_plugin_libs/src/emqx_plugin_libs_ssl.erl index 2bcf66763..6a40abac2 100644 --- a/apps/emqx_plugin_libs/src/emqx_plugin_libs_ssl.erl +++ b/apps/emqx_plugin_libs/src/emqx_plugin_libs_ssl.erl @@ -75,7 +75,7 @@ save_files_return_opts(Options, Dir) -> CA = do_save_file(CAFile, Dir), Verify = GetD(verify, verify_none), SNI = Get(server_name_indication), - Versions = emqx_tls_lib:integral_versions(Get(tls_versions)), + Versions = emqx_tls_lib:integral_versions(Get(versions)), Ciphers = emqx_tls_lib:integral_ciphers(Versions, Get(ciphers)), filter([{keyfile, Key}, {certfile, Cert}, {cacertfile, CA}, {verify, Verify}, {server_name_indication, SNI}, {versions, Versions}, {ciphers, Ciphers}]).