├── .docker └── mosquitto │ ├── Dockerfile │ ├── mosquitto.conf │ └── users.txt ├── README.md ├── docker-compose.yml └── src └── main.go /.docker/mosquitto/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM eclipse-mosquitto 2 | 3 | ADD mosquitto.conf /mosquitto/config/mosquitto.conf 4 | ADD users.txt /mosquitto/config/users.txt 5 | 6 | WORKDIR /src 7 | 8 | EXPOSE 1883 9 | EXPOSE 9001 10 | -------------------------------------------------------------------------------- /.docker/mosquitto/mosquitto.conf: -------------------------------------------------------------------------------- 1 | # Config file for mosquitto 2 | # 3 | # See mosquitto.conf(5) for more information. 4 | # 5 | # Default values are shown, uncomment to change. 6 | # 7 | # Use the # character to indicate a comment, but only if it is the 8 | # very first character on the line. 9 | 10 | # ================================================================= 11 | # General configuration 12 | # ================================================================= 13 | 14 | # Time in seconds to wait before resending an outgoing QoS=1 or 15 | # QoS=2 message. 16 | #retry_interval 20 17 | 18 | # Time in seconds between updates of the $SYS tree. 19 | # Set to 0 to disable the publishing of the $SYS tree. 20 | #sys_interval 10 21 | 22 | # Time in seconds between cleaning the internal message store of 23 | # unreferenced messages. Lower values will result in lower memory 24 | # usage but more processor time, higher values will have the 25 | # opposite effect. 26 | # Setting a value of 0 means the unreferenced messages will be 27 | # disposed of as quickly as possible. 28 | #store_clean_interval 10 29 | 30 | # Write process id to a file. Default is a blank string which means 31 | # a pid file shouldn't be written. 32 | # This should be set to /var/run/mosquitto.pid if mosquitto is 33 | # being run automatically on boot with an init script and 34 | # start-stop-daemon or similar. 35 | #pid_file 36 | 37 | # When run as root, drop privileges to this user and its primary 38 | # group. 39 | # Leave blank to stay as root, but this is not recommended. 40 | # If run as a non-root user, this setting has no effect. 41 | # Note that on Windows this has no effect and so mosquitto should 42 | # be started by the user you wish it to run as. 43 | #user mosquitto 44 | 45 | # The maximum number of QoS 1 and 2 messages currently inflight per 46 | # client. 47 | # This includes messages that are partway through handshakes and 48 | # those that are being retried. Defaults to 20. Set to 0 for no 49 | # maximum. Setting to 1 will guarantee in-order delivery of QoS 1 50 | # and 2 messages. 51 | #max_inflight_messages 20 52 | 53 | # The maximum number of QoS 1 and 2 messages to hold in a queue 54 | # above those that are currently in-flight. Defaults to 100. Set 55 | # to 0 for no maximum (not recommended). 56 | # See also queue_qos0_messages. 57 | #max_queued_messages 100 58 | 59 | # Set to true to queue messages with QoS 0 when a persistent client is 60 | # disconnected. These messages are included in the limit imposed by 61 | # max_queued_messages. 62 | # Defaults to false. 63 | # This is a non-standard option for the MQTT v3.1 spec but is allowed in 64 | # v3.1.1. 65 | #queue_qos0_messages false 66 | 67 | # This option sets the maximum publish payload size that the broker will allow. 68 | # Received messages that exceed this size will not be accepted by the broker. 69 | # The default value is 0, which means that all valid MQTT messages are 70 | # accepted. MQTT imposes a maximum payload size of 268435455 bytes. 71 | #message_size_limit 0 72 | 73 | # This option controls whether a client is allowed to connect with a zero 74 | # length client id or not. This option only affects clients using MQTT v3.1.1 75 | # and later. If set to false, clients connecting with a zero length client id 76 | # are disconnected. If set to true, clients will be allocated a client id by 77 | # the broker. This means it is only useful for clients with clean session set 78 | # to true. 79 | #allow_zero_length_clientid true 80 | 81 | # If allow_zero_length_clientid is true, this option allows you to set a prefix 82 | # to automatically generated client ids to aid visibility in logs. 83 | #auto_id_prefix 84 | 85 | # This option allows persistent clients (those with clean session set to false) 86 | # to be removed if they do not reconnect within a certain time frame. 87 | # 88 | # This is a non-standard option in MQTT V3.1 but allowed in MQTT v3.1.1. 89 | # 90 | # Badly designed clients may set clean session to false whilst using a randomly 91 | # generated client id. This leads to persistent clients that will never 92 | # reconnect. This option allows these clients to be removed. 93 | # 94 | # The expiration period should be an integer followed by one of h d w m y for 95 | # hour, day, week, month and year respectively. For example 96 | # 97 | # persistent_client_expiration 2m 98 | # persistent_client_expiration 14d 99 | # persistent_client_expiration 1y 100 | # 101 | # The default if not set is to never expire persistent clients. 102 | #persistent_client_expiration 103 | 104 | # If a client is subscribed to multiple subscriptions that overlap, e.g. foo/# 105 | # and foo/+/baz , then MQTT expects that when the broker receives a message on 106 | # a topic that matches both subscriptions, such as foo/bar/baz, then the client 107 | # should only receive the message once. 108 | # Mosquitto keeps track of which clients a message has been sent to in order to 109 | # meet this requirement. The allow_duplicate_messages option allows this 110 | # behaviour to be disabled, which may be useful if you have a large number of 111 | # clients subscribed to the same set of topics and are very concerned about 112 | # minimising memory usage. 113 | # It can be safely set to true if you know in advance that your clients will 114 | # never have overlapping subscriptions, otherwise your clients must be able to 115 | # correctly deal with duplicate messages even when then have QoS=2. 116 | #allow_duplicate_messages false 117 | 118 | # The MQTT specification requires that the QoS of a message delivered to a 119 | # subscriber is never upgraded to match the QoS of the subscription. Enabling 120 | # this option changes this behaviour. If upgrade_outgoing_qos is set true, 121 | # messages sent to a subscriber will always match the QoS of its subscription. 122 | # This is a non-standard option explicitly disallowed by the spec. 123 | #upgrade_outgoing_qos false 124 | 125 | # ================================================================= 126 | # Default listener 127 | # ================================================================= 128 | 129 | # IP address/hostname to bind the default listener to. If not 130 | # given, the default listener will not be bound to a specific 131 | # address and so will be accessible to all network interfaces. 132 | # bind_address ip-address/host name 133 | #bind_address 134 | 135 | # Port to use for the default listener. 136 | #port 1883 137 | 138 | # The maximum number of client connections to allow. This is 139 | # a per listener setting. 140 | # Default is -1, which means unlimited connections. 141 | # Note that other process limits mean that unlimited connections 142 | # are not really possible. Typically the default maximum number of 143 | # connections possible is around 1024. 144 | #max_connections -1 145 | 146 | # Choose the protocol to use when listening. 147 | # This can be either mqtt or websockets. 148 | # Websockets support is currently disabled by default at compile time. 149 | # Certificate based TLS may be used with websockets, except that 150 | # only the cafile, certfile, keyfile and ciphers options are supported. 151 | #protocol mqtt 152 | 153 | # When a listener is using the websockets protocol, it is possible to serve 154 | # http data as well. Set http_dir to a directory which contains the files you 155 | # wish to serve. If this option is not specified, then no normal http 156 | # connections will be possible. 157 | #http_dir 158 | 159 | # Set use_username_as_clientid to true to replace the clientid that a client 160 | # connected with with its username. This allows authentication to be tied to 161 | # the clientid, which means that it is possible to prevent one client 162 | # disconnecting another by using the same clientid. 163 | # If a client connects with no username it will be disconnected as not 164 | # authorised when this option is set to true. 165 | # Do not use in conjunction with clientid_prefixes. 166 | # See also use_identity_as_username. 167 | #use_username_as_clientid 168 | 169 | # ----------------------------------------------------------------- 170 | # Certificate based SSL/TLS support 171 | # ----------------------------------------------------------------- 172 | # The following options can be used to enable SSL/TLS support for 173 | # this listener. Note that the recommended port for MQTT over TLS 174 | # is 8883, but this must be set manually. 175 | # 176 | # See also the mosquitto-tls man page. 177 | 178 | # At least one of cafile or capath must be defined. They both 179 | # define methods of accessing the PEM encoded Certificate 180 | # Authority certificates that have signed your server certificate 181 | # and that you wish to trust. 182 | # cafile defines the path to a file containing the CA certificates. 183 | # capath defines a directory that will be searched for files 184 | # containing the CA certificates. For capath to work correctly, the 185 | # certificate files must have ".crt" as the file ending and you must run 186 | # "c_rehash " each time you add/remove a certificate. 187 | #cafile 188 | #capath 189 | 190 | # Path to the PEM encoded server certificate. 191 | #certfile 192 | 193 | # Path to the PEM encoded keyfile. 194 | #keyfile 195 | 196 | # This option defines the version of the TLS protocol to use for this listener. 197 | # The default value allows v1.2, v1.1 and v1.0, if they are all supported by 198 | # the version of openssl that the broker was compiled against. For openssl >= 199 | # 1.0.1 the valid values are tlsv1.2 tlsv1.1 and tlsv1. For openssl < 1.0.1 the 200 | # valid values are tlsv1. 201 | #tls_version 202 | 203 | # By default a TLS enabled listener will operate in a similar fashion to a 204 | # https enabled web server, in that the server has a certificate signed by a CA 205 | # and the client will verify that it is a trusted certificate. The overall aim 206 | # is encryption of the network traffic. By setting require_certificate to true, 207 | # the client must provide a valid certificate in order for the network 208 | # connection to proceed. This allows access to the broker to be controlled 209 | # outside of the mechanisms provided by MQTT. 210 | #require_certificate false 211 | 212 | # If require_certificate is true, you may set use_identity_as_username to true 213 | # to use the CN value from the client certificate as a username. If this is 214 | # true, the password_file option will not be used for this listener. 215 | #use_identity_as_username false 216 | 217 | # If you have require_certificate set to true, you can create a certificate 218 | # revocation list file to revoke access to particular client certificates. If 219 | # you have done this, use crlfile to point to the PEM encoded revocation file. 220 | #crlfile 221 | 222 | # If you wish to control which encryption ciphers are used, use the ciphers 223 | # option. The list of available ciphers can be optained using the "openssl 224 | # ciphers" command and should be provided in the same format as the output of 225 | # that command. 226 | # If unset defaults to DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2:@STRENGTH 227 | #ciphers DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2:@STRENGTH 228 | 229 | # ----------------------------------------------------------------- 230 | # Pre-shared-key based SSL/TLS support 231 | # ----------------------------------------------------------------- 232 | # The following options can be used to enable PSK based SSL/TLS support for 233 | # this listener. Note that the recommended port for MQTT over TLS is 8883, but 234 | # this must be set manually. 235 | # 236 | # See also the mosquitto-tls man page and the "Certificate based SSL/TLS 237 | # support" section. Only one of certificate or PSK encryption support can be 238 | # enabled for any listener. 239 | 240 | # The psk_hint option enables pre-shared-key support for this listener and also 241 | # acts as an identifier for this listener. The hint is sent to clients and may 242 | # be used locally to aid authentication. The hint is a free form string that 243 | # doesn't have much meaning in itself, so feel free to be creative. 244 | # If this option is provided, see psk_file to define the pre-shared keys to be 245 | # used or create a security plugin to handle them. 246 | #psk_hint 247 | 248 | # Set use_identity_as_username to have the psk identity sent by the client used 249 | # as its username. Authentication will be carried out using the PSK rather than 250 | # the MQTT username/password and so password_file will not be used for this 251 | # listener. 252 | #use_identity_as_username false 253 | 254 | # When using PSK, the encryption ciphers used will be chosen from the list of 255 | # available PSK ciphers. If you want to control which ciphers are available, 256 | # use the "ciphers" option. The list of available ciphers can be optained 257 | # using the "openssl ciphers" command and should be provided in the same format 258 | # as the output of that command. 259 | #ciphers 260 | 261 | # ================================================================= 262 | # Extra listeners 263 | # ================================================================= 264 | 265 | # Listen on a port/ip address combination. By using this variable 266 | # multiple times, mosquitto can listen on more than one port. If 267 | # this variable is used and neither bind_address nor port given, 268 | # then the default listener will not be started. 269 | # The port number to listen on must be given. Optionally, an ip 270 | # address or host name may be supplied as a second argument. In 271 | # this case, mosquitto will attempt to bind the listener to that 272 | # address and so restrict access to the associated network and 273 | # interface. By default, mosquitto will listen on all interfaces. 274 | # listener port-number [ip address/host name] 275 | #listener 276 | 277 | # The maximum number of client connections to allow. This is 278 | # a per listener setting. 279 | # Default is -1, which means unlimited connections. 280 | # Note that other process limits mean that unlimited connections 281 | # are not really possible. Typically the default maximum number of 282 | # connections possible is around 1024. 283 | #max_connections -1 284 | 285 | # The listener can be restricted to operating within a topic hierarchy using 286 | # the mount_point option. This is achieved be prefixing the mount_point string 287 | # to all topics for any clients connected to this listener. This prefixing only 288 | # happens internally to the broker; the client will not see the prefix. 289 | #mount_point 290 | 291 | # Choose the protocol to use when listening. 292 | # This can be either mqtt or websockets. 293 | # Certificate based TLS may be used with websockets, except that only the 294 | # cafile, certfile, keyfile and ciphers options are supported. 295 | #protocol mqtt 296 | 297 | # When a listener is using the websockets protocol, it is possible to serve 298 | # http data as well. Set http_dir to a directory which contains the files you 299 | # wish to serve. If this option is not specified, then no normal http 300 | # connections will be possible. 301 | #http_dir 302 | 303 | # Set use_username_as_clientid to true to replace the clientid that a client 304 | # connected with with its username. This allows authentication to be tied to 305 | # the clientid, which means that it is possible to prevent one client 306 | # disconnecting another by using the same clientid. 307 | # If a client connects with no username it will be disconnected as not 308 | # authorised when this option is set to true. 309 | # Do not use in conjunction with clientid_prefixes. 310 | # See also use_identity_as_username. 311 | #use_username_as_clientid 312 | 313 | # ----------------------------------------------------------------- 314 | # Certificate based SSL/TLS support 315 | # ----------------------------------------------------------------- 316 | # The following options can be used to enable certificate based SSL/TLS support 317 | # for this listener. Note that the recommended port for MQTT over TLS is 8883, 318 | # but this must be set manually. 319 | # 320 | # See also the mosquitto-tls man page and the "Pre-shared-key based SSL/TLS 321 | # support" section. Only one of certificate or PSK encryption support can be 322 | # enabled for any listener. 323 | 324 | # At least one of cafile or capath must be defined to enable certificate based 325 | # TLS encryption. They both define methods of accessing the PEM encoded 326 | # Certificate Authority certificates that have signed your server certificate 327 | # and that you wish to trust. 328 | # cafile defines the path to a file containing the CA certificates. 329 | # capath defines a directory that will be searched for files 330 | # containing the CA certificates. For capath to work correctly, the 331 | # certificate files must have ".crt" as the file ending and you must run 332 | # "c_rehash " each time you add/remove a certificate. 333 | #cafile 334 | #capath 335 | 336 | # Path to the PEM encoded server certificate. 337 | #certfile 338 | 339 | # Path to the PEM encoded keyfile. 340 | #keyfile 341 | 342 | # By default an TLS enabled listener will operate in a similar fashion to a 343 | # https enabled web server, in that the server has a certificate signed by a CA 344 | # and the client will verify that it is a trusted certificate. The overall aim 345 | # is encryption of the network traffic. By setting require_certificate to true, 346 | # the client must provide a valid certificate in order for the network 347 | # connection to proceed. This allows access to the broker to be controlled 348 | # outside of the mechanisms provided by MQTT. 349 | #require_certificate false 350 | 351 | # If require_certificate is true, you may set use_identity_as_username to true 352 | # to use the CN value from the client certificate as a username. If this is 353 | # true, the password_file option will not be used for this listener. 354 | #use_identity_as_username false 355 | 356 | # If you have require_certificate set to true, you can create a certificate 357 | # revocation list file to revoke access to particular client certificates. If 358 | # you have done this, use crlfile to point to the PEM encoded revocation file. 359 | #crlfile 360 | 361 | # If you wish to control which encryption ciphers are used, use the ciphers 362 | # option. The list of available ciphers can be optained using the "openssl 363 | # ciphers" command and should be provided in the same format as the output of 364 | # that command. 365 | #ciphers 366 | 367 | # ----------------------------------------------------------------- 368 | # Pre-shared-key based SSL/TLS support 369 | # ----------------------------------------------------------------- 370 | # The following options can be used to enable PSK based SSL/TLS support for 371 | # this listener. Note that the recommended port for MQTT over TLS is 8883, but 372 | # this must be set manually. 373 | # 374 | # See also the mosquitto-tls man page and the "Certificate based SSL/TLS 375 | # support" section. Only one of certificate or PSK encryption support can be 376 | # enabled for any listener. 377 | 378 | # The psk_hint option enables pre-shared-key support for this listener and also 379 | # acts as an identifier for this listener. The hint is sent to clients and may 380 | # be used locally to aid authentication. The hint is a free form string that 381 | # doesn't have much meaning in itself, so feel free to be creative. 382 | # If this option is provided, see psk_file to define the pre-shared keys to be 383 | # used or create a security plugin to handle them. 384 | #psk_hint 385 | 386 | # Set use_identity_as_username to have the psk identity sent by the client used 387 | # as its username. Authentication will be carried out using the PSK rather than 388 | # the MQTT username/password and so password_file will not be used for this 389 | # listener. 390 | #use_identity_as_username false 391 | 392 | # When using PSK, the encryption ciphers used will be chosen from the list of 393 | # available PSK ciphers. If you want to control which ciphers are available, 394 | # use the "ciphers" option. The list of available ciphers can be optained 395 | # using the "openssl ciphers" command and should be provided in the same format 396 | # as the output of that command. 397 | #ciphers 398 | 399 | # ================================================================= 400 | # Persistence 401 | # ================================================================= 402 | 403 | # If persistence is enabled, save the in-memory database to disk 404 | # every autosave_interval seconds. If set to 0, the persistence 405 | # database will only be written when mosquitto exits. See also 406 | # autosave_on_changes. 407 | # Note that writing of the persistence database can be forced by 408 | # sending mosquitto a SIGUSR1 signal. 409 | #autosave_interval 1800 410 | 411 | # If true, mosquitto will count the number of subscription changes, retained 412 | # messages received and queued messages and if the total exceeds 413 | # autosave_interval then the in-memory database will be saved to disk. 414 | # If false, mosquitto will save the in-memory database to disk by treating 415 | # autosave_interval as a time in seconds. 416 | #autosave_on_changes false 417 | 418 | # Save persistent message data to disk (true/false). 419 | # This saves information about all messages, including 420 | # subscriptions, currently in-flight messages and retained 421 | # messages. 422 | # retained_persistence is a synonym for this option. 423 | #persistence false 424 | 425 | # The filename to use for the persistent database, not including 426 | # the path. 427 | #persistence_file mosquitto.db 428 | 429 | # Location for persistent database. Must include trailing / 430 | # Default is an empty string (current directory). 431 | # Set to e.g. /var/lib/mosquitto/ if running as a proper service on Linux or 432 | # similar. 433 | #persistence_location 434 | 435 | # ================================================================= 436 | # Logging 437 | # ================================================================= 438 | 439 | # Places to log to. Use multiple log_dest lines for multiple 440 | # logging destinations. 441 | # Possible destinations are: stdout stderr syslog topic file 442 | # 443 | # stdout and stderr log to the console on the named output. 444 | # 445 | # syslog uses the userspace syslog facility which usually ends up 446 | # in /var/log/messages or similar. 447 | # 448 | # topic logs to the broker topic '$SYS/broker/log/', 449 | # where severity is one of D, E, W, N, I, M which are debug, error, 450 | # warning, notice, information and message. Message type severity is used by 451 | # the subscribe/unsubscribe log_types and publishes log messages to 452 | # $SYS/broker/log/M/susbcribe or $SYS/broker/log/M/unsubscribe. 453 | # 454 | # The file destination requires an additional parameter which is the file to be 455 | # logged to, e.g. "log_dest file /var/log/mosquitto.log". The file will be 456 | # closed and reopened when the broker receives a HUP signal. Only a single file 457 | # destination may be configured. 458 | # 459 | # Note that if the broker is running as a Windows service it will default to 460 | # "log_dest none" and neither stdout nor stderr logging is available. 461 | # Use "log_dest none" if you wish to disable logging. 462 | #log_dest stderr 463 | 464 | # If using syslog logging (not on Windows), messages will be logged to the 465 | # "daemon" facility by default. Use the log_facility option to choose which of 466 | # local0 to local7 to log to instead. The option value should be an integer 467 | # value, e.g. "log_facility 5" to use local5. 468 | #log_facility 469 | 470 | # Types of messages to log. Use multiple log_type lines for logging 471 | # multiple types of messages. 472 | # Possible types are: debug, error, warning, notice, information, 473 | # none, subscribe, unsubscribe, websockets, all. 474 | # Note that debug type messages are for decoding the incoming/outgoing 475 | # network packets. They are not logged in "topics". 476 | #log_type error 477 | #log_type warning 478 | #log_type notice 479 | #log_type information 480 | 481 | # Change the websockets logging level. This is a global option, it is not 482 | # possible to set per listener. This is an integer that is interpreted by 483 | # libwebsockets as a bit mask for its lws_log_levels enum. See the 484 | # libwebsockets documentation for more details. "log_type websockets" must also 485 | # be enabled. 486 | #websockets_log_level 0 487 | 488 | # If set to true, client connection and disconnection messages will be included 489 | # in the log. 490 | #connection_messages true 491 | 492 | # If set to true, add a timestamp value to each log message. 493 | #log_timestamp true 494 | 495 | # ================================================================= 496 | # Security 497 | # ================================================================= 498 | 499 | # If set, only clients that have a matching prefix on their 500 | # clientid will be allowed to connect to the broker. By default, 501 | # all clients may connect. 502 | # For example, setting "secure-" here would mean a client "secure- 503 | # client" could connect but another with clientid "mqtt" couldn't. 504 | #clientid_prefixes 505 | 506 | # Boolean value that determines whether clients that connect 507 | # without providing a username are allowed to connect. If set to 508 | # false then a password file should be created (see the 509 | # password_file option) to control authenticated client access. 510 | # Defaults to true. 511 | allow_anonymous false 512 | 513 | # In addition to the clientid_prefixes, allow_anonymous and TLS 514 | # authentication options, username based authentication is also 515 | # possible. The default support is described in "Default 516 | # authentication and topic access control" below. The auth_plugin 517 | # allows another authentication method to be used. 518 | # Specify the path to the loadable plugin and see the 519 | # "Authentication and topic access plugin options" section below. 520 | #auth_plugin 521 | 522 | # ----------------------------------------------------------------- 523 | # Default authentication and topic access control 524 | # ----------------------------------------------------------------- 525 | 526 | # Control access to the broker using a password file. This file can be 527 | # generated using the mosquitto_passwd utility. If TLS support is not compiled 528 | # into mosquitto (it is recommended that TLS support should be included) then 529 | # plain text passwords are used, in which case the file should be a text file 530 | # with lines in the format: 531 | # username:password 532 | # The password (and colon) may be omitted if desired, although this 533 | # offers very little in the way of security. 534 | # 535 | # See the TLS client require_certificate and use_identity_as_username options 536 | # for alternative authentication options. 537 | #password_file 538 | 539 | # Access may also be controlled using a pre-shared-key file. This requires 540 | # TLS-PSK support and a listener configured to use it. The file should be text 541 | # lines in the format: 542 | # identity:key 543 | # The key should be in hexadecimal format without a leading "0x". 544 | #psk_file 545 | 546 | # Control access to topics on the broker using an access control list 547 | # file. If this parameter is defined then only the topics listed will 548 | # have access. 549 | # If the first character of a line of the ACL file is a # it is treated as a 550 | # comment. 551 | # Topic access is added with lines of the format: 552 | # 553 | # topic [read|write|readwrite] 554 | # 555 | # The access type is controlled using "read", "write" or "readwrite". This 556 | # parameter is optional (unless contains a space character) - if not 557 | # given then the access is read/write. can contain the + or # 558 | # wildcards as in subscriptions. 559 | # 560 | # The first set of topics are applied to anonymous clients, assuming 561 | # allow_anonymous is true. User specific topic ACLs are added after a 562 | # user line as follows: 563 | # 564 | # user 565 | # 566 | # The username referred to here is the same as in password_file. It is 567 | # not the clientid. 568 | # 569 | # 570 | # If is also possible to define ACLs based on pattern substitution within the 571 | # topic. The patterns available for substition are: 572 | # 573 | # %c to match the client id of the client 574 | # %u to match the username of the client 575 | # 576 | # The substitution pattern must be the only text for that level of hierarchy. 577 | # 578 | # The form is the same as for the topic keyword, but using pattern as the 579 | # keyword. 580 | # Pattern ACLs apply to all users even if the "user" keyword has previously 581 | # been given. 582 | # 583 | # If using bridges with usernames and ACLs, connection messages can be allowed 584 | # with the following pattern: 585 | # pattern write $SYS/broker/connection/%c/state 586 | # 587 | # pattern [read|write|readwrite] 588 | # 589 | # Example: 590 | # 591 | # pattern write sensor/%u/data 592 | # 593 | #acl_file 594 | 595 | # ----------------------------------------------------------------- 596 | # Authentication and topic access plugin options 597 | # ----------------------------------------------------------------- 598 | 599 | # If the auth_plugin option above is used, define options to pass to the 600 | # plugin here as described by the plugin instructions. All options named 601 | # using the format auth_opt_* will be passed to the plugin, for example: 602 | # 603 | # auth_opt_db_host 604 | # auth_opt_db_port 605 | # auth_opt_db_username 606 | # auth_opt_db_password 607 | 608 | 609 | # ================================================================= 610 | # Bridges 611 | # ================================================================= 612 | 613 | # A bridge is a way of connecting multiple MQTT brokers together. 614 | # Create a new bridge using the "connection" option as described below. Set 615 | # options for the bridges using the remaining parameters. You must specify the 616 | # address and at least one topic to subscribe to. 617 | # Each connection must have a unique name. 618 | # The address line may have multiple host address and ports specified. See 619 | # below in the round_robin description for more details on bridge behaviour if 620 | # multiple addresses are used. 621 | # The direction that the topic will be shared can be chosen by 622 | # specifying out, in or both, where the default value is out. 623 | # The QoS level of the bridged communication can be specified with the next 624 | # topic option. The default QoS level is 0, to change the QoS the topic 625 | # direction must also be given. 626 | # The local and remote prefix options allow a topic to be remapped when it is 627 | # bridged to/from the remote broker. This provides the ability to place a topic 628 | # tree in an appropriate location. 629 | # For more details see the mosquitto.conf man page. 630 | # Multiple topics can be specified per connection, but be careful 631 | # not to create any loops. 632 | # If you are using bridges with cleansession set to false (the default), then 633 | # you may get unexpected behaviour from incoming topics if you change what 634 | # topics you are subscribing to. This is because the remote broker keeps the 635 | # subscription for the old topic. If you have this problem, connect your bridge 636 | # with cleansession set to true, then reconnect with cleansession set to false 637 | # as normal. 638 | #connection 639 | #address [:] [[:]] 640 | #topic [[[out | in | both] qos-level] local-prefix remote-prefix] 641 | 642 | # Set the version of the MQTT protocol to use with for this bridge. Can be one 643 | # of mqttv31 or mqttv311. Defaults to mqttv31. 644 | #bridge_protocol_version mqttv31 645 | 646 | # If a bridge has topics that have "out" direction, the default behaviour is to 647 | # send an unsubscribe request to the remote broker on that topic. This means 648 | # that changing a topic direction from "in" to "out" will not keep receiving 649 | # incoming messages. Sending these unsubscribe requests is not always 650 | # desirable, setting bridge_attempt_unsubscribe to false will disable sending 651 | # the unsubscribe request. 652 | #bridge_attempt_unsubscribe true 653 | 654 | # If the bridge has more than one address given in the address/addresses 655 | # configuration, the round_robin option defines the behaviour of the bridge on 656 | # a failure of the bridge connection. If round_robin is false, the default 657 | # value, then the first address is treated as the main bridge connection. If 658 | # the connection fails, the other secondary addresses will be attempted in 659 | # turn. Whilst connected to a secondary bridge, the bridge will periodically 660 | # attempt to reconnect to the main bridge until successful. 661 | # If round_robin is true, then all addresses are treated as equals. If a 662 | # connection fails, the next address will be tried and if successful will 663 | # remain connected until it fails 664 | #round_robin false 665 | 666 | # Set the client id to use on the remote end of this bridge connection. If not 667 | # defined, this defaults to 'name.hostname' where name is the connection name 668 | # and hostname is the hostname of this computer. 669 | # This replaces the old "clientid" option to avoid confusion. "clientid" 670 | # remains valid for the time being. 671 | #remote_clientid 672 | 673 | # Set the clientid to use on the local broker. If not defined, this defaults to 674 | # 'local.'. If you are bridging a broker to itself, it is important 675 | # that local_clientid and clientid do not match. 676 | #local_clientid 677 | 678 | # Set the clean session variable for this bridge. 679 | # When set to true, when the bridge disconnects for any reason, all 680 | # messages and subscriptions will be cleaned up on the remote 681 | # broker. Note that with cleansession set to true, there may be a 682 | # significant amount of retained messages sent when the bridge 683 | # reconnects after losing its connection. 684 | # When set to false, the subscriptions and messages are kept on the 685 | # remote broker, and delivered when the bridge reconnects. 686 | #cleansession false 687 | 688 | # If set to true, publish notification messages to the local and remote brokers 689 | # giving information about the state of the bridge connection. Retained 690 | # messages are published to the topic $SYS/broker/connection//state 691 | # unless the notification_topic option is used. 692 | # If the message is 1 then the connection is active, or 0 if the connection has 693 | # failed. 694 | #notifications true 695 | 696 | # Choose the topic on which notification messages for this bridge are 697 | # published. If not set, messages are published on the topic 698 | # $SYS/broker/connection//state 699 | #notification_topic 700 | 701 | # Set the keepalive interval for this bridge connection, in 702 | # seconds. 703 | #keepalive_interval 60 704 | 705 | # Set the start type of the bridge. This controls how the bridge starts and 706 | # can be one of three types: automatic, lazy and once. Note that RSMB provides 707 | # a fourth start type "manual" which isn't currently supported by mosquitto. 708 | # 709 | # "automatic" is the default start type and means that the bridge connection 710 | # will be started automatically when the broker starts and also restarted 711 | # after a short delay (30 seconds) if the connection fails. 712 | # 713 | # Bridges using the "lazy" start type will be started automatically when the 714 | # number of queued messages exceeds the number set with the "threshold" 715 | # parameter. It will be stopped automatically after the time set by the 716 | # "idle_timeout" parameter. Use this start type if you wish the connection to 717 | # only be active when it is needed. 718 | # 719 | # A bridge using the "once" start type will be started automatically when the 720 | # broker starts but will not be restarted if the connection fails. 721 | #start_type automatic 722 | 723 | # Set the amount of time a bridge using the automatic start type will wait 724 | # until attempting to reconnect. Defaults to 30 seconds. 725 | #restart_timeout 30 726 | 727 | # Set the amount of time a bridge using the lazy start type must be idle before 728 | # it will be stopped. Defaults to 60 seconds. 729 | #idle_timeout 60 730 | 731 | # Set the number of messages that need to be queued for a bridge with lazy 732 | # start type to be restarted. Defaults to 10 messages. 733 | # Must be less than max_queued_messages. 734 | #threshold 10 735 | 736 | # If try_private is set to true, the bridge will attempt to indicate to the 737 | # remote broker that it is a bridge not an ordinary client. If successful, this 738 | # means that loop detection will be more effective and that retained messages 739 | # will be propagated correctly. Not all brokers support this feature so it may 740 | # be necessary to set try_private to false if your bridge does not connect 741 | # properly. 742 | #try_private true 743 | 744 | # Set the username to use when connecting to a broker that requires 745 | # authentication. 746 | # This replaces the old "username" option to avoid confusion. "username" 747 | # remains valid for the time being. 748 | #remote_username 749 | 750 | # Set the password to use when connecting to a broker that requires 751 | # authentication. This option is only used if remote_username is also set. 752 | # This replaces the old "password" option to avoid confusion. "password" 753 | # remains valid for the time being. 754 | #remote_password 755 | 756 | # ----------------------------------------------------------------- 757 | # Certificate based SSL/TLS support 758 | # ----------------------------------------------------------------- 759 | # Either bridge_cafile or bridge_capath must be defined to enable TLS support 760 | # for this bridge. 761 | # bridge_cafile defines the path to a file containing the 762 | # Certificate Authority certificates that have signed the remote broker 763 | # certificate. 764 | # bridge_capath defines a directory that will be searched for files containing 765 | # the CA certificates. For bridge_capath to work correctly, the certificate 766 | # files must have ".crt" as the file ending and you must run "c_rehash " each time you add/remove a certificate. 768 | #bridge_cafile 769 | #bridge_capath 770 | 771 | # Path to the PEM encoded client certificate, if required by the remote broker. 772 | #bridge_certfile 773 | 774 | # Path to the PEM encoded client private key, if required by the remote broker. 775 | #bridge_keyfile 776 | 777 | # When using certificate based encryption, bridge_insecure disables 778 | # verification of the server hostname in the server certificate. This can be 779 | # useful when testing initial server configurations, but makes it possible for 780 | # a malicious third party to impersonate your server through DNS spoofing, for 781 | # example. Use this option in testing only. If you need to resort to using this 782 | # option in a production environment, your setup is at fault and there is no 783 | # point using encryption. 784 | #bridge_insecure false 785 | 786 | # ----------------------------------------------------------------- 787 | # PSK based SSL/TLS support 788 | # ----------------------------------------------------------------- 789 | # Pre-shared-key encryption provides an alternative to certificate based 790 | # encryption. A bridge can be configured to use PSK with the bridge_identity 791 | # and bridge_psk options. These are the client PSK identity, and pre-shared-key 792 | # in hexadecimal format with no "0x". Only one of certificate and PSK based 793 | # encryption can be used on one 794 | # bridge at once. 795 | #bridge_identity 796 | #bridge_psk 797 | 798 | 799 | # ================================================================= 800 | # External config files 801 | # ================================================================= 802 | 803 | # External configuration files may be included by using the 804 | # include_dir option. This defines a directory that will be searched 805 | # for config files. All files that end in '.conf' will be loaded as 806 | # a configuration file. It is best to have this as the last option 807 | # in the main file. This option will only be processed from the main 808 | # configuration file. The directory specified must not contain the 809 | # main configuration file. 810 | #include_dir 811 | 812 | # ================================================================= 813 | # rsmb options - unlikely to ever be supported 814 | # ================================================================= 815 | 816 | #ffdc_output 817 | #max_log_entries 818 | #trace_level 819 | #trace_output -------------------------------------------------------------------------------- /.docker/mosquitto/users.txt: -------------------------------------------------------------------------------- 1 | username:$6$6jOr4vVqaKxisTls$4KVYh8NBZdP+z4S/YbuoSHKlJ+5F1DxiE7XtWWXVHQ+7PlCI+b6LhqSbj8lL45HnGlo4D5t0AVFYrYGjb5lTxg== 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Transforming TCP sockets to MQTT with Go 2 | 3 | In the last post we've created one proxy to upgrade one legacy application that sends raw TCP sockets to a HTTP server without changing the original application. 4 | 5 | Now we're going to do the same but instead sending HTTP request we're going to connect to a MQTT broker. Probably try to change the legacy application to connect to a MQTT broker can be a nightmare but with with this approach is pretty straightforward. 6 | 7 | The idea is the same. We're going to send our TCP sockets to localhost. Then we're going to build a go client that reads the TCP sockets and send the information to the MQTT broker. 8 | 9 | We're going to use Mosquitto as MQTT broker. We can set up easily with docker: 10 | 11 | ```yaml 12 | version: '2' 13 | 14 | services: 15 | mosquitto: 16 | image: eclipse-mosquitto 17 | hostname: mosquitto 18 | container_name: mosquitto 19 | build: 20 | context: .docker/mosquitto 21 | dockerfile: Dockerfile 22 | expose: 23 | - "1883" 24 | - "9001" 25 | ports: 26 | - "1883:1883" 27 | - "9001:9001" 28 | ``` 29 | 30 | We can also set up our Mosquitto server with user and password with mosquitto.conf and users.txt. For this example we're going to use the credentials: username:password 31 | 32 | ``` 33 | username:$6$6jOr4vVqaKxisTls$4KVYh8NBZdP+z4S/YbuoSHKlJ+5F1DxiE7XtWWXVHQ+7PlCI+b6LhqSbj8lL45HnGlo4D5t0AVFYrYGjb5lTxg== 34 | ``` 35 | 36 | Our Go program is very similar than the http version: 37 | 38 | ```go 39 | package main 40 | 41 | import ( 42 | "bufio" 43 | "encoding/json" 44 | "flag" 45 | mqtt "github.com/eclipse/paho.mqtt.golang" 46 | "log" 47 | "net" 48 | "os" 49 | "strings" 50 | "time" 51 | ) 52 | 53 | func main() { 54 | port, closeConnection, topic, broker := parseFlags() 55 | openSocket(*port, *closeConnection, *topic, *broker, onMessage) 56 | } 57 | 58 | func openSocket(port string, closeConnection bool, topic string, broker string, onMessage func(url string, topic string, buffer string)) { 59 | PORT := "localhost:" + port 60 | l, err := net.Listen("tcp4", PORT) 61 | log.Printf("Serving %s\n", l.Addr().String()) 62 | if err != nil { 63 | log.Fatalln(err) 64 | } 65 | defer l.Close() 66 | 67 | for { 68 | c, err := l.Accept() 69 | if err != nil { 70 | log.Fatalln(err) 71 | } 72 | go handleConnection(c, closeConnection, topic, broker, onMessage) 73 | } 74 | } 75 | 76 | func createClientOptions(url string) *mqtt.ClientOptions { 77 | opts := mqtt.NewClientOptions() 78 | opts.AddBroker(url) 79 | opts.SetUsername(os.Getenv("MQTT_USERNAME")) 80 | opts.SetPassword(os.Getenv("MQTT_PASSWORD")) 81 | return opts 82 | } 83 | 84 | func connect(url string) mqtt.Client { 85 | opts := createClientOptions(url) 86 | client := mqtt.NewClient(opts) 87 | token := client.Connect() 88 | for !token.WaitTimeout(3 * time.Second) { 89 | } 90 | if err := token.Error(); err != nil { 91 | log.Fatal(err) 92 | } 93 | return client 94 | } 95 | 96 | func onMessage(url string, topic string, buffer string) { 97 | client := connect(url) 98 | client.Publish(topic, 0, false, buffer) 99 | } 100 | 101 | func parseFlags() (*string, *bool, *string, *string) { 102 | port := flag.String("port", "7777", "port number") 103 | closeConnection := flag.Bool("close", true, "Close connection") 104 | topic := flag.String("topic", "topic", "mqtt topic") 105 | broker := flag.String("broker", "tcp://localhost:1883", "mqtt topic") 106 | flag.Parse() 107 | 108 | return port, closeConnection, topic, broker 109 | } 110 | 111 | func handleConnection(c net.Conn, closeConnection bool, topic string, broker string, onMessage func(url string, topic string, buffer string)) { 112 | log.Printf("Accepted connection from %s\n", c.RemoteAddr().String()) 113 | for { 114 | ip, port, err := net.SplitHostPort(c.RemoteAddr().String()) 115 | netData, err := bufio.NewReader(c).ReadString('\n') 116 | if err != nil { 117 | log.Println(err) 118 | } 119 | 120 | message := map[string]interface{}{ 121 | "body": strings.TrimSpace(netData), 122 | "ipFrom": ip, 123 | "port": port, 124 | } 125 | 126 | log.Printf("sending to topic %s message:%s\n", topic, message) 127 | bytesRepresentation, err := json.Marshal(message) 128 | if err != nil { 129 | log.Println(err) 130 | } else { 131 | onMessage(broker, topic, string(bytesRepresentation)) 132 | } 133 | 134 | if closeConnection { 135 | c.Close() 136 | return 137 | } 138 | } 139 | c.Close() 140 | } 141 | ``` 142 | 143 | And that's all. Our legacy application can now speak MQTT without problems 144 | 145 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | mosquitto: 5 | image: eclipse-mosquitto 6 | hostname: mosquitto 7 | container_name: mosquitto 8 | build: 9 | context: .docker/mosquitto 10 | dockerfile: Dockerfile 11 | expose: 12 | - "1883" 13 | - "9001" 14 | ports: 15 | - "1883:1883" 16 | - "9001:9001" 17 | -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "flag" 7 | mqtt "github.com/eclipse/paho.mqtt.golang" 8 | "log" 9 | "net" 10 | "os" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | func main() { 16 | port, closeConnection, topic, broker := parseFlags() 17 | openSocket(*port, *closeConnection, *topic, *broker, onMessage) 18 | } 19 | 20 | func openSocket(port string, closeConnection bool, topic string, broker string, onMessage func(url string, topic string, buffer string)) { 21 | PORT := "localhost:" + port 22 | l, err := net.Listen("tcp4", PORT) 23 | log.Printf("Serving %s\n", l.Addr().String()) 24 | if err != nil { 25 | log.Fatalln(err) 26 | } 27 | defer l.Close() 28 | 29 | for { 30 | c, err := l.Accept() 31 | if err != nil { 32 | log.Fatalln(err) 33 | } 34 | go handleConnection(c, closeConnection, topic, broker, onMessage) 35 | } 36 | } 37 | 38 | func createClientOptions(url string) *mqtt.ClientOptions { 39 | opts := mqtt.NewClientOptions() 40 | opts.AddBroker(url) 41 | opts.SetUsername(os.Getenv("MQTT_USERNAME")) 42 | opts.SetPassword(os.Getenv("MQTT_PASSWORD")) 43 | return opts 44 | } 45 | 46 | func connect(url string) mqtt.Client { 47 | opts := createClientOptions(url) 48 | client := mqtt.NewClient(opts) 49 | token := client.Connect() 50 | for !token.WaitTimeout(3 * time.Second) { 51 | } 52 | if err := token.Error(); err != nil { 53 | log.Fatal(err) 54 | } 55 | return client 56 | } 57 | 58 | func onMessage(url string, topic string, buffer string) { 59 | client := connect(url) 60 | client.Publish(topic, 0, false, buffer) 61 | } 62 | 63 | func parseFlags() (*string, *bool, *string, *string) { 64 | port := flag.String("port", "7777", "port number") 65 | closeConnection := flag.Bool("close", true, "Close connection") 66 | topic := flag.String("topic", "topic", "mqtt topic") 67 | broker := flag.String("broker", "tcp://localhost:1883", "mqtt topic") 68 | flag.Parse() 69 | 70 | return port, closeConnection, topic, broker 71 | } 72 | 73 | func handleConnection(c net.Conn, closeConnection bool, topic string, broker string, onMessage func(url string, topic string, buffer string)) { 74 | log.Printf("Accepted connection from %s\n", c.RemoteAddr().String()) 75 | for { 76 | ip, port, err := net.SplitHostPort(c.RemoteAddr().String()) 77 | netData, err := bufio.NewReader(c).ReadString('\n') 78 | if err != nil { 79 | log.Println(err) 80 | } 81 | 82 | message := map[string]interface{}{ 83 | "body": strings.TrimSpace(netData), 84 | "ipFrom": ip, 85 | "port": port, 86 | } 87 | 88 | log.Printf("sending to topic %s message:%s\n", topic, message) 89 | bytesRepresentation, err := json.Marshal(message) 90 | if err != nil { 91 | log.Println(err) 92 | } else { 93 | onMessage(broker, topic, string(bytesRepresentation)) 94 | } 95 | 96 | if closeConnection { 97 | c.Close() 98 | return 99 | } 100 | } 101 | c.Close() 102 | } 103 | --------------------------------------------------------------------------------