commit d28fb1f24fc89dfa577b6d43bdef6ca5f3b617c3 Author: Saber Karmous Date: Wed Oct 24 21:18:46 2018 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..50357b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# OS generated files # +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +.rnd diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000..988f1df --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +FROM python:2.7-alpine +LABEL maintainer bitrox + +# Set environment variables. +ENV TERM=xterm-color +ENV SHELL=/bin/bash + +RUN \ + mkdir /mosquitto && \ + mkdir /mosquitto/log && \ + mkdir /mosquitto/conf && \ + apk update && \ + apk upgrade && \ + apk add \ + bash \ + coreutils \ + nano \ + py-crypto \ + ca-certificates \ + certbot \ + mosquitto \ + mosquitto-clients && \ + rm -f /var/cache/apk/* && \ + pip install --upgrade pip && \ + pip install pyRFC3339 configobj ConfigArgParse + +COPY run.sh /run.sh +COPY certbot.sh /certbot.sh +COPY restart.sh /restart.sh +COPY croncert.sh /etc/periodic/weekly/croncert.sh +RUN \ + chmod +x /run.sh && \ + chmod +x /certbot.sh && \ + chmod +x /restart.sh && \ + chmod +x /etc/periodic/weekly/croncert.sh + +EXPOSE 1883 +EXPOSE 8883 +EXPOSE 80 + +# This will run any scripts found in /scripts/*.sh +# then start Apache +CMD ["/bin/bash","-c","/run.sh"] diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..99b657b --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 Bitrox.io + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100755 index 0000000..899802e --- /dev/null +++ b/README.md @@ -0,0 +1,206 @@ +# alpine-mosquitto-certbot + +An automated build that integrates the [Mosquitto MQTT server](https://mosquitto.org/) with [Certbot](https://certbot.eff.org/) on top of [Alpine linux](https://www.alpinelinux.org/). + +As the Internet of Things (IoT) world rapidly grows and evolves, developers need a simple and secure way to implement peer-to-peer and peer-to-server (backend) communications. MQTT is a relatively simple message/queue-based protocol that provides exactly that. +Unfortunately, there are a ton of Docker images available for brokers, e.g. eclipse-mosquitto; but, nearly all of them leave it up to the user to figure out how to secure the platform. This results in many developers simply ignoring security altogether for the sake of simplicity. Even more dangerous, many semi-technical home owners are now dabbling in the home automation space and due to the complexity of securing system, they are hanging IoT/automation devices on the internet completely unsecurred. + +This docker image attempts to make it easier to implement a secure MQTT broker, either in the cloud or on premise. The secured broker can be used with home automation platforms like [Home Assistant](https://home-assistant.io/) or simply as a means of enabling secure IoT device communications. + +For those interested in some of the nuts and bolts related to the integration, reference [Brian Boucheron's](https://www.digitalocean.com/community/users/bboucheron) excellent article [How to Install and Secure the Mosquitto MQTT Messaging Broker on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-the-mosquitto-mqtt-messaging-broker-on-ubuntu-16-04) which servered as a reference in creating this image. + +## Standing up a server + +A straigtforward way of standing up a server is to use [docker-compose](https://docs.docker.com/compose/). Here is a sample [docker-compose.yml](https://docs.docker.com/compose/compose-file/) file: + +``` +version: '2' +services: + mqtt: + image: bitrox/alpine-mosquitto-certbot + networks: + - backend-net + ports: + - 1883:1883 + - 8083:8083 + - 8883:8883 + - 80:80 + environment: + - DOMAIN=mqtt.myserver.com + - EMAIL=myemail@myprovider.com + volumes: + - ./mosquitto/conf/:/mosquitto/conf + - ./mosquitto/log/:/mosquitto/log + - ./letsencrypt:/etc/letsencrypt + - ./scripts:/scripts + container_name: mqtt + restart: always +networks: + backend-net: + external: + name: backend-net +``` + +In this case, four ports are exposed, which we'll go over in more detail when describing how this configuration matches that of the mosquitto.conf file. The first three ports are associated with Mosquitto, the forth port mapping (80:80) allows Certbot/LetsEncrypt to verify the DOMAIN. Also shown in the yml file is a backend-net network, which you many or may not have implemented with your particular Docker environment (Docker networking is WAY beyond the scope of this discussion). + +## Environment Variables + +There are three environment variables useable with this image. DOMAIN and EMAIL are required for Certbot/[Letencrypt](https://letsencrypt.org/) to obtain certificates necessary for secure communications. The third, TESTCERT, is optional. + +**DOMAIN** - This should be defined as your fully qualified domain name, i.e. mqtt.myserver.com. The domain needs to point to your server as LetsEncrypt will verify such when obtaining certificates. + +**EMAIL** - This simply needs to be an email address. It's required by certbot/LetsEncrypt to obtain certificates. + +**TESTCERT** - This variable can be set to any value (e.g. TRUE). When defined, the image will utilize certbot/LetsEncrypt --staging server and obtain non-valid test-certs. It will also use --dry-run when simulating certificate renewal. The variable's utility is in the fact that it enables the user to configure and test/debug the process of obtaining certificates without running into the fairly low hourly limits imposed by LetsEncrypt. Example: If the service is configured correctly, but the server isn't reachable on port 80 due to an incorrectly configured firewall, TESTCERT defined will reveal this fact without attempting to obtain a real certificate. + +## Volumes (persistence) + +The scripts associated with this image assume a standard directory structure for mosquitto configuration and certbot/LetsEncrypt. It is possible to deviate from the below defined standard, but doing so should be left to those more familiar with Docker and Mosquitto. + +``` +/mosquitto/conf/ + mosquitto.conf + passwd +/mosquitto/log/ +/letsencrypt/ +``` + +The docker-compose.yml file shown above maps local (persistent) directories to the relevant container volumes: + +**/mosquitto/conf/** - this directory is where Mosquitto will look for the mosquitto.conf file. + +**/mosquitto/conf/mosquitto.conf** - this file is user supplied. The startup scripts will look for exactly this file in exactly this directory. If it isn't found, the container will exit with appropriate error messages. + +**/mosquitto/conf/passwd** - this file is the standard location for Mosquitto users/passwords. An alternate file/location can be specified in mosquitto.conf, but it must be in a location persisted through docker volume mapping. It's presence/use is optional, but allowing anonymous access to MQTT somewhat defeats the purpose of this image. + +**/mosquitto/log** - This directory is the location where mosquitto will place log file(s). Like passwd defined above, its use is optional and can be controlled based on the contents of mosquitto.conf. + +**/letsencrypt** - This directory is where certbot/LetsEncrypt will place retrieved certificates. The certbot scripts specifically require/expect this directory to exist in the container, so it should be mapped. + +**/scripts** - To enable customization of the container, the run.sh script looks for this directory. If it finds /scripts, it will look inside the directory for any file ending in .sh, e.g. myscript.sh. It will then attempt to execuite said script(s) during container startup, immediately after dealing with certbot/LetsEncrypt, but before starting Mosquitto. Scripts found will be executed in alpha order. A suggested naming convention for scripts include a number followed by a dash, then the script name, ending in .sh, e.g. 00-myfirstscript.sh, 01-mysecondscript.sh, etc. This will ensure your scripts are executed in the order intended. An example of this functionality would be if you want additional software/utilities in the container. + +The sample docker-compose.yml file shows a local directory ./scripts mapped to the container volume /scripts where run.sh will look for the above discussed user scripts to run at startup. + +## Certbot/LetsEncrypt Integration + +At container startup, scripts will look to see if certificates for DOMAIN exist in /letsencrypt. If it doesn't find any certificates, it will attempt to obtain them (via certbot certonly --standalone --agree-tos --standalone-supported-challenges http-01 -n -d $DOMAIN -m $EMAIL). +If certificates do exist, then an attempt will be made to renew them (via certbot renew). +Once a week, scripts will be run to check to see if the certificates need renewal. If so, they will be renewed, then the mosquitto server will be restarted so that it picks up the new certificates. Unfortunately, this does mean that there will be a brief (few second) outage each time certificates are in fact renewed. Adjust use cases for this server accordingly. + +## mosquitto.conf + +Documentation for Mosquitto should be consulted for details on how to properly configure this file. However, below is a sample configuration file that matches the docker-compose.yml shown above. + + In the below configuration, we make mosquitto available via three different ports. Port 1883 uses the standard mqtt protocol. It is accessible without TLS/SSL, but does require user id/password verification (defined in /mosquitto/conf/passwd). The use case for 1883 is that it is expose internally to other processes/servers on a private network. Port 8883 provides accessiblity via the mqtt protocol, but requires TLS/SSL. The use case is that port 8883 is exposed to the internet, accessible via DOMAIN. And lastly, port 8083 allows the server to be accessed via websockets. It also requires TLS/SSL. Again it's use case would be that port 8083 is exposed to the internet, accessible via DOMAIN. + + Logging is enabled and the directory for storing log files is defined as /mosquitto/log. The highest level of detail for logging is enabled. Consult [Mosquitto documentation](https://mosquitto.org/documentation/) for the logging parameters if you want a lesser level of logging turned on once you have the server debugged and integrated with your other devices/software. + + Anonymous access to the server is disabled, indicating all connections must be validated via user id/password. + +``` +# Config file for mosquitto +# +# See mosquitto.conf(5) for more information. +# + +# ================================================================= +# General configuration +# ================================================================= + +# When run as root, drop privileges to this user and its primary +# group. +# Leave blank to stay as root, but this is not recommended. +# If run as a non-root user, this setting has no effect. +# Note that on Windows this has no effect and so mosquitto should +# be started by the user you wish it to run as. +user mosquitto + +# ================================================================= +# Default listener +# ================================================================= + +port 1883 +protocol mqtt + +# ================================================================= +# Extra listeners +# ================================================================= + +listener 8083 +protocol websockets +cafile /etc/letsencrypt/live/mqtt.bitrox.io/chain.pem +certfile /etc/letsencrypt/live/mqtt.bitrox.io/fullchain.pem +keyfile /etc/letsencrypt/live/mqtt.bitrox.io/privkey.pem + +listener 8883 +protocol mqtt +cafile /etc/letsencrypt/live/mqtt.bitrox.io/chain.pem +certfile /etc/letsencrypt/live/mqtt.bitrox.io/fullchain.pem +keyfile /etc/letsencrypt/live/mqtt.bitrox.io/privkey.pem + +# ================================================================= +# Logging +# ================================================================= + +log_dest file /mosquitto/log/mosquitto.log +log_type all +websockets_log_level 255 +connection_messages true +log_timestamp true + +# ================================================================= +# Security +# ================================================================= + +allow_anonymous false + +# ----------------------------------------------------------------- +# Default authentication and topic access control +# ----------------------------------------------------------------- + +# Control access to the broker using a password file. This file can be +# generated using the mosquitto_passwd utility. +password_file /mosquitto/conf/passwd + +## Generating User ID/Password + +Mosquitto provides a utility (mosquitto_passwd) for adding users to a password file with encrypted passwords. Assuming the passwd file is in the standard location as shown in the mosquitto.conf file above, you can add a user/password combination (e.g. booboy myPwd123) to the file once the docker container is up and running, using the following command: + +docker exec -it mqtt mosquitto_passwd -b /mosquitto/conf/passwd booboy myPwd123 + +This command doesn't provide any feedback if successful, but does show errors if there are problems. You can verify success simply looking in the passwd file. You should see an entry similar to: booboy:$6$+NKkI0p3oZmSukn9$mOUEEHUizK2zqc8Hk2l0JlHHXTW8GPzSonP9Ujrjhs1tVNQqN3lGCAFcFKnpJefOjUPwjqE5mZ +qSjBl6BCKnPA== +``` + +## Testing Your Server + +To test your server locally (i.e. within the container), you can pop into the container and use mosquitto_pub and mosquitto_sub. Note that you'll need to do this from two separate terminal sessions so see the effect. If you receive error messages, look in the mosquitto error log (/mosquitto/log) for diagnostic information. You should also make sure the container came up properly using a command like: + +``` +docker logs mqtt +``` + +For the MQTT subscriber: + +``` +docker exec -it mqtt /bin/bash +mosquitto_sub -h -u "booboy" -P "myPwd123" -t "testQueue" +``` + +The mosquitto_sub command will block waiting for messages from ++testQueue++. + +To publish a message to ++testQueue++, open another terminal and use the following: + +``` +docker exec -it mqtt /bin/bash +mosquitto_pub -h -u "booboy" -P "myPwd123" -t "testQueue" -m "Hello subscribers to testQueue!" +``` + +In the first (subscriber) terminal window, you should immediately see the message "Hello subscribers to testQueue!". + +To test remotely, [mqtt-admin](https://hobbyquaker.github.io/mqtt-admin/) by [Sebastian Raff](https://hobbyquaker.github.io) is an excellent resource. Note that you need to make sure any filewalls/routers between your container and the internet are properly configured to route requests on the ports specified before attempting to use mqtt-admin. + + + + + diff --git a/certbot.sh b/certbot.sh new file mode 100755 index 0000000..6b5b092 --- /dev/null +++ b/certbot.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# Check to see if certs for the specified domain exist +# Attempt to retrieve certs if missing or +# if present attempt to renew them +# +# WARNING: +# This script is called weekly from /etc/periodic/weekly/croncert.sh +# +# Duing the weekly check, if certs are renewed, +# the mosquitto process is restarted, causing +# a brief (few second) unavoidable service disruption +# +# If the environment varialbe TESTCERT is defined, this script +# will use --staging --test-cert for obtaining a cert and --dry-run for renewal +# This allows the user to test out the configuration and connectivity for obtaining +# certs without running into LetsEncrypt limits. It's advisable to define TESTCERT +# when initially bringing up the container. Once the logs (docker logs ) +# show that LetsEncrypt is working fine, then remove TESTCERT environment variable +# to let this script obtain and manage the real certificates +# + +FOLDER="/etc/letsencrypt/live/$DOMAIN" +echo "Dealing with certificates..." +echo "Location: $FOLDER" +if [ -d "$FOLDER" ]; then + echo "Certificates exist, attempting to renew..." + if [ ! -z "$TESTCERT" ]; then + echo "Renew dry run ..." + certbot renew --dry-run --noninteractive --post-hook "/restart.sh" + else + echo "Renew certs ..." + certbot renew --noninteractive --post-hook "/restart.sh" + fi +else + if [ ! -z "$DOMAIN" ]; then + if [ ! -z "$EMAIL" ]; then + if [ ! -z "$TESTCERT" ]; then + echo "Obtaining TEST cert for $DOMAIN" + certbot certonly \ + --staging \ + --test-cert \ + --standalone \ + --agree-tos \ + --standalone-supported-challenges http-01 \ + -n \ + -d $DOMAIN \ + -m $EMAIL + else + echo "Obtaining cert for $DOMAIN" + certbot certonly \ + --standalone \ + --agree-tos \ + --standalone-supported-challenges http-01 \ + -n \ + -d $DOMAIN \ + -m $EMAIL + fi + else + echo 'ERROR: $EMAIL must be defined' + fi + else + echo 'ERROR: $DOMAIN must be defined' + fi +fi diff --git a/croncert.sh b/croncert.sh new file mode 100755 index 0000000..dcbb188 --- /dev/null +++ b/croncert.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo "Checking on certificates..." +/certbot.sh diff --git a/restart.sh b/restart.sh new file mode 100755 index 0000000..595770e --- /dev/null +++ b/restart.sh @@ -0,0 +1,10 @@ +#!/bin/bash +ps cax | grep mosquitto > /dev/null +if [ $? -eq 0 ]; then + echo "Mosquitto is running." + pkill -f "mosquitto" + sleep 1 + /usr/sbin/mosquitto -c /mosquitto/conf/mosquitto.conf& +else + echo "Mosquitto is not running." +fi diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..4ec403e --- /dev/null +++ b/run.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Check and if needed install/renew certs +# Note that this script (certbot.sh) is also +# run weekely from /etc/periodic/weekly/croncert.sh +# +# WARNING: +# Duing the weekly check, if certs are renewed, +# the mosquitto process is restarted, causing +# a brief (few second) unavoidable service disruption +# +/certbot.sh + +# This script assumes a standard persistent directory and file layout of: +# /mosquitto/ +# conf/ +# mosquitto.conf - the main configuation file +# passwd - the password file +# log/ +# +# The presense and location of mosquitto.conf isn't optional. +# (We could allow user definition via environment var, but honestly why bother) +# +# The location of the log directory and passwd file can be +# mapped differently in mosquitto.conf. If so, this script will +# simply generate warnings, but continue to function. +# +if [ ! -d "/mosquitto/log" ]; then + echo "WARNING: missing /mosquitto/log directory" + echo "WARNING: ignore if your mosquitto.conf has a non-standard configuration" +fi + +# create blank passwd if it doesn't exist +if [ -d "/mosquitto/conf" ]; then + if [ ! -f "/mosquitto/conf/passwd" ]; then + echo "Creating blank passwd file at /mosquitto/conf/passwd" + touch /mosquitto/conf/passwd + fi +else + echo "WARNING: /mosquitto/conf should be mapped to persistent docker volume" + echo "WARNING: ignore if your mosquitto.conf has a non-standard configuration" +fi + +# execute any pre-exec scripts, useful for customization of images +if [ -d "/scripts" ]; then + echo "Looking for user scripts to execute..." + for i in /scripts/*sh + do + if [ -e "${i}" ]; then + echo "Found user script - processing $i" + . "${i}" + fi + done +fi + +echo "Starting mosquitto process (daemon)..." +if [ -f "/mosquitto/conf/mosquitto.conf" ]; then + # Note that this method of starting mosquitto results in the process + # not receiving the SIGTERM signal from Docker on shutdown. This is + # nessary because mosquitto must be restarted automatically when + # certificates are renewed. In other words, we need the container to + # continue running beyond the life of the mosquitto process. + # + # A possible enhancement would be to include an "is alive" check + # for mosquitto to restart it if required or exit the container. + /usr/sbin/mosquitto -c /mosquitto/conf/mosquitto.conf& + echo "Going to sleep..." + # sleep infinity not available, so 9999d should be an acceptable substitute :-) + sleep 9999d +else + echo "ERROR: missing /mosquitto/conf/mosquitto.conf" + echo "ERROR: check your Docker volume mappings" + echo "Exiting..." +fi diff --git a/sample/docker-compose.yml b/sample/docker-compose.yml new file mode 100755 index 0000000..f52a532 --- /dev/null +++ b/sample/docker-compose.yml @@ -0,0 +1,25 @@ +version: '2' +services: + mqtt: + image: bitrox/mqtt + networks: + - backend-net + ports: + - 1883:1883 + - 8083:8083 + - 8883:8883 + - 8116:80 + environment: + - DOMAIN=mqtt.bitrox.io + - EMAIL=proxy@bitrox.io + volumes: + - ./mosquitto/conf/:/mosquitto/conf + - ./mosquitto/log/:/mosquitto/log + - ./letsencrypt:/etc/letsencrypt + - ./scripts:/scripts + container_name: mqtt + restart: always +networks: + backend-net: + external: + name: backend-net diff --git a/sample/mosquitto.conf b/sample/mosquitto.conf new file mode 100755 index 0000000..3018d93 --- /dev/null +++ b/sample/mosquitto.conf @@ -0,0 +1,64 @@ +# Config file for mosquitto +# +# See mosquitto.conf(5) for more information. +# + +# ================================================================= +# General configuration +# ================================================================= + +# When run as root, drop privileges to this user and its primary +# group. +# Leave blank to stay as root, but this is not recommended. +# If run as a non-root user, this setting has no effect. +# Note that on Windows this has no effect and so mosquitto should +# be started by the user you wish it to run as. +user mosquitto + +# ================================================================= +# Default listener +# ================================================================= + +port 1883 +protocol mqtt + +# ================================================================= +# Extra listeners +# ================================================================= + +listener 8083 +protocol websockets +cafile /etc/letsencrypt/live/mqtt.bitrox.io/chain.pem +certfile /etc/letsencrypt/live/mqtt.bitrox.io/fullchain.pem +keyfile /etc/letsencrypt/live/mqtt.bitrox.io/privkey.pem + +listener 8883 +protocol mqtt +cafile /etc/letsencrypt/live/mqtt.bitrox.io/chain.pem +certfile /etc/letsencrypt/live/mqtt.bitrox.io/fullchain.pem +keyfile /etc/letsencrypt/live/mqtt.bitrox.io/privkey.pem + +# ================================================================= +# Logging +# ================================================================= + +log_dest file /mosquitto/log/mosquitto.log +log_type all +websockets_log_level 255 +connection_messages true +log_timestamp true + +# ================================================================= +# Security +# ================================================================= + +allow_anonymous false + +# ----------------------------------------------------------------- +# Default authentication and topic access control +# ----------------------------------------------------------------- + +# Control access to the broker using a password file. This file can be +# generated using the mosquitto_passwd utility. +password_file /mosquitto/conf/passwd +