diff --git a/.env b/.env new file mode 100644 index 0000000000..69c917c7e6 --- /dev/null +++ b/.env @@ -0,0 +1,195 @@ +# Docker: Environment variables in Compose + +## NginX +MF_NGINX_HTTP_PORT=80 +MF_NGINX_SSL_PORT=443 +MF_NGINX_MQTT_PORT=1883 +MF_NGINX_MQTTS_PORT=8883 + +## NATS +MF_NATS_URL=nats://nats:4222 + +## Redis +MF_REDIS_TCP_PORT=6379 + +## UI +MF_UI_PORT=3000 + +## Grafana +MF_GRAFANA_PORT=3000 + +## Jaeger +MF_JAEGER_PORT=6831 +MF_JAEGER_FRONTEND=16686 +MF_JAEGER_COLLECTOR=14268 +MF_JAEGER_CONFIGS=5778 +MF_JAEGER_URL=jaeger:6831 + +## Core Services + +### AuthN +MF_AUTHN_LOG_LEVEL=debug +MF_AUTHN_HTTP_PORT=8189 +MF_AUTHN_GRPC_PORT=8181 +MF_AUTHN_DB_PORT=5432 +MF_AUTHN_DB_USER=mainflux +MF_AUTHN_DB_PASS=mainflux +MF_AUTHN_DB=authn +MF_AUTHN_SECRET=secret + +### Users +MF_USERS_LOG_LEVEL=debug +MF_USERS_HTTP_PORT=8180 +MF_USERS_DB_PORT=5432 +MF_USERS_DB_USER=mainflux +MF_USERS_DB_PASS=mainflux +MF_USERS_DB=users + +### Email utility +MF_EMAIL_DRIVER=smtp +MF_EMAIL_HOST=smtp.mailtrap.io +MF_EMAIL_PORT=2525 +MF_EMAIL_USERNAME=18bf7f70705139 +MF_EMAIL_PASSWORD=2b0d302e775b1e +MF_EMAIL_FROM_ADDRESS=from@example.com +MF_EMAIL_FROM_NAME=Example +MF_EMAIL_TEMPLATE=email.tmpl + +### Token utility +MF_TOKEN_RESET_ENDPOINT=/reset-request + +### Things +MF_THINGS_LOG_LEVEL=debug +MF_THINGS_HTTP_PORT=8182 +MF_THINGS_AUTH_HTTP_PORT=8989 +MF_THINGS_AUTH_GRPC_PORT=8183 +MF_THINGS_DB_PORT=5432 +MF_THINGS_DB_USER=mainflux +MF_THINGS_DB_PASS=mainflux +MF_THINGS_DB=things +MF_THINGS_SECRET=secret +MF_THINGS_ES_URL=localhost:6379 +MF_THINGS_ES_PASS= +MF_THINGS_ES_DB=0 + +### WS +MF_WS_ADAPTER_LOG_LEVEL=debug +MF_WS_ADAPTER_PORT=8186 + +### HTTP +MF_HTTP_ADAPTER_PORT=8185 + +### MQTT +MF_MQTT_ADAPTER_LOG_LEVEL=debug +MF_MQTT_ADAPTER_PORT=1883 +MF_MQTT_ADAPTER_WS_PORT=8080 +MF_MQTT_ADAPTER_ES_DB=0 +MF_MQTT_ADAPTER_ES_PASS= + +### CoAP +MF_COAP_ADAPTER_LOG_LEVEL=debug +MF_COAP_ADAPTER_PORT=5683 + +## Addons Services +### Bootstrap +MF_BOOTSTRAP_LOG_LEVEL=debug +MF_BOOTSTRAP_PORT=8202 +MF_BOOTSTRAP_DB_PORT=5432 +MF_BOOTSTRAP_DB_USER=mainflux +MF_BOOTSTRAP_DB_PASS=mainflux +MF_BOOTSTRAP_DB=bootstrap +MF_BOOTSTRAP_DB_SSL_MODE=disable + +### LoRa +MF_LORA_ADAPTER_LOG_LEVEL=debug +MF_LORA_ADAPTER_MESSAGES_URL=tcp://lora.mqtt.mainflux.io:1883 +MF_LORA_ADAPTER_HTTP_PORT=8187 +MF_LORA_ADAPTER_ROUTE_MAP_URL=localhost:6379 +MF_LORA_ADAPTER_ROUTE_MAP_PASS= +MF_LORA_ADAPTER_ROUTE_MAP_DB=0 + +### OPC-UA +MF_OPCUA_ADAPTER_HTTP_PORT=8188 +MF_OPCUA_ADAPTER_LOG_LEVEL=debug +MF_OPCUA_ADAPTER_SERVER_URI=opc.tcp://opcua.rocks:4840 +MF_OPCUA_ADAPTER_NODE_NAMESPACE=0 +MF_OPCUA_ADAPTER_NODE_IDENTIFIER=2256 +MF_OPCUA_ADAPTER_POLICY= +MF_OPCUA_ADAPTER_MODE= +MF_OPCUA_ADAPTER_CERT_FILE= +MF_OPCUA_ADAPTER_KEY_FILE= +MF_OPCUA_ADAPTER_ROUTE_MAP_URL=localhost:6379 +MF_OPCUA_ADAPTER_ROUTE_MAP_PASS= +MF_OPCUA_ADAPTER_ROUTE_MAP_DB=0 +MF_OPCUA_ADAPTER_EVENT_CONSUMER=opcua + +### Cassandra Writer +MF_CASSANDRA_WRITER_LOG_LEVEL=debug +MF_CASSANDRA_WRITER_PORT=8902 +MF_CASSANDRA_WRITER_DB_PORT=9042 +MF_CASSANDRA_WRITER_DB_CLUSTER=mainflux-cassandra +MF_CASSANDRA_WRITER_DB_KEYSPACE=mainflux + +### Cassandra Reader +MF_CASSANDRA_READER_LOG_LEVEL=debug +MF_CASSANDRA_READER_PORT=8903 +MF_CASSANDRA_READER_DB_PORT=9042 +MF_CASSANDRA_READER_DB_CLUSTER=mainflux-cassandra +MF_CASSANDRA_READER_DB_KEYSPACE=mainflux + +### InfluxDB Writer +MF_INFLUX_WRITER_LOG_LEVEL=debug +MF_INFLUX_WRITER_PORT=8900 +MF_INFLUX_WRITER_BATCH_SIZE=5000 +MF_INFLUX_WRITER_BATCH_TIMEOUT=5 +MF_INFLUX_WRITER_DB_PORT=8086 +MF_INFLUX_WRITER_DB_NAME=mainflux +MF_INFLUX_WRITER_DB_USER=mainflux +MF_INFLUX_WRITER_DB_PASS=mainflux +MF_INFLUX_WRITER_GRAFANA_PORT=3001 + +### InfluxDB Reader +MF_INFLUX_READER_LOG_LEVEL=debug +MF_INFLUX_READER_PORT=8905 +MF_INFLUX_READER_DB_NAME=mainflux +MF_INFLUX_READER_DB_PORT=8086 +MF_INFLUX_READER_DB_USER=mainflux +MF_INFLUX_READER_DB_PASS=mainflux + +### MongoDB Writer +MF_MONGO_WRITER_LOG_LEVEL=debug +MF_MONGO_WRITER_PORT=8901 +MF_MONGO_WRITER_DB_NAME=mainflux +MF_MONGO_WRITER_DB_PORT=27017 + +### MongoDB Reader +MF_MONGO_READER_LOG_LEVEL=debug +MF_MONGO_READER_PORT=8904 +MF_MONGO_READER_DB_NAME=mainflux +MF_MONGO_READER_DB_PORT=27017 + +### Postgres Writer +MF_POSTGRES_WRITER_LOG_LEVEL=debug +MF_POSTGRES_WRITER_PORT=9104 +MF_POSTGRES_WRITER_DB_PORT=5432 +MF_POSTGRES_WRITER_DB_USER=mainflux +MF_POSTGRES_WRITER_DB_PASS=mainflux +MF_POSTGRES_WRITER_DB_NAME=messages +MF_POSTGRES_WRITER_DB_SSL_MODE=disable +MF_POSTGRES_WRITER_DB_SSL_CERT="" +MF_POSTGRES_WRITER_DB_SSL_KEY="" +MF_POSTGRES_WRITER_DB_SSL_ROOT_CERT="" + +### Postgres Reader +MF_POSTGRES_READER_LOG_LEVEL=debug +MF_POSTGRES_READER_PORT=9204 +MF_POSTGRES_READER_CLIENT_TLS=false +MF_POSTGRES_READER_CA_CERTS="" +MF_POSTGRES_READER_DB_PORT=5432 +MF_POSTGRES_READER_DB_USER=mainflux +MF_POSTGRES_READER_DB_PASS=mainflux +MF_POSTGRES_READER_DB_NAME=messages +MF_POSTGRES_READER_DB_SSL_MODE=disable +MF_POSTGRES_READER_DB_SSL_CERT="" +MF_POSTGRES_READER_DB_SSL_KEY="" +MF_POSTGRES_READER_DB_SSL_ROOT_CERT="" diff --git a/Makefile b/Makefile index 9a71777818..d4e705a4e3 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,5 @@ docker_ui: docker build --tag=mainflux/ui -f docker/Dockerfile . + +run: + docker-compose -f docker/docker-compose.yml -f docker/aedes.yml up diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000000..cc1cad69d6 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,18 @@ +# Docker Composition + +Configure environment variables and run Mainflux Docker Composition. + +*Note**: `docker-compose` uses `.env` file to set all environment variables. Ensure that you run the command from the same location as .env file. + +## Installation + +Follow the [official documentation](https://docs.docker.com/compose/install/). + + +## Usage + +Run following commands from project root directory. + +``` +docker-compose -f docker/docker-compose.yml up +``` diff --git a/docker/aedes.yml b/docker/aedes.yml new file mode 100644 index 0000000000..2afcbf2251 --- /dev/null +++ b/docker/aedes.yml @@ -0,0 +1,45 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +version: "3.7" + +volumes: + mainflux-mqtt-redis-volume: + +services: + nginx: + depends_on: + - mqtt-adapter + + mqtt-redis: + image: redis:5.0-alpine + container_name: mainflux-mqtt-redis + restart: on-failure + networks: + - mainflux-base-net + volumes: + - mainflux-mqtt-redis-volume:/data + + mqtt-adapter: + image: mainflux/mqtt:latest + container_name: mainflux-mqtt + depends_on: + - things + - nats + - mqtt-redis + restart: on-failure + environment: + MF_MQTT_ADAPTER_LOG_LEVEL: ${MF_MQTT_ADAPTER_LOG_LEVEL} + MF_MQTT_INSTANCE_ID: mqtt-adapter-1 + MF_MQTT_ADAPTER_PORT: ${MF_MQTT_ADAPTER_PORT} + MF_MQTT_ADAPTER_WS_PORT: ${MF_MQTT_ADAPTER_WS_PORT} + MF_MQTT_ADAPTER_REDIS_HOST: mqtt-redis + MF_MQTT_ADAPTER_ES_HOST: es-redis + MF_NATS_URL: ${MF_NATS_URL} + MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} + MF_JAEGER_URL: ${MF_JAEGER_URL} + ports: + - 18831:${MF_MQTT_ADAPTER_PORT} + - 8881:${MF_MQTT_ADAPTER_WS_PORT} + networks: + - mainflux-base-net diff --git a/docker/channels.toml b/docker/channels.toml new file mode 100644 index 0000000000..febebdd005 --- /dev/null +++ b/docker/channels.toml @@ -0,0 +1,4 @@ +# If you want to listen on all channels, just pass one element ["*"], otherwise +# pass the list of channels. +[channels] +filter = ["*"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000000..cbfbaf4c2b --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,450 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +version: "3.7" + +networks: + mainflux-base-net: + driver: bridge + +volumes: + mainflux-authn-db-volume: + mainflux-users-db-volume: + mainflux-things-db-volume: + mainflux-things-redis-volume: + mainflux-es-redis-volume: + mainflux-influxdb-volume: + mainflux-bootstrap-db-volume: + mainflux-grafana-volume: + mainflux-opcua-adapter-volume: + +services: + nginx: + image: nginx:1.16.0-alpine + container_name: mainflux-nginx + restart: on-failure + volumes: + - ./nginx/nginx-${AUTH-key}.conf:/etc/nginx/nginx.conf.template + - ./nginx/entrypoint.sh:/entrypoint.sh + - ./nginx/snippets:/etc/nginx/snippets + - ./ssl/authorization.js:/etc/nginx/authorization.js + - ./ssl/certs/mainflux-server.crt:/etc/ssl/certs/mainflux-server.crt + - ./ssl/certs/ca.crt:/etc/ssl/certs/ca.crt + - ./ssl/certs/mainflux-server.key:/etc/ssl/private/mainflux-server.key + - ./ssl/dhparam.pem:/etc/ssl/certs/dhparam.pem + ports: + - ${MF_NGINX_HTTP_PORT}:${MF_NGINX_HTTP_PORT} + - ${MF_NGINX_SSL_PORT}:${MF_NGINX_SSL_PORT} + - ${MF_NGINX_MQTT_PORT}:${MF_NGINX_MQTT_PORT} + - ${MF_NGINX_MQTTS_PORT}:${MF_NGINX_MQTTS_PORT} + networks: + - mainflux-base-net + env_file: + - ../.env + command: /entrypoint.sh + depends_on: + - things + - users + - http-adapter + - ws-adapter + + nats: + image: nats:1.3.0 + container_name: mainflux-nats + restart: on-failure + networks: + - mainflux-base-net + + authn-db: + image: postgres:10.8-alpine + container_name: mainflux-authn-db + restart: on-failure + environment: + POSTGRES_USER: ${MF_AUTHN_DB_USER} + POSTGRES_PASSWORD: ${MF_AUTHN_DB_PASS} + POSTGRES_DB: ${MF_AUTHN_DB} + networks: + - mainflux-base-net + volumes: + - mainflux-authn-db-volume:/var/lib/postgresql/data + + authn: + image: mainflux/authn:latest + container_name: mainflux-authn + depends_on: + - authn-db + expose: + - ${MF_AUTHN_GRPC_PORT} + restart: on-failure + environment: + MF_AUTHN_LOG_LEVEL: ${MF_AUTHN_LOG_LEVEL} + MF_AUTHN_DB_HOST: authn-db + MF_AUTHN_DB_PORT: ${MF_AUTHN_DB_PORT} + MF_AUTHN_DB_USER: ${MF_AUTHN_DB_USER} + MF_AUTHN_DB_PASS: ${MF_AUTHN_DB_PASS} + MF_AUTHN_DB: ${MF_AUTHN_DB} + MF_AUTHN_HTTP_PORT: ${MF_AUTHN_HTTP_PORT} + MF_AUTHN_GRPC_PORT: ${MF_AUTHN_GRPC_PORT} + MF_AUTHN_SECRET: ${MF_AUTHN_SECRET} + MF_JAEGER_URL: ${MF_JAEGER_URL} + ports: + - ${MF_AUTHN_HTTP_PORT}:${MF_AUTHN_HTTP_PORT} + - ${MF_AUTHN_GRPC_PORT}:${MF_AUTHN_GRPC_PORT} + networks: + - mainflux-base-net + + users-db: + image: postgres:10.8-alpine + container_name: mainflux-users-db + restart: on-failure + environment: + POSTGRES_USER: ${MF_USERS_DB_USER} + POSTGRES_PASSWORD: ${MF_USERS_DB_PASS} + POSTGRES_DB: ${MF_USERS_DB} + networks: + - mainflux-base-net + volumes: + - mainflux-users-db-volume:/var/lib/postgresql/data + + users: + image: mainflux/users:latest + container_name: mainflux-users + volumes: + - ./users/emailer/templates/${MF_EMAIL_TEMPLATE}:/${MF_EMAIL_TEMPLATE} + depends_on: + - users-db + - authn + restart: on-failure + environment: + MF_USERS_LOG_LEVEL: ${MF_USERS_LOG_LEVEL} + MF_USERS_DB_HOST: users-db + MF_USERS_DB_PORT: ${MF_USERS_DB_PORT} + MF_USERS_DB_USER: ${MF_USERS_DB_USER} + MF_USERS_DB_PASS: ${MF_USERS_DB_PASS} + MF_USERS_DB: ${MF_USERS_DB} + MF_USERS_HTTP_PORT: ${MF_USERS_HTTP_PORT} + MF_JAEGER_URL: ${MF_JAEGER_URL} + MF_EMAIL_DRIVER: ${MF_EMAIL_DRIVER} + MF_EMAIL_HOST: ${MF_EMAIL_HOST} + MF_EMAIL_PORT: ${MF_EMAIL_PORT} + MF_EMAIL_USERNAME: ${MF_EMAIL_USERNAME} + MF_EMAIL_PASSWORD: ${MF_EMAIL_PASSWORD} + MF_EMAIL_FROM_ADDRESS: ${MF_EMAIL_FROM_ADDRESS} + MF_EMAIL_FROM_NAME: ${MF_EMAIL_FROM_NAME} + MF_EMAIL_TEMPLATE: ${MF_EMAIL_TEMPLATE} + MF_TOKEN_RESET_ENDPOINT: ${MF_TOKEN_RESET_ENDPOINT} + MF_AUTHN_URL: authn:${MF_AUTHN_GRPC_PORT} + ports: + - ${MF_USERS_HTTP_PORT}:${MF_USERS_HTTP_PORT} + networks: + - mainflux-base-net + + things-db: + image: postgres:10.8-alpine + container_name: mainflux-things-db + restart: on-failure + environment: + POSTGRES_USER: ${MF_THINGS_DB_USER} + POSTGRES_PASSWORD: ${MF_THINGS_DB_PASS} + POSTGRES_DB: ${MF_THINGS_DB} + networks: + - mainflux-base-net + volumes: + - mainflux-things-db-volume:/var/lib/postgresql/data + + things-redis: + image: redis:5.0-alpine + container_name: mainflux-things-redis + restart: on-failure + networks: + - mainflux-base-net + volumes: + - mainflux-things-redis-volume:/data + + things: + image: mainflux/things:latest + container_name: mainflux-things + depends_on: + - things-db + - authn + restart: on-failure + environment: + MF_THINGS_LOG_LEVEL: ${MF_THINGS_LOG_LEVEL} + MF_THINGS_DB_HOST: things-db + MF_THINGS_DB_PORT: ${MF_THINGS_DB_PORT} + MF_THINGS_DB_USER: ${MF_THINGS_DB_USER} + MF_THINGS_DB_PASS: ${MF_THINGS_DB_PASS} + MF_THINGS_DB: ${MF_THINGS_DB} + MF_THINGS_CACHE_URL: things-redis:${MF_REDIS_TCP_PORT} + MF_THINGS_ES_URL: es-redis:${MF_REDIS_TCP_PORT} + MF_THINGS_HTTP_PORT: ${MF_THINGS_HTTP_PORT} + MF_THINGS_AUTH_HTTP_PORT: ${MF_THINGS_AUTH_HTTP_PORT} + MF_THINGS_AUTH_GRPC_PORT: ${MF_THINGS_AUTH_GRPC_PORT} + MF_AUTH_URL: authn:${MF_AUTHN_GRPC_PORT} + MF_THINGS_SECRET: ${MF_THINGS_SECRET} + MF_JAEGER_URL: ${MF_JAEGER_URL} + ports: + - ${MF_THINGS_HTTP_PORT}:${MF_THINGS_HTTP_PORT} + - ${MF_THINGS_AUTH_HTTP_PORT}:${MF_THINGS_AUTH_HTTP_PORT} + - ${MF_THINGS_AUTH_GRPC_PORT}:${MF_THINGS_AUTH_GRPC_PORT} + networks: + - mainflux-base-net + + jaeger: + image: jaegertracing/all-in-one:1.13 + container_name: mainflux-jaeger + ports: + - ${MF_JAEGER_PORT}:${MF_JAEGER_PORT}/udp + - ${MF_JAEGER_FRONTEND}:${MF_JAEGER_FRONTEND} + - ${MF_JAEGER_COLLECTOR}:${MF_JAEGER_COLLECTOR} + - ${MF_JAEGER_CONFIGS}:${MF_JAEGER_CONFIGS} + networks: + - mainflux-base-net + + ws-adapter: + image: mainflux/ws:latest + container_name: mainflux-ws + depends_on: + - things + - nats + restart: on-failure + environment: + MF_WS_ADAPTER_LOG_LEVEL: ${MF_WS_ADAPTER_LOG_LEVEL} + MF_WS_ADAPTER_PORT: ${MF_WS_ADAPTER_PORT} + MF_NATS_URL: ${MF_NATS_URL} + MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} + MF_JAEGER_URL: ${MF_JAEGER_URL} + ports: + - ${MF_WS_ADAPTER_PORT}:${MF_WS_ADAPTER_PORT} + networks: + - mainflux-base-net + + http-adapter: + image: mainflux/http:latest + container_name: mainflux-http + depends_on: + - things + - nats + restart: on-failure + environment: + MF_HTTP_ADAPTER_LOG_LEVEL: debug + MF_HTTP_ADAPTER_PORT: ${MF_HTTP_ADAPTER_PORT} + MF_NATS_URL: ${MF_NATS_URL} + MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} + MF_JAEGER_URL: ${MF_JAEGER_URL} + ports: + - ${MF_HTTP_ADAPTER_PORT}:${MF_HTTP_ADAPTER_PORT} + networks: + - mainflux-base-net + + es-redis: + image: redis:5.0-alpine + container_name: mainflux-es-redis + restart: on-failure + networks: + - mainflux-base-net + volumes: + - mainflux-es-redis-volume:/data + + coap-adapter: + image: mainflux/coap:latest + container_name: mainflux-coap + depends_on: + - things + - nats + restart: on-failure + environment: + MF_COAP_ADAPTER_LOG_LEVEL: ${MF_COAP_ADAPTER_LOG_LEVEL} + MF_COAP_ADAPTER_PORT: ${MF_COAP_ADAPTER_PORT} + MF_NATS_URL: ${MF_NATS_URL} + MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} + MF_JAEGER_URL: ${MF_JAEGER_URL} + ports: + - ${MF_COAP_ADAPTER_PORT}:${MF_COAP_ADAPTER_PORT}/udp + - ${MF_COAP_ADAPTER_PORT}:${MF_COAP_ADAPTER_PORT}/tcp + networks: + - mainflux-base-net + + ui: + image: mainflux/ui:latest + container_name: mainflux-ui + restart: on-failure + ports: + - ${MF_UI_PORT}:${MF_UI_PORT} + networks: + - mainflux-base-net + environment: + MF_UI_PORT: ${MF_UI_PORT} + + opcua-redis: + image: redis:5.0-alpine + container_name: mainflux-opcua-redis + restart: on-failure + networks: + - mainflux-base-net + + opcua-adapter: + image: mainflux/opcua:latest + container_name: mainflux-opcua + depends_on: + - nats + restart: on-failure + environment: + MF_OPCUA_ADAPTER_HTTP_PORT: ${MF_OPCUA_ADAPTER_HTTP_PORT} + MF_OPCUA_ADAPTER_LOG_LEVEL: ${MF_OPCUA_ADAPTER_LOG_LEVEL} + MF_NATS_URL: ${MF_NATS_URL} + MF_OPCUA_ADAPTER_SERVER_URI: ${MF_OPCUA_ADAPTER_SERVER_URI} + MF_OPCUA_ADAPTER_NODE_NAMESPACE: ${MF_OPCUA_ADAPTER_NODE_NAMESPACE} + MF_OPCUA_ADAPTER_NODE_IDENTIFIER: ${MF_OPCUA_ADAPTER_NODE_IDENTIFIER} + MF_OPCUA_ADAPTER_POLICY: ${MF_OPCUA_ADAPTER_POLICY} + MF_OPCUA_ADAPTER_MODE: ${MF_OPCUA_ADAPTER_MODE} + MF_OPCUA_ADAPTER_CERT_FILE: ${MF_OPCUA_ADAPTER_CERT_FILE} + MF_OPCUA_ADAPTER_KEY_FILE: ${MF_OPCUA_ADAPTER_KEY_FILE} + MF_OPCUA_ADAPTER_ROUTE_MAP_URL: opcua-redis:${MF_REDIS_TCP_PORT} + MF_OPCUA_ADAPTER_ROUTE_MAP_PASS: ${MF_OPCUA_ADAPTER_ROUTE_MAP_PASS} + MF_OPCUA_ADAPTER_ROUTE_MAP_DB: ${MF_OPCUA_ADAPTER_ROUTE_MAP_DB} + MF_THINGS_ES_URL: es-redis:${MF_REDIS_TCP_PORT} + MF_THINGS_ES_PASS: ${MF_THINGS_ES_PASS} + MF_THINGS_ES_DB: ${MF_THINGS_ES_DB} + MF_OPCUA_ADAPTER_EVENT_CONSUMER: ${MF_OPCUA_ADAPTER_EVENT_CONSUMER} + ports: + - ${MF_OPCUA_ADAPTER_HTTP_PORT}:${MF_OPCUA_ADAPTER_HTTP_PORT} + networks: + - mainflux-base-net + volumes: + - mainflux-opcua-adapter-volume:/store + + influxdb: + image: influxdb:1.6.4-alpine + container_name: mainflux-influxdb + restart: on-failure + environment: + INFLUXDB_DB: ${MF_INFLUX_WRITER_DB_NAME} + INFLUXDB_ADMIN_USER: ${MF_INFLUX_WRITER_DB_USER} + INFLUXDB_ADMIN_PASSWORD: ${MF_INFLUX_WRITER_DB_PASS} + networks: + - mainflux-base-net + ports: + - ${MF_INFLUX_WRITER_DB_PORT}:${MF_INFLUX_WRITER_DB_PORT} + volumes: + - mainflux-influxdb-volume:/var/lib/influxdb + + influxdb-writer: + image: mainflux/influxdb-writer:latest + container_name: mainflux-influxdb-writer + depends_on: + - influxdb + - nats + restart: on-failure + environment: + MF_INFLUX_WRITER_LOG_LEVEL: debug + MF_NATS_URL: ${MF_NATS_URL} + MF_INFLUX_WRITER_PORT: ${MF_INFLUX_WRITER_PORT} + MF_INFLUX_WRITER_BATCH_SIZE: ${MF_INFLUX_WRITER_BATCH_SIZE} + MF_INFLUX_WRITER_BATCH_TIMEOUT: ${MF_INFLUX_WRITER_BATCH_TIMEOUT} + MF_INFLUX_WRITER_DB_NAME: ${MF_INFLUX_WRITER_DB_NAME} + MF_INFLUX_WRITER_DB_HOST: mainflux-influxdb + MF_INFLUX_WRITER_DB_PORT: ${MF_INFLUX_WRITER_DB_PORT} + MF_INFLUX_WRITER_DB_USER: ${MF_INFLUX_WRITER_DB_USER} + MF_INFLUX_WRITER_DB_PASS: ${MF_INFLUX_WRITER_DB_PASS} + ports: + - ${MF_INFLUX_WRITER_PORT}:${MF_INFLUX_WRITER_PORT} + networks: + - mainflux-base-net + volumes: + - ./channels.toml:/config/channels.toml + + grafana: + image: grafana/grafana:5.1.3 + container_name: mainflux-grafana + depends_on: + - influxdb + restart: on-failure + ports: + - ${MF_INFLUX_WRITER_GRAFANA_PORT}:${MF_GRAFANA_PORT} + networks: + - mainflux-base-net + volumes: + - mainflux-grafana-volume:/var/lib/grafana + + influxdb-reader: + image: mainflux/influxdb-reader:latest + container_name: mainflux-influxdb-reader + restart: on-failure + environment: + MF_INFLUX_READER_LOG_LEVEL: debug + MF_THINGS_URL: things:${MF_THINGS_AUTH_GRPC_PORT} + MF_INFLUX_READER_PORT: ${MF_INFLUX_READER_PORT} + MF_INFLUX_READER_DB_NAME: ${MF_INFLUX_READER_DB_NAME} + MF_INFLUX_READER_DB_HOST: mainflux-influxdb + MF_INFLUX_READER_DB_PORT: ${MF_INFLUX_READER_DB_PORT} + MF_INFLUX_READER_DB_USER: ${MF_INFLUX_READER_DB_USER} + MF_INFLUX_READER_DB_PASS: ${MF_INFLUX_READER_DB_PASS} + MF_JAEGER_URL: ${MF_JAEGER_URL} + ports: + - ${MF_INFLUX_READER_PORT}:${MF_INFLUX_READER_PORT} + networks: + - mainflux-base-net + + lora-redis: + image: redis:5.0-alpine + container_name: mainflux-lora-redis + restart: on-failure + networks: + - mainflux-base-net + + lora-adapter: + image: mainflux/lora:latest + container_name: mainflux-lora + depends_on: + - nats + restart: on-failure + environment: + MF_LORA_ADAPTER_LOG_LEVEL: ${MF_LORA_ADAPTER_LOG_LEVEL} + MF_THINGS_ES_URL: es-redis:${MF_REDIS_TCP_PORT} + MF_LORA_ADAPTER_ROUTE_MAP_URL: lora-redis:${MF_REDIS_TCP_PORT} + MF_LORA_ADAPTER_MESSAGES_URL: ${MF_LORA_ADAPTER_MESSAGES_URL} + MF_LORA_ADAPTER_HTTP_PORT: ${MF_LORA_ADAPTER_HTTP_PORT} + MF_NATS_URL: ${MF_NATS_URL} + ports: + - ${MF_LORA_ADAPTER_HTTP_PORT}:${MF_LORA_ADAPTER_HTTP_PORT} + networks: + - mainflux-base-net + + bootstrap-db: + image: postgres:10.2-alpine + container_name: mainflux-bootstrap-db + restart: on-failure + environment: + POSTGRES_USER: ${MF_BOOTSTRAP_DB_USER} + POSTGRES_PASSWORD: ${MF_BOOTSTRAP_DB_PASS} + POSTGRES_DB: ${MF_BOOTSTRAP_DB} + networks: + - mainflux-base-net + volumes: + - mainflux-bootstrap-db-volume:/var/lib/postgresql/data + + bootstrap: + image: mainflux/bootstrap:latest + container_name: mainflux-bootstrap + depends_on: + - bootstrap-db + restart: on-failure + ports: + - ${MF_BOOTSTRAP_PORT}:${MF_BOOTSTRAP_PORT} + environment: + MF_BOOTSTRAP_LOG_LEVEL: ${MF_BOOTSTRAP_LOG_LEVEL} + MF_BOOTSTRAP_DB_HOST: bootstrap-db + MF_BOOTSTRAP_DB_PORT: ${MF_BOOTSTRAP_DB_PORT} + MF_BOOTSTRAP_DB_USER: ${MF_BOOTSTRAP_DB_USER} + MF_BOOTSTRAP_DB_PASS: ${MF_BOOTSTRAP_DB_PASS} + MF_BOOTSTRAP_DB: ${MF_BOOTSTRAP_DB} + MF_BOOTSTRAP_DB_SSL_MODE: ${MF_BOOTSTRAP_DB_SSL_MODE} + MF_BOOTSTRAP_PORT: ${MF_BOOTSTRAP_PORT} + MF_SDK_BASE_URL: http://mainflux-things:${MF_THINGS_HTTP_PORT} + MF_AUTH_URL: authn:${MF_AUTHN_GRPC_PORT} + MF_THINGS_ES_URL: es-redis:${MF_REDIS_TCP_PORT} + MF_BOOTSTRAP_ES_URL: es-redis:${MF_REDIS_TCP_PORT} + MF_JAEGER_URL: ${MF_JAEGER_URL} + networks: + - mainflux-base-net diff --git a/docker/nginx/.gitignore b/docker/nginx/.gitignore new file mode 100644 index 0000000000..e8deea88ff --- /dev/null +++ b/docker/nginx/.gitignore @@ -0,0 +1,5 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +snippets/mqtt-upstream.conf +snippets/mqtt-ws-upstream.conf \ No newline at end of file diff --git a/docker/nginx/entrypoint.sh b/docker/nginx/entrypoint.sh new file mode 100755 index 0000000000..4ee5f26844 --- /dev/null +++ b/docker/nginx/entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/ash + +if [ -z "$MF_MQTT_CLUSTER" ] +then + envsubst '${MF_MQTT_ADAPTER_PORT}' < /etc/nginx/snippets/mqtt-upstream-single.conf > /etc/nginx/snippets/mqtt-upstream.conf + envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /etc/nginx/snippets/mqtt-ws-upstream-single.conf > /etc/nginx/snippets/mqtt-ws-upstream.conf +else + envsubst '${MF_MQTT_ADAPTER_PORT}' < /etc/nginx/snippets/mqtt-upstream-cluster.conf > /etc/nginx/snippets/mqtt-upstream.conf + envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /etc/nginx/snippets/mqtt-ws-upstream-cluster.conf > /etc/nginx/snippets/mqtt-ws-upstream.conf +fi + +envsubst ' + ${MF_USERS_HTTP_PORT} + ${MF_THINGS_HTTP_PORT} + ${MF_THINGS_HTTP_PORT} + ${MF_HTTP_ADAPTER_PORT} + ${MF_WS_ADAPTER_PORT} + ${MF_UI_PORT} + ${MF_INFLUX_READER_PORT} + ${MF_BOOTSTRAP_PORT} + ${MF_OPCUA_ADAPTER_HTTP_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf + +exec nginx -g "daemon off;" diff --git a/docker/nginx/nginx-key.conf b/docker/nginx/nginx-key.conf new file mode 100644 index 0000000000..4378e1691a --- /dev/null +++ b/docker/nginx/nginx-key.conf @@ -0,0 +1,145 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +# This is the default Mainflux NGINX configuration. + +user nginx; +worker_processes auto; +worker_cpu_affinity auto; +pid /run/nginx.pid; +include /etc/nginx/modules-enabled/*.conf; + +events { + # Explanation: https://serverfault.com/questions/787919/optimal-value-for-nginx-worker-connections + # We'll keep 10k connections per core (assuming one worker per core) + worker_connections 10000; +} + +http { + include snippets/http_access_log.conf; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + + # Include single-node or multiple-node (cluster) upstream + include snippets/mqtt-ws-upstream.conf; + + server { + listen 80 default_server; + listen [::]:80 default_server; + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + + include snippets/ssl.conf; + + add_header Strict-Transport-Security "max-age=63072000; includeSubdomains"; + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header Access-Control-Allow-Origin '*'; + add_header Access-Control-Allow-Methods '*'; + add_header Access-Control-Allow-Headers '*'; + + server_name localhost; + + # Proxy pass to users service + location ~ ^/(users|tokens) { + include snippets/proxy-headers.conf; + proxy_pass http://users:${MF_USERS_HTTP_PORT}; + } + + # Proxy pass to things service + location ~ ^/(things|channels|connect) { + include snippets/proxy-headers.conf; + add_header Access-Control-Expose-Headers Location; + proxy_pass http://things:${MF_THINGS_HTTP_PORT}; + } + + location /version { + include snippets/proxy-headers.conf; + proxy_pass http://things:${MF_THINGS_HTTP_PORT}; + } + + # Proxy pass to mainflux-http-adapter + location /http/ { + include snippets/proxy-headers.conf; + + # Trailing `/` is mandatory. Refer to the http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass + # If the proxy_pass directive is specified with a URI, then when a request is passed to the server, + # the part of a normalized request URI matching the location is replaced by a URI specified in the directive + proxy_pass http://http-adapter:${MF_HTTP_ADAPTER_PORT}/; + } + + # Proxy pass to mainflux-ws-adapter + location /ws/ { + include snippets/proxy-headers.conf; + include snippets/ws-upgrade.conf; + proxy_pass http://ws-adapter:${MF_WS_ADAPTER_PORT}/; + } + + # Proxy pass to mainflux-mqtt-adapter over WS + location /mqtt/ { + include snippets/proxy-headers.conf; + include snippets/ws-upgrade.conf; + proxy_pass http://mqtt_ws_cluster/; + } + + # Proxy pass to mainflux-influxdb-reader + location /reader/ { + include snippets/proxy-headers.conf; + proxy_pass http://influxdb-reader:${MF_INFLUX_READER_PORT}/; + } + + # Proxy pass to mainflux-http-reader + location /writer/ { + include snippets/proxy-headers.conf; + proxy_pass http://http-adapter:${MF_HTTP_ADAPTER_PORT}/; + } + + # Proxy pass to mainflux-bootstrap + location /bootstrap/ { + include snippets/proxy-headers.conf; + proxy_pass http://bootstrap:${MF_BOOTSTRAP_PORT}/; + } + + # Proxy pass to mainflux-opcua-adapter + location /browse { + include snippets/proxy-headers.conf; + proxy_pass http://opcua-adapter:${MF_OPCUA_ADAPTER_HTTP_PORT}; + } + + location / { + include snippets/proxy-headers.conf; + proxy_pass http://ui:${MF_UI_PORT}; + } + } +} + +# MQTT +stream { + include snippets/stream_access_log.conf; + + # Include single-node or multiple-node (cluster) upstream + include snippets/mqtt-upstream.conf; + + server { + listen 1883; + listen [::]:1883; + listen 8883 ssl; + listen [::]:8883 ssl; + + include snippets/ssl.conf; + + proxy_pass mqtt_cluster; + } +} + +error_log info.log info; diff --git a/docker/nginx/nginx-x509.conf b/docker/nginx/nginx-x509.conf new file mode 100644 index 0000000000..6170f62488 --- /dev/null +++ b/docker/nginx/nginx-x509.conf @@ -0,0 +1,161 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +# This is the Mainflux NGINX configuration for mututal authentication based on X.509 certifiactes. + +user nginx; +worker_processes auto; +worker_cpu_affinity auto; +pid /run/nginx.pid; +load_module /etc/nginx/modules/ngx_stream_js_module.so; +load_module /etc/nginx/modules/ngx_http_js_module.so; +include /etc/nginx/modules-enabled/*.conf; + +events { + # Explanation: https://serverfault.com/questions/787919/optimal-value-for-nginx-worker-connections + # We'll keep 10k connections per core (assuming one worker per core) + worker_connections 10000; +} + +http { + include snippets/http_access_log.conf; + + js_include authorization.js; + js_set $auth_key setKey; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + + # Include single-node or multiple-node (cluster) upstream + include snippets/mqtt-ws-upstream.conf; + + server { + listen 80 default_server; + listen [::]:80 default_server; + listen 443 ssl http2 default_server; + listen [::]:443 ssl http2 default_server; + ssl_verify_client optional; + include snippets/ssl.conf; + include snippets/ssl-client.conf; + + add_header Strict-Transport-Security "max-age=63072000; includeSubdomains"; + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header Access-Control-Allow-Origin '*'; + add_header Access-Control-Allow-Methods '*'; + add_header Access-Control-Allow-Headers '*'; + + server_name localhost; + + # Proxy pass to users service + location ~ ^/(users|tokens) { + include snippets/proxy-headers.conf; + proxy_pass http://users:${MF_USERS_HTTP_PORT}; + } + + # Proxy pass to things service + location ~ ^/(things|channels) { + include snippets/proxy-headers.conf; + add_header Access-Control-Expose-Headers Location; + proxy_pass http://things:${MF_THINGS_HTTP_PORT}; + } + + location /version { + include snippets/proxy-headers.conf; + proxy_pass http://things:${MF_THINGS_HTTP_PORT}; + } + + # Proxy pass to mainflux-http-adapter + location /http/ { + include snippets/verify-ssl-client.conf; + include snippets/proxy-headers.conf; + proxy_set_header Authorization $auth_key; + + # Trailing `/` is mandatory. Refer to the http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass + # If the proxy_pass directive is specified with a URI, then when a request is passed to the server, + # the part of a normalized request URI matching the location is replaced by a URI specified in the directive + proxy_pass http://http-adapter:${MF_HTTP_ADAPTER_PORT}/; + } + + # Proxy pass to mainflux-ws-adapter + location /ws/ { + include snippets/verify-ssl-client.conf; + include snippets/proxy-headers.conf; + include snippets/ws-upgrade.conf; + proxy_pass http://ws-adapter:${MF_WS_ADAPTER_PORT}/; + } + + # Proxy pass to mainflux-mqtt-adapter over WS + location /mqtt/ { + include snippets/verify-ssl-client.conf; + include snippets/proxy-headers.conf; + include snippets/ws-upgrade.conf; + proxy_pass http://mqtt_ws_cluster/; + } + + # Proxy pass to mainflux-influxdb-reader + location /reader/ { + include snippets/proxy-headers.conf; + proxy_pass http://influxdb-reader:${MF_INFLUX_READER_PORT}/; + } + + # Proxy pass to mainflux-http-reader + location /writer/ { + include snippets/proxy-headers.conf; + proxy_pass http://http-adapter:${MF_HTTP_ADAPTER_PORT}/; + } + + # Proxy pass to mainflux-bootstrap + location /bootstrap/ { + include snippets/proxy-headers.conf; + proxy_pass http://bootstrap:${MF_BOOTSTRAP_PORT}/; + } + + # Proxy pass to mainflux-opcua-adapter + location /browse { + include snippets/proxy-headers.conf; + proxy_pass http://opcua-adapter:${MF_OPCUA_ADAPTER_HTTP_PORT}; + } + + location / { + include snippets/proxy-headers.conf; + proxy_pass http://ui:${MF_UI_PORT}; + } + } +} + +# MQTT +stream { + include snippets/stream_access_log.conf; + + # Include JS script for mTLS + js_include authorization.js; + + # Include single-node or multiple-node (cluster) upstream + include snippets/mqtt-upstream.conf; + ssl_verify_client on; + include snippets/ssl-client.conf; + + server { + listen 1883; + listen [::]:1883; + listen 8883 ssl; + listen [::]:8883 ssl; + + include snippets/ssl.conf; + js_preread authenticate; + + proxy_pass mqtt_cluster; + } +} + +error_log info.log info; diff --git a/docker/nginx/snippets/http_access_log.conf b/docker/nginx/snippets/http_access_log.conf new file mode 100644 index 0000000000..5e3daf7e66 --- /dev/null +++ b/docker/nginx/snippets/http_access_log.conf @@ -0,0 +1,8 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +log_format access_log_format 'HTTP/WS ' + '$remote_addr: ' + '"$request" $status; ' + 'request time=$request_time upstream connect time=$upstream_connect_time upstream response time=$upstream_response_time'; +access_log access.log access_log_format; diff --git a/docker/nginx/snippets/mqtt-upstream-cluster.conf b/docker/nginx/snippets/mqtt-upstream-cluster.conf new file mode 100644 index 0000000000..3165729499 --- /dev/null +++ b/docker/nginx/snippets/mqtt-upstream-cluster.conf @@ -0,0 +1,9 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +upstream mqtt_cluster { + least_conn; + server mqtt-adapter-1:${MF_MQTT_ADAPTER_PORT}; + server mqtt-adapter-2:${MF_MQTT_ADAPTER_PORT}; + server mqtt-adapter-3:${MF_MQTT_ADAPTER_PORT}; +} \ No newline at end of file diff --git a/docker/nginx/snippets/mqtt-upstream-single.conf b/docker/nginx/snippets/mqtt-upstream-single.conf new file mode 100644 index 0000000000..67a0828b6d --- /dev/null +++ b/docker/nginx/snippets/mqtt-upstream-single.conf @@ -0,0 +1,6 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +upstream mqtt_cluster { + server mqtt-adapter:${MF_MQTT_ADAPTER_PORT}; +} \ No newline at end of file diff --git a/docker/nginx/snippets/mqtt-ws-upstream-cluster.conf b/docker/nginx/snippets/mqtt-ws-upstream-cluster.conf new file mode 100644 index 0000000000..7ebc9fcee6 --- /dev/null +++ b/docker/nginx/snippets/mqtt-ws-upstream-cluster.conf @@ -0,0 +1,9 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +upstream mqtt_ws_cluster { + least_conn; + server mqtt-adapter-1:${MF_MQTT_ADAPTER_WS_PORT}; + server mqtt-adapter-2:${MF_MQTT_ADAPTER_WS_PORT}; + server mqtt-adapter-3:${MF_MQTT_ADAPTER_WS_PORT}; +} \ No newline at end of file diff --git a/docker/nginx/snippets/mqtt-ws-upstream-single.conf b/docker/nginx/snippets/mqtt-ws-upstream-single.conf new file mode 100644 index 0000000000..5729b01de5 --- /dev/null +++ b/docker/nginx/snippets/mqtt-ws-upstream-single.conf @@ -0,0 +1,6 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +upstream mqtt_ws_cluster { + server mqtt-adapter:${MF_MQTT_ADAPTER_WS_PORT}; +} \ No newline at end of file diff --git a/docker/nginx/snippets/proxy-headers.conf b/docker/nginx/snippets/proxy-headers.conf new file mode 100644 index 0000000000..d43679c922 --- /dev/null +++ b/docker/nginx/snippets/proxy-headers.conf @@ -0,0 +1,15 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +proxy_redirect off; +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $scheme; + +# Allow OPTIONS method CORS +if ($request_method = OPTIONS) { + add_header Content-Length 0; + add_header Content-Type text/plain; + return 200; +} \ No newline at end of file diff --git a/docker/nginx/snippets/ssl-client.conf b/docker/nginx/snippets/ssl-client.conf new file mode 100644 index 0000000000..a704dbd9a3 --- /dev/null +++ b/docker/nginx/snippets/ssl-client.conf @@ -0,0 +1,5 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +ssl_client_certificate /etc/ssl/certs/ca.crt; +ssl_verify_depth 2; diff --git a/docker/nginx/snippets/ssl.conf b/docker/nginx/snippets/ssl.conf new file mode 100644 index 0000000000..119647a041 --- /dev/null +++ b/docker/nginx/snippets/ssl.conf @@ -0,0 +1,16 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +# These paths are set to its default values as +# a volume in the docker/docker-compose.yml file. +ssl_certificate /etc/ssl/certs/mainflux-server.crt; +ssl_certificate_key /etc/ssl/private/mainflux-server.key; +ssl_dhparam /etc/ssl/certs/dhparam.pem; + +ssl_protocols TLSv1.2 TLSv1.3; +ssl_prefer_server_ciphers on; +ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; +ssl_ecdh_curve secp384r1; +ssl_session_tickets off; +resolver 8.8.8.8 8.8.4.4 valid=300s; +resolver_timeout 5s; diff --git a/docker/nginx/snippets/stream_access_log.conf b/docker/nginx/snippets/stream_access_log.conf new file mode 100644 index 0000000000..891a4a5e17 --- /dev/null +++ b/docker/nginx/snippets/stream_access_log.conf @@ -0,0 +1,7 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +log_format access_log_format '$protocol ' + '$remote_addr: ' + 'status=$status; upstream connect time=$upstream_connect_time'; +access_log access.log access_log_format; diff --git a/docker/nginx/snippets/verify-ssl-client.conf b/docker/nginx/snippets/verify-ssl-client.conf new file mode 100644 index 0000000000..be44b243a1 --- /dev/null +++ b/docker/nginx/snippets/verify-ssl-client.conf @@ -0,0 +1,9 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +if ($ssl_client_verify != SUCCESS) { + return 403; +} +if ($auth_key = '') { + return 403; +} \ No newline at end of file diff --git a/docker/nginx/snippets/ws-upgrade.conf b/docker/nginx/snippets/ws-upgrade.conf new file mode 100644 index 0000000000..424b9ea511 --- /dev/null +++ b/docker/nginx/snippets/ws-upgrade.conf @@ -0,0 +1,9 @@ +# Copyright (c) Mainflux +# SPDX-License-Identifier: Apache-2.0 + +proxy_http_version 1.1; +proxy_set_header Upgrade $http_upgrade; +proxy_set_header Connection "Upgrade"; +proxy_connect_timeout 7d; +proxy_send_timeout 7d; +proxy_read_timeout 7d; \ No newline at end of file diff --git a/docker/ssl/Makefile b/docker/ssl/Makefile new file mode 100644 index 0000000000..9cf19bd2fd --- /dev/null +++ b/docker/ssl/Makefile @@ -0,0 +1,42 @@ +CRT_LOCATION = certs +KEY = default +O = Mainflux +OU = mainflux +EA = info@mainflux.com +CN = localhost +CRT_FILE_NAME = thing + +all: clean_certs ca server_crt + +# CA name and key is "ca". +ca: + openssl req -newkey rsa:2048 -x509 -nodes -sha512 -days 1095 \ + -keyout $(CRT_LOCATION)/ca.key -out $(CRT_LOCATION)/ca.crt -subj "/CN=localhost/O=Mainflux/OU=IoT/emailAddress=info@mainflux.com" + +# Server cert and key name is "mainflux-server". +server_cert: + # Create mainflux server key and CSR. + openssl req -new -sha256 -newkey rsa:4096 -nodes -keyout $(CRT_LOCATION)/mainflux-server.key \ + -out $(CRT_LOCATION)/mainflux-server.csr -subj "/CN=$(CN)/O=$(O)/OU=$(OU)/emailAddress=$(EA)" + + # Sign server CSR. + openssl x509 -req -days 1000 -in $(CRT_LOCATION)/mainflux-server.csr -CA $(CRT_LOCATION)/ca.crt -CAkey $(CRT_LOCATION)/ca.key -CAcreateserial -out $(CRT_LOCATION)/mainflux-server.crt + + # Remove CSR. + rm $(CRT_LOCATION)/mainflux-server.csr + +thing_cert: + # Create mainflux server key and CSR. + openssl req -new -sha256 -newkey rsa:4096 -nodes -keyout $(CRT_LOCATION)/$(CRT_FILE_NAME).key \ + -out $(CRT_LOCATION)/$(CRT_FILE_NAME).csr -subj "/CN=$(KEY)/O=$(O)/OU=$(OU)/emailAddress=$(EA)" + + # Sign client CSR. + openssl x509 -req -days 730 -in $(CRT_LOCATION)/$(CRT_FILE_NAME).csr -CA $(CRT_LOCATION)/ca.crt -CAkey $(CRT_LOCATION)/ca.key -CAcreateserial -out $(CRT_LOCATION)/$(CRT_FILE_NAME).crt + + # Remove CSR. + rm $(CRT_LOCATION)/$(CRT_FILE_NAME).csr + +clean_certs: + rm -r $(CRT_LOCATION)/*.crt + rm -r $(CRT_LOCATION)/*.key + rm -r $(CRT_LOCATION)/*.srl diff --git a/docker/ssl/authorization.js b/docker/ssl/authorization.js new file mode 100644 index 0000000000..7e8968c225 --- /dev/null +++ b/docker/ssl/authorization.js @@ -0,0 +1,176 @@ +var clientKey = ''; + +// Check certificate MQTTS. +function authenticate(s) { + if (!s.variables.ssl_client_s_dn || !s.variables.ssl_client_s_dn.length || + !s.variables.ssl_client_verify || s.variables.ssl_client_verify != "SUCCESS") { + s.deny(); + return + } + + s.on('upload', function (data) { + if (data == '') { + return; + } + + var packet_type_flags_byte = data.codePointAt(0); + // First MQTT packet contain message type and flags. CONNECT message type + // is encoded as 0001, and we're not interested in flags, so only values + // 0001xxxx (which is between 16 and 32) should be checked. + if (packet_type_flags_byte < 16 || packet_type_flags_byte >= 32) { + s.off('upload'); + s.allow(); + return; + } + + if (clientKey === '') { + clientKey = parseCert(s.variables.ssl_client_s_dn, 'CN'); + } + + var pass = parsePackage(s, data); + + if (!clientKey.length || pass !== clientKey) { + s.error('Cert CN (' + clientKey + ') does not match client password'); + s.off('upload') + s.deny(); + return; + } + + s.off('upload'); + s.allow(); + }) +} + +function parsePackage(s, data) { + // An explanation of MQTT packet structure can be found here: + // https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#msg-format. + + // CONNECT message is explained here: + // https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect. + + /* + 0 1 2 3 + 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TYPE | RSRVD | REMAINING LEN | PROTOCOL NAME LEN | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | PROTOCOL NAME | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| + | VERSION | FLAGS | KEEP ALIVE | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| + | Payload (if any) ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + First byte with remaining length represents fixed header. + Remaining Length is the length of the variable header (10 bytes) plus the length of the Payload. + It is encoded in the manner described here: + http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180836. + + Connect flags byte looks like this: + | 7 | 6 | 5 | 4 3 | 2 | 1 | 0 | + | Username Flag | Password Flag | Will Retain | Will QoS | Will Flag | Clean Session | Reserved | + + The payload is determined by the flags and comes in this order: + 1. Client ID (2 bytes length + ID value) + 2. Will Topic (2 bytes length + Will Topic value) if Will Flag is 1. + 3. Will Message (2 bytes length + Will Message value) if Will Flag is 1. + 4. User Name (2 bytes length + User Name value) if User Name Flag is 1. + 5. Password (2 bytes length + Password value) if Password Flag is 1. + + This method extracts Password field. + */ + + // Extract variable length header. It's 1-4 bytes. As long as continuation byte is + // 1, there are more bytes in this header. This algorithm is explained here: + // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180836 + var len_size = 1; + for (var remaining_len = 1; remaining_len < 5; remaining_len++) { + if (data.codePointAt(remaining_len) > 128) { + len_size += 1; + continue; + } + break; + } + + // CONTROL(1) + MSG_LEN(1-4) + PROTO_NAME_LEN(2) + PROTO_NAME(4) + PROTO_VERSION(1) + var flags_pos = 1 + len_size + 2 + 4 + 1; + var flags = data.codePointAt(flags_pos); + + // If there are no username and password flags (11xxxxxx), return. + if (flags < 192) { + s.error('MQTT username or password not provided'); + return ''; + } + + // FLAGS(1) + KEEP_ALIVE(2) + var shift = flags_pos + 1 + 2; + + // Number of bytes to encode length. + var len_bytes_num = 2; + + // If Wil Flag is present, Will Topic and Will Message need to be skipped as well. + var shift_flags = 196 <= flags ? 5 : 3; + var len_msb, len_lsb, len; + + for (var i = 0; i < shift_flags; i++) { + len_msb = data.codePointAt(shift).toString(16); + len_lsb = data.codePointAt(shift + 1).toString(16); + len = calcLen(len_msb, len_lsb); + shift += len_bytes_num; + if (i != shift_flags - 1) { + shift += len; + } + } + + var password = data.substring(shift, shift + len); + return password; +} + +// Check certificate HTTPS and WSS. +function setKey(r) { + if (clientKey === '') { + clientKey = parseCert(r.variables.ssl_client_s_dn, 'CN'); + } + + var auth = r.headersIn['Authorization']; + if (auth && auth.length && auth != clientKey) { + r.error('Authorization header does not match certificate'); + return ''; + } + + if (r.uri.startsWith('/ws') && (!auth || !auth.length)) { + var a; + for (a in r.args) { + if (a == 'authorization' && r.args[a] === clientKey) { + return clientKey; + } + } + + r.error('Authorization param does not match certificate'); + return ''; + } + + return clientKey; +} + +function calcLen(msb, lsb) { + if (lsb < 2) { + lsb = '0' + lsb; + } + + return parseInt(msb + lsb, 16); +} + +function parseCert(cert, key) { + if (cert.length) { + var pairs = cert.split(','); + for (var i = 0; i < pairs.length; i++) { + var pair = pairs[i].split('='); + if (pair[0].toUpperCase() == key) { + return pair[1]; + } + } + } + + return ''; +} diff --git a/docker/ssl/certs/ca.crt b/docker/ssl/certs/ca.crt new file mode 100644 index 0000000000..1d9f2b85a1 --- /dev/null +++ b/docker/ssl/certs/ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjzCCAnegAwIBAgIUQ1AagVQXCuOIzmGXm+KhsbyBc18wDQYJKoZIhvcNAQEN +BQAwVzESMBAGA1UEAwwJbG9jYWxob3N0MREwDwYDVQQKDAhNYWluZmx1eDEMMAoG +A1UECwwDSW9UMSAwHgYJKoZIhvcNAQkBFhFpbmZvQG1haW5mbHV4LmNvbTAeFw0x +OTA0MDEwOTI3MDFaFw0yMjAzMzEwOTI3MDFaMFcxEjAQBgNVBAMMCWxvY2FsaG9z +dDERMA8GA1UECgwITWFpbmZsdXgxDDAKBgNVBAsMA0lvVDEgMB4GCSqGSIb3DQEJ +ARYRaW5mb0BtYWluZmx1eC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCq6O4PHwgGOmEafjea5KocG80GYSYbvN37ums6fQ1wcmCxn8LtZek8WkfJ +S2NQQPDvn8QWRY7aUkTAW7cEB4vxpT25bevP7KJNFAS8XZO7NTfF8fscJS+YWSXz +VS0OFZ2YuqTnjCiqWf5mvjAkkXBGIYq+k2ONM1tHlEA0lzbLun2a9H/XarCG+znj +pfYpW6R08zFzXyGb4sI2pyYpP7iZLla7PTSZTt9h6jkY3qqMDhEHhPdlXDhO1O9/ +lA8yWMO9vKCzC7ngDXnV99Nl+tFhp9z9VkTUveLMuN9+riDJRfP25fOzHuRYzmsR +emYjD1NvSgsvFqSbFDVXB8kcyrXPAgMBAAGjUzBRMB0GA1UdDgQWBBRs4xR91qEj +NRGmw391xS7x6Tc+8jAfBgNVHSMEGDAWgBRs4xR91qEjNRGmw391xS7x6Tc+8jAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAAPMf7bVFhzUG8AYq0 +VS9BWVwVtdNzZ3X9FkG9O+tZZO43GlaToym8PmhJHF9wk3AA+pmgfcmBrHcTG0me +PeincN2euO0c4iv1f/i4bAY5/iq/Q0w/GiuTL5VLVpaH1SQrWhc0ZD7Ii+lVPpFQ +bJXKHFQBnZU7mWeQnL9W1SVhWfsSKShBkAEUeGXo3YMC7nYsFJkl/heC3sYqfrW4 +7fq80u+TU6HjGetSAWKacae7eeNmprMn0lFw2VqPQG3M4M0l9pEfcrRygOAnqNKO +aNi2UYKBla3XeDjObovOsXRScTKmJZwJ/STJlu+x5UAwF34ZBJy0O2qdd+kOxAhj +5Yq2 +-----END CERTIFICATE----- diff --git a/docker/ssl/certs/ca.key b/docker/ssl/certs/ca.key new file mode 100644 index 0000000000..e29d0f6ede --- /dev/null +++ b/docker/ssl/certs/ca.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCq6O4PHwgGOmEa +fjea5KocG80GYSYbvN37ums6fQ1wcmCxn8LtZek8WkfJS2NQQPDvn8QWRY7aUkTA +W7cEB4vxpT25bevP7KJNFAS8XZO7NTfF8fscJS+YWSXzVS0OFZ2YuqTnjCiqWf5m +vjAkkXBGIYq+k2ONM1tHlEA0lzbLun2a9H/XarCG+znjpfYpW6R08zFzXyGb4sI2 +pyYpP7iZLla7PTSZTt9h6jkY3qqMDhEHhPdlXDhO1O9/lA8yWMO9vKCzC7ngDXnV +99Nl+tFhp9z9VkTUveLMuN9+riDJRfP25fOzHuRYzmsRemYjD1NvSgsvFqSbFDVX +B8kcyrXPAgMBAAECggEAbp/el0MKup1HBRL1gvjHcvI7vwla1VFmje2YQn93F3Wx +SMeUMH1qfnohRRXa7rNaQIA1OAVF9eKSRcAXsjAAUSUX0tJndGpCk4mFlzcqzF4h +/6olU45uRDpP6jUTuK4dGCKXYpjCKaGenXo1RzYsafiECd707Qx05Nv8ww2tlifN +HtUR0xCZfVGDZfmNMZVrksUIZ1XHwZNtNLWQW6MBl3RhFaA0Wz/RfFMi2FzacEbj +75IqE6PLic1fin6P3GouzKamtZ6YPTyR5PqxCOCw97oZDCUGy2qGyAuPUi9O2HKB +fQgSyIxuR73S2korvxAmvekubjBFAqhan2oEjZs6oQKBgQDT28COlC33BSrpr2+V +pZIL4Bb1rGHreTi1M/4n9nP3GOZ9gqnSUsWXyxYVoZ2YfixorjZhUzHyx4SfZ2E9 +p5PkIJ0wOiHLlKQ36vEVN9ZO1UyNCYUgs3seW40xnsAiMNczZjufIZrsejO3tc2j +Jhgp+B/9Bt5A8us2ewhz3LlQowKBgQDOhQmZAfL/xAjYBCUS73t/YO60i5e1yg2J +i6jXeKjd5gRZ32upkBzQ8UBvAGSQGqrcCnqIzrU5TeeD046bZzkokg7iKwHwQDrL +SXTthUB6ABZddP/VXCEUVBer3FEnUgJm9jw08RzmPyNEPjfp91FDmJ9GYcbdo/nL +hBPHh3lc5QKBgQCJYZ0yWACeiKlVNECFqAJW1Q/Oa+RrkAYn6vlK7NQyTeFZTlvV +WXtsfXNqv4y0kE037JCy+AIRzzO/MoiqNHsAme2Ukn3LyC3dXOrMuZKtOEAVzTCZ +Dgoum2up26n4AffrCsZq4J3X7z6OSMR6oX9V5+LGb6e8Mko43/uRNnatRQKBgEMH +bQkLV+ppnxE1ry7JKcU7Gd7hm9j1/pTRDnj5AZ4b5Peii1ganS+3zdj5QKqA7UnD +4Od8Z9d0kJr51EReKXAgj9IacWOgBTUr31akNDwkwR2ONubyIw5tCM3QEUr41CzE +6N+qDl4wyeqBYzZ9/hM5eyCl5ZzUduP2N1FAiER9AoGAW2T0OeM5ZsPABMKu9eEN +FB9bVysqWT1tExB34OGWrZvNEzsHTqvr/D3KSWv0PS1pM46M1XkVbybOzRmPrzab +AGMDJXgGhMuk2UtDA/s9mgqTOeDXpvmaFyThVkoH162j6GMuX2SwxHnH9D42zgMR +3LEZ/5Q5HMJ4jwEM880jvP4= +-----END PRIVATE KEY----- diff --git a/docker/ssl/certs/ca.srl b/docker/ssl/certs/ca.srl new file mode 100644 index 0000000000..0df06ebce7 --- /dev/null +++ b/docker/ssl/certs/ca.srl @@ -0,0 +1 @@ +27207EA9519D3D252E08AFA38D23BF2928FD5E20 diff --git a/docker/ssl/certs/mainflux-server.crt b/docker/ssl/certs/mainflux-server.crt new file mode 100644 index 0000000000..00dff88717 --- /dev/null +++ b/docker/ssl/certs/mainflux-server.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEOjCCAyICFCcgfqlRnT0lLgivo40jvyko/V4fMA0GCSqGSIb3DQEBCwUAMFcx +EjAQBgNVBAMMCWxvY2FsaG9zdDERMA8GA1UECgwITWFpbmZsdXgxDDAKBgNVBAsM +A0lvVDEgMB4GCSqGSIb3DQEJARYRaW5mb0BtYWluZmx1eC5jb20wHhcNMTkwNDAx +MDkyNzA1WhcNMjExMjI2MDkyNzA1WjBcMRIwEAYDVQQDDAlsb2NhbGhvc3QxETAP +BgNVBAoMCE1haW5mbHV4MREwDwYDVQQLDAhtYWluZmx1eDEgMB4GCSqGSIb3DQEJ +ARYRaW5mb0BtYWluZmx1eC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQD1LayMnOYlTHWkK/7BIc2nRLkfkbfyejIujEKIuOPYZ1DbG36VeSM1MYlp +zl+E7gJvvK1RuCcL4DKG0uExI6HV2GdEq9kSe3Pj/512VTq+bXvMDRTcHMUkaN0N +J7GybHNk8J3fmFlB61zUpZUNy0M14YYX8tZRMMw8Ke6ThJyj8ulSky4Cp2tfiGK9 ++YLP/UJkSm+0EOVAOMAtLNvXtg5+/0e63M+stdf+F3txLuiYXiOG399tXlI61r4L +5fKs0xau6P1V5uEPwAnQiXYVLCdahfGrUJIjHnHTU0TS2EpE8OxAu0krzQeONGSU +g6SMM8vCP0d8yqQrYZGkmaFmIiTgOmy/fs+8u/ykautiOR/SviTR3hi/ofjZ+NTd +T2Udg98BGuZBwKw+elajHUSUEkxtJVxeuFiVGzZNXkEhuxU6VNCnPeXxtl502rU9 +nmhmO2WJ0/1KX+oe/uTC99b+olEPm72exsX0mwkSpIwDRBpX9meER4vJe4yX9fmo +tqEC2G30C9KYn+STcY9P7jptJgLLuN61DVBjeMPLW+0NTjqmtplcu73zYvyCsG4r +hIhY291wvz18iNLY7BfehU3beEx68ApdLMue6xi9JlFKxHf5FHBnBSvD2xrR47rH +9UMOHLglB+QkoidQ3KugHJ8r1sVHPhuS8mE7cENReFoNfh+N2wIDAQABMA0GCSqG +SIb3DQEBCwUAA4IBAQARH5ZD86TPaKW7Dty1bAnj1owp0o+DOp65hGZOZ2AqYVDF +UMz46ahAuBWhHPIiSkBnonBL5xVV3qihhlISaOQKe2FPdt/ekhUTzI/upAZDphN0 +m4ZNllXaHAA0IQpXp3O/An6/IhrLCGLth9pnIzswi6sF+I5nIfpcuAV7TJfLUAG+ +UTjy8GsZhE/ZCx0JSYzhpC1mDGxtyCQR7QY7rnEohXv0bHmv/jVVIZenT2SZZHJ5 +sQEiaIZWbpHctpgbom1qi5BNmIz9APKus3f8ACGuMLOHiW1u6I8vl4b1kqc44Qoe +2c5uGEHh+Iv6v/V5JwzTrfbcaWeAv058NnN9rF8i +-----END CERTIFICATE----- diff --git a/docker/ssl/certs/mainflux-server.key b/docker/ssl/certs/mainflux-server.key new file mode 100644 index 0000000000..8a71b14685 --- /dev/null +++ b/docker/ssl/certs/mainflux-server.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQD1LayMnOYlTHWk +K/7BIc2nRLkfkbfyejIujEKIuOPYZ1DbG36VeSM1MYlpzl+E7gJvvK1RuCcL4DKG +0uExI6HV2GdEq9kSe3Pj/512VTq+bXvMDRTcHMUkaN0NJ7GybHNk8J3fmFlB61zU +pZUNy0M14YYX8tZRMMw8Ke6ThJyj8ulSky4Cp2tfiGK9+YLP/UJkSm+0EOVAOMAt +LNvXtg5+/0e63M+stdf+F3txLuiYXiOG399tXlI61r4L5fKs0xau6P1V5uEPwAnQ +iXYVLCdahfGrUJIjHnHTU0TS2EpE8OxAu0krzQeONGSUg6SMM8vCP0d8yqQrYZGk +maFmIiTgOmy/fs+8u/ykautiOR/SviTR3hi/ofjZ+NTdT2Udg98BGuZBwKw+elaj +HUSUEkxtJVxeuFiVGzZNXkEhuxU6VNCnPeXxtl502rU9nmhmO2WJ0/1KX+oe/uTC +99b+olEPm72exsX0mwkSpIwDRBpX9meER4vJe4yX9fmotqEC2G30C9KYn+STcY9P +7jptJgLLuN61DVBjeMPLW+0NTjqmtplcu73zYvyCsG4rhIhY291wvz18iNLY7Bfe +hU3beEx68ApdLMue6xi9JlFKxHf5FHBnBSvD2xrR47rH9UMOHLglB+QkoidQ3Kug +HJ8r1sVHPhuS8mE7cENReFoNfh+N2wIDAQABAoICAQDwIbfqUJGo3mYqUVzGVBFU +Tp7yKIp9VulnZcCUoGGoRiiPMTKdu3OcWdQ4aQRs8aA5SBaI/1Be9UsHeetNcZTE +GZurSpTk4Tz0hhr3Fyrd7+VcSUOxAgykSICYrdQA6O9sYa5+nHxvb9IQA80lIXvG +fggT1KfMBXtDQd6FZVD10qhrU/OwbcFgY/gyEPrqMyafi7g0KIgginTOyizX7Vvt +TqI2hqZwhfnyx5UDmen9sdYh94qhV8w6DLE+fg1c77I7xb66L9Mm1fPG55tbyU8R +/jZgkB1RgDQDwn6Z83VqaH08OTLFT56izPXl77luCBz9N3UQ6Hg6dOlsyXumyLJL +cKjZ3Yoaqu9GHEauiaIJqPX4bN6O0TjG/vW0yKdGSFgh7jfjIiYCmMnb526PolKM +YN4xZ/KcdGa4QGKuX0hfgYLaOAM4U+V2Flb6QiSSta+UAFPOC7d7fEZIwYrgieB9 +10jnFqXqAef5w16Z1KZNI+X1FO5keHkOBqliXCdQJoLBwAt7nJFkgETatK5XCra1 +WR/iVcanZrvwD7pITZXbROcOF9MxKxkAqxU8Xeftr47r5XteuqSd3uRmsa7034QV +0TNV2OUBv4UCa5DpEftSjoX5GQKKXkAFix3QdW+RqiZ4tGVnrCUShhgg9H3aOu2y +dePbf3F5R7P9g4SbfKFGAQKCAQEA/GVCzoihnM9j+DmP63xoNyWNGVQ3foeZw/o6 +FvGYx5yUeYm+uwuq80hpd4KtHZoqbO3ohEsyZBHBwGjbmjxzwmrKbZukJ8Zxs7QQ +cB0YBmHduay59+MnmL3uiiLGsFedSlbReCJnFw+66yXfvDPVf4vs1I+DPtEIBkY5 +mOOdfP7b4vQtQbhtw4EAPSORbcR9ap4DCRmedp9aWTh48VNvqGLJIxRxveHcX/F2 +zAySGkw2s/pwQXq0htCComDn3X3yqw4y9WKirzmS5hYU4gsuk+dy3JSHhe0bsA8Y +daf4kEdutqyjYj1IaaDuvkiRW/6Pukb//R94/tejmwBLA5LXGwKCAQEA+K4HGruw +zlGKfQNc2uKX5uzB6N+rWQ//5oFhe/Lga2pQZlwDCay+3G9YWiVLP+wxEAkuoQcx +thfYFWLMy/8+Vyuiej/N73hWYXPgja7BA0d/j9/IKhtjvN2qIzA2xxKCkzqunXms +VnOoHVwhoqtVEZ9trB2gdO3ywE52aqSq131rVABOJDoFq9lVw4bl9Rj9wkp6D+tr +Dx1pMeKrBFKfxuCgAyj//BJrNSryxAxglAzyC75RKAPT6fvcw3Wcpnb10IXDt3Rd +g5YHMxas3g1fh2ieRVsQG4OvGytP9Uap6//AqM6c273Q86U3/pu3r8nvEMBKxdsx +pc4/raRsoUPQQQKCAQBNCBLFukmo9FsMjXTxaDzeZ+WSj3OIeJZji+Fi00XP1mgy +V+oQaFU6fyVBRm7TlBPSvyGyDslIZWr+8IHlpwGlmrZBkbkeMqDNOe2yag7FE+V6 +H896aqfRJFbDbi258GOfJrQzuDxCe5iO4DZS2HcWwHv9u/dQmreaQqCdmwqb9aTi +taeCYWmOu7Z48nwWRlwIyEUg5+LHTYdjp6qx7MctW0kMHddBHsgFuEqLqGKHCC/B +6nOMaIjkhIr6SB08Ko5/youe/QWt+SJuetrQypzio0cZL3PVWjKTH2hVsHhagJK3 +yiTrfMy3AFkdVkSXETCIp9bFSG/DR8k1K3e5lX11AoIBAAfSCT0o++VxIQbPbUMg +7x//ABYfupbBbw3DsdohCDe4jzC44guS2Cm8gq3LEHPBLMXRVBsSS9jrJQt/IOul +akN5htGLYiGOykCkUUKDZWSCAhv3MKdKVzegTPJwWLin911D8ivXoLjTSE0sEY65 +DqLQPbW09M/Yj9LGZOjzpr/CHPb2T37KKFWALzdH7cFoeMp8ZxxLDgHare04sKIh +Kw8pDz8qMequdZqlcB8EOKFPSuldodW9URPBrO6kqzl88jwNiNsjGLHDrRRJOUR+ +bSun+Zo6w+XpnT8gfJI9F6jpURi97qbmcETJRFqIcR1hH1iKg493VjddphkC27uy +k0ECggEAR1LyWFqTUxpP15EGA4vE64c0T6gmWmSyQiZ2VbMYWlgBCZJQN1EFDoFB +rLQvhy8jEU3zxbJPEOQmQL8OGzMBMgV/akEsTTEAPLbQc0ROSR7CW3YV8UlyBUP5 +4/WK1NUR8GXyeCjJSWHgn/LclkcFmyJ5DCKmesMRAodMhkHkqmSTZxPAYmlipn87 +PcGOoG02NlgjDADjwhRepRI7wVbb8HVXfGxhiPokAri/OgC/odnHIGzkdznur6JS +5eUoZkBcH16zBGxfwoGhqSGdip1BNNs2nUp4T0i+LGVKz8mYsZ9CdiGElfUwLpcc +eFet28DEMzSifudXY3LYE5N0Vl6g+A== +-----END PRIVATE KEY----- diff --git a/docker/ssl/certs/thing.crt b/docker/ssl/certs/thing.crt new file mode 100644 index 0000000000..a1eb60d1ca --- /dev/null +++ b/docker/ssl/certs/thing.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEODCCAyACFCcgfqlRnT0lLgivo40jvyko/V4gMA0GCSqGSIb3DQEBCwUAMFcx +EjAQBgNVBAMMCWxvY2FsaG9zdDERMA8GA1UECgwITWFpbmZsdXgxDDAKBgNVBAsM +A0lvVDEgMB4GCSqGSIb3DQEJARYRaW5mb0BtYWluZmx1eC5jb20wHhcNMTkwNDAx +MDkyNzEyWhcNMjEwMzMxMDkyNzEyWjBaMRAwDgYDVQQDDAdkZWZhdWx0MREwDwYD +VQQKDAhNYWluZmx1eDERMA8GA1UECwwIbWFpbmZsdXgxIDAeBgkqhkiG9w0BCQEW +EWluZm9AbWFpbmZsdXguY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +AgEAuNyXOzUfhH4WOvXgaOIV+ViqXEXO/VfjxPBE4EOYxxk1S5N1tAM5STontEqJ +kv8sSInZkYPPPjcqv8yEh1q5GyZrsI3Th5wqovFQEKUwAP2m0bluJYwY0oqo8dcZ +vPhcMdEdJu1hzRI0LOiBv5EgkT6KT5z97CfDtV16uaVISywnQmo1TcY7tT0tQs2S +znc4kg6mbfGnbmkEHfiV5aOj4ahULd5grdPh+8YcnWXNE/maSZJLOKz2PIMuySS7 +TmmmgJC6uMpsT5rCGI+WzNwYy8X6731zy/DVaMQ752QJfS0rUPwHa7KiEQNKAyrZ +NbYxiILKnbvd/JrN2iW6yllQDQn+XTft1aAui4fQcw/aBQZ5zbiedJeeDtKuve9+ +X23PVhRumsI4Wfo4CzKBsdH6fT5oGOqmL8WFVCQl/p93vqPRbhadEeEqGgdE2om/ +1SaA5nl7W4rbfo9beLpmi3KE+oldlLh5/mgH+7vWQQmmidC633AFaY7TabxU/59+ +38Kzo6eAJauVoHFdXGCIgg/SemNS1KWo3t+pwBHJIPHdsLlWsRVtV5Vt8QW+MlD1 +ODkApTstom0rtLvBoqBkI+2z29J+i07R4C2K/ZFdhv8Exf/MxUZeET+AznUwLHTE +SNxCsI/7wWQVyLVb1AwWLaBbt1cYd4YGVWe+QcslxNNayMkCAwEAATANBgkqhkiG +9w0BAQsFAAOCAQEAi7jvvUUMH2yVXfYgLUuBB8jRmwQcYKJo0jbPKZew07F+L3xM +WdYP+pDhdkyF79l99/fZS0Xs8dwYtAgU2tVkVoT6p/6vCvnqodgKgZJWi2dNCdG7 +ftIJR9dkusHIy3cpSHNb+A/hYLvj1nY9IAmRiY1fBNrRflmQe73gUuIjuoqDQ8wV +5jteUUt33rH0wYhbMf4z9HFSDBK1Ti+Mw27ybDYnYb79FZjUnXAKR/Gb0QyyGQyI +N5sVboXyBEK6KlJ4xBQZ0gEvmhN0ZGgmje4u7+2E3pJxo3zRN8Qm5Poqyll+3Omd +3rPdUhkTrQhKC3iMi+hXr4ZjNSlcgF5f+zvRIA== +-----END CERTIFICATE----- diff --git a/docker/ssl/certs/thing.key b/docker/ssl/certs/thing.key new file mode 100644 index 0000000000..cd7a6c980a --- /dev/null +++ b/docker/ssl/certs/thing.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +hqHUM7qo5k3nI6/dSdWQ1gBdmv104g== +-----END PRIVATE KEY----- diff --git a/docker/ssl/dhparam.pem b/docker/ssl/dhparam.pem new file mode 100644 index 0000000000..e0f2ebb78f --- /dev/null +++ b/docker/ssl/dhparam.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAquN8NRcSdLOM9RiumqWH8Jw3CGVR/eQQeq+jvT3zpxlUQPAMExQb +MRCspm1oRgDWGvch3Z4zfMmBZyzKJA4BDTh4USzcE5zvnx8aUcUPZPQpwSicKgzb +QGnl0Xf/75GAWrwhxn8GNyMP29wrpcd1Qg8fEQ3HAW1fCd9girKMKY9aBaHli/h2 +R9Rd/KTbeqN88aoMjUvZHooIIZXu0A+kyulOajYQO4k3Sp6CBqv0FFcoLQnYNH13 +kMUE5qJ68U732HybTw8sofTCOxKcCfM2kVP7dVoF3prlGjUw3z3l3STY8vuTdq0B +R7PslkoQHNmqcL+2gouoWP3GI+IeRzGSSwIBAg== +-----END DH PARAMETERS----- diff --git a/proxy-config.json b/proxy-config.json index 86a2f782fa..266852cea0 100644 --- a/proxy-config.json +++ b/proxy-config.json @@ -27,14 +27,14 @@ "target": "http://localhost:8182", "secure": false }, - "/writer/channels/*": { + "/writer/*": { "target": "http://localhost:8185", "secure": false, "pathRewrite": { "^/writer": "" } }, - "/reader/channels/*": { + "/reader/*": { "target": "http://localhost:8905", "secure": false, "pathRewrite": { @@ -45,7 +45,7 @@ "target": "http://localhost:8202", "secure": false, "pathRewrite": { - "^/bootstrap": "/things/bootstrap" + "^/bootstrap": "" } }, "/browse": { @@ -55,12 +55,5 @@ "/grafana/*": { "target": "http://localhost:3001", "secure": false - }, - "/configs/*": { - "target": "http://localhost:8202", - "secure": false, - "pathRewrite": { - "^/config": "/things/configs" - } } } diff --git a/src/app/common/services/bootstrap/bootstrap.service.ts b/src/app/common/services/bootstrap/bootstrap.service.ts index 440fad2f46..e19f976987 100644 --- a/src/app/common/services/bootstrap/bootstrap.service.ts +++ b/src/app/common/services/bootstrap/bootstrap.service.ts @@ -38,7 +38,7 @@ export class BootstrapService { state: 0, }; - return this.http.post(environment.configUrl, config, { observe: 'response' }) + return this.http.post(environment.bootstrapConfigsUrl, config, { observe: 'response' }) .map( resp => { const cfgID: string = resp.headers.get('location').replace('/things/configs/', ''); @@ -71,6 +71,6 @@ export class BootstrapService { } updateConfig(configUpdate: ConfigUpdate, gateway: Gateway) { - return this.http.put(`${environment.configUrl}/${gateway.id}`, configUpdate, { observe: 'response' }); + return this.http.put(`${environment.bootstrapConfigsUrl}/${gateway.id}`, configUpdate, { observe: 'response' }); } } diff --git a/src/app/pages/things/gateways/details/gateways.details.component.html b/src/app/pages/things/gateways/details/gateways.details.component.html index 6e8e98ddf4..69738c5617 100644 --- a/src/app/pages/things/gateways/details/gateways.details.component.html +++ b/src/app/pages/things/gateways/details/gateways.details.component.html @@ -9,7 +9,7 @@