diff --git a/.gitignore b/.gitignore index 13fc46fe..2c01e4c4 100644 --- a/.gitignore +++ b/.gitignore @@ -42,5 +42,4 @@ api/tacticalrmm/accounts/management/commands/random_data.py versioninfo.go resource.syso htmlcov/ -docker-compose.override.yml docker-compose.dev.yml \ No newline at end of file diff --git a/api/tacticalrmm/core/management/commands/initial_mesh_setup.py b/api/tacticalrmm/core/management/commands/initial_mesh_setup.py index e837c1f3..79a5835a 100644 --- a/api/tacticalrmm/core/management/commands/initial_mesh_setup.py +++ b/api/tacticalrmm/core/management/commands/initial_mesh_setup.py @@ -50,26 +50,26 @@ class Command(BaseCommand): def handle(self, *args, **kwargs): self.mesh_settings = CoreSettings.objects.first() - # set mesh token if not set - if not self.mesh_settings.mesh_token and not hasattr( - settings, "MESH_TOKEN_KEY" - ): - filepath = "/opt/tactical/tmp/mesh_token" + try: + # Check for Mesh Username + if not self.mesh_settings.mesh_username and settings.MESH_USERNAME: + self.mesh_settings.mesh_username = settings.MESH_USERNAME - try: - with open(filepath, "r") as read_file: - key = read_file.readlines() + # Check for Mesh Site + if not self.mesh_settings.mesh_site and settings.MESH_SITE: + self.mesh_settings.mesh_site = settings.MESH_SITE - # Remove key file contents for security reasons - with open(filepath, "w") as write_file: - write_file.write("") + # Check for Mesh Token + if not self.mesh_settings.mesh_token and settings.MESH_TOKEN_KEY: + self.mesh_settings.mesh_token = settings.MESH_TOKEN_KEY - # readlines() returns an array. Get first item - self.mesh_settings.mesh_token = key[0].rstrip() - self.mesh_settings.save() - except: - self.stdout.write("Mesh Central key wasn't found") - return + except AttributeError: + pass - asyncio.get_event_loop().run_until_complete(self.websocket_call()) - self.stdout.write("Initial Mesh Central setup complete") + if self.mesh_settings.mesh_token: + asyncio.get_event_loop().run_until_complete(self.websocket_call()) + self.stdout.write("Initial Mesh Central setup complete") + else: + self.stdout.write( + "Mesh Setup was skipped being the token wasn't set. Set it up manually." + ) diff --git a/api/tacticalrmm/core/models.py b/api/tacticalrmm/core/models.py index 816ff2a6..ce654b84 100644 --- a/api/tacticalrmm/core/models.py +++ b/api/tacticalrmm/core/models.py @@ -18,7 +18,10 @@ TZ_CHOICES = [(_, _) for _ in pytz.all_timezones] class CoreSettings(BaseAuditModel): email_alert_recipients = ArrayField( - models.EmailField(null=True, blank=True), null=True, blank=True, default=list, + models.EmailField(null=True, blank=True), + null=True, + blank=True, + default=list, ) sms_alert_recipients = ArrayField( models.CharField(max_length=255, null=True, blank=True), @@ -69,17 +72,6 @@ class CoreSettings(BaseAuditModel): if not self.pk and CoreSettings.objects.exists(): raise ValidationError("There can only be one CoreSettings instance") - # Only runs on first create - if not self.pk: - mesh_settings = self.get_initial_mesh_settings() - - if "mesh_token" in mesh_settings: - self.mesh_token = mesh_settings["mesh_token"] - if "mesh_username" in mesh_settings: - self.mesh_username = mesh_settings["mesh_username"] - if "mesh_site" in mesh_settings: - self.mesh_site = mesh_settings["mesh_site"] - return super(CoreSettings, self).save(*args, **kwargs) def __str__(self): @@ -165,39 +157,6 @@ class CoreSettings(BaseAuditModel): except Exception as e: logger.error(f"SMS failed to send: {e}") - def get_initial_mesh_settings(self): - - mesh_settings = {} - - # Check for Mesh Username - try: - if settings.MESH_USERNAME: - mesh_settings["mesh_username"] = settings.MESH_USERNAME - else: - raise AttributeError("MESH_USERNAME doesn't exist") - except AttributeError: - pass - - # Check for Mesh Site - try: - if settings.MESH_SITE: - mesh_settings["mesh_site"] = settings.MESH_SITE - else: - raise AttributeError("MESH_SITE doesn't exist") - except AttributeError: - pass - - # Check for Mesh Token - try: - if settings.MESH_TOKEN_KEY: - mesh_settings["mesh_token"] = settings.MESH_TOKEN_KEY - else: - raise AttributeError("MESH_SITE doesn't exist") - except AttributeError: - pass - - return mesh_settings - @staticmethod def serialize(core): # serializes the core and returns json diff --git a/docker/.env.example b/docker/.env.example index e516744e..7583ce72 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -1,24 +1,24 @@ -MESH_HOST=mesh.example.com -MESH_USER=mesh -MESH_PASS=meshpass -EMAIL_USER=admin@example.com +IMAGE_REPO= +VERSION=latest +# tactical credentials (Used to login to dashboard) +TRMM_USER=tactical +TRMM_PASS=tactical + +# dns settings +APP_HOST=app.simplermm.com +API_HOST=api.simplermm.com +MESH_HOST=mesh.simplermm.com + +# mesh settings +MESH_USER=meshcentral +MESH_PASS=meshcentralpass MONGODB_USER=mongouser MONGODB_PASSWORD=mongopass +# database settings POSTGRES_USER=postgres -POSTGRES_PASS=pass -POSTGRES_HOST=db +POSTGRES_PASS=postgrespass -APP_HOST=app.example.com -API_HOST=api.example.com - -REDIS_HOST=redis - -SALT_HOST=salt -SALT_USER=saltapi -SALT_PASS=password - -ADMIN_URL=admin -DJANGO_SEKRET=secret12341234123412341234 -DJANGO_DEBUG=False +# salt settings +SALT_PASS=saltpass \ No newline at end of file diff --git a/docker/containers/tactical-salt/dockerfile b/docker/containers/tactical-salt/dockerfile index 826e217d..202a8a92 100644 --- a/docker/containers/tactical-salt/dockerfile +++ b/docker/containers/tactical-salt/dockerfile @@ -1,5 +1,7 @@ FROM ubuntu:20.04 +ENV TACTICAL_DIR /opt/tactical + RUN apt-get update && \ apt-get install -y ca-certificates wget gnupg2 tzdata supervisor && \ wget -O - https://repo.saltstack.com/py3/ubuntu/20.04/amd64/latest/SALTSTACK-GPG-KEY.pub | apt-key add - && \ diff --git a/docker/containers/tactical-salt/entrypoint.sh b/docker/containers/tactical-salt/entrypoint.sh index bd5ac385..1afd07fd 100644 --- a/docker/containers/tactical-salt/entrypoint.sh +++ b/docker/containers/tactical-salt/entrypoint.sh @@ -5,11 +5,11 @@ set -e : "${SALT_USER:=saltapi}" : "${SALT_USER:=saltpass}" -# create salt user -useradd -M -s /bin/bash -u 1000 "${SALT_USER}" +# create sal user +groupadd -g 1000 "${SALT_USER}" +useradd -M -d "/opt/tactical" -s /bin/bash -u 1000 -g 1000 "${SALT_USER}" echo "${SALT_USER}:${SALT_PASS}" | chpasswd - cherrypy_config="$(cat << EOF module_dirs: ["/opt/tactical/_modules"] timeout: 20 diff --git a/docker/containers/tactical/dockerfile b/docker/containers/tactical/dockerfile index 5b2a0e55..2efa9340 100644 --- a/docker/containers/tactical/dockerfile +++ b/docker/containers/tactical/dockerfile @@ -4,50 +4,35 @@ ARG DEBIAN_FRONTEND=noninteractive ARG BUILD_DATE ENV TACTICAL_DIR /opt/tactical -ENV TACTICAL_TMP_DIR /tmp/tacticalrmm/ +ENV TACTICAL_GO_DIR /usr/local/rmmgo ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready ENV TACTICAL_USER tactical +ENV PATH ${TACTICAL_DIR}/api/env/bin:$PATH SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] -# install tactical reqs -COPY api/tacticalrmm/requirements.txt ${TACTICAL_TMP_DIR} +# copy files from repo +COPY api/tacticalrmm ${TACTICAL_DIR}/api +COPY scripts ${TACTICAL_DIR}/scripts +COPY _modules ${TACTICAL_DIR}/_modules +COPY api/tacticalrmm/core/goinstaller/bin/goversioninfo /usr/local/bin/goversioninfo +# install deps RUN apt-get update && \ apt-get upgrade -y && \ apt-get install -y --no-install-recommends wget ca-certificates gcc libc6-dev && \ rm -rf /var/lib/apt/lists/* && \ pip install --upgrade pip && \ - mkdir -p ${TACTICAL_DIR}/api && \ pip install --no-cache-dir virtualenv && python -m virtualenv ${TACTICAL_DIR}/api/env && \ ${TACTICAL_DIR}/api/env/bin/pip install --no-cache-dir setuptools wheel gunicorn && \ - ${TACTICAL_DIR}/api/env/bin/pip install --no-cache-dir -r ${TACTICAL_TMP_DIR}/requirements.txt && \ + ${TACTICAL_DIR}/api/env/bin/pip install --no-cache-dir -r ${TACTICAL_DIR}/api/requirements.txt && \ wget https://golang.org/dl/go1.15.linux-amd64.tar.gz -P /tmp && \ - tar -xzf /tmp/go1.15.linux-amd64.tar.gz -C /tmp - - -FROM python:3.8-slim - -ARG DEBIAN_FRONTEND=noninteractive -ARG BUILD_DATE - -ENV TACTICAL_DIR /opt/tactical -ENV TACTICAL_TMP_DIR /tmp/tacticalrmm -ENV TACTICAL_GO_DIR /usr/local/rmmgo -ENV TACTICAL_READY_FILE ${TACTICAL_DIR}/tmp/tactical.ready -ENV TACTICAL_USER tactical - -# create tactical user -RUN groupadd -g 1000 "${TACTICAL_USER}" && \ + mkdir -p ${TACTICAL_GO_DIR}/go && \ + tar -xzf /tmp/go1.15.linux-amd64.tar.gz -C ${TACTICAL_GO_DIR}/go && \ + rm -f /tmp/go1.15.linux-amd64.tar.gz && \ + groupadd -g 1000 "${TACTICAL_USER}" && \ useradd -M -d "${TACTICAL_DIR}" -s /bin/bash -u 1000 -g 1000 "${TACTICAL_USER}" -# copy files from repo and 1st build step -COPY api/tacticalrmm ${TACTICAL_DIR}/api -COPY scripts ${TACTICAL_DIR}/scripts -COPY _modules ${TACTICAL_DIR}/_modules -COPY api/tacticalrmm/core/goinstaller/bin/goversioninfo /usr/local/bin/goversioninfo -COPY --from=builder /tmp/go ${TACTICAL_GO_DIR}/go - # docker init COPY docker/containers/tactical/entrypoint.sh / RUN chmod +x /entrypoint.sh diff --git a/docker/containers/tactical/entrypoint.sh b/docker/containers/tactical/entrypoint.sh index 6b7666de..13e5fc5e 100644 --- a/docker/containers/tactical/entrypoint.sh +++ b/docker/containers/tactical/entrypoint.sh @@ -2,6 +2,8 @@ set -e +: "${TRMM_USER:=tactical}" +: "${TRMM_PASS:=tactical}" : "${POSTGRES_HOST:=tactical-postgres}" : "${POSTGRES_PORT:=5432}" : "${POSTGRES_USER:=tactical}" @@ -19,8 +21,6 @@ set -e : "${REDIS_HOST:=tactical-redis}" -source ${TACTICAL_DIR}/api/env/bin/activate - function check_tactical_ready { sleep 15 until [ -f "${TACTICAL_READY_FILE}" ]; do @@ -42,7 +42,14 @@ if [ "$1" = 'tactical-init' ]; then sleep 5 done + # check mesh setup and wait for mesh token + until [ -f "${TACTICAL_DIR}/tmp/mesh_token" ]; do + echo "waiting for mesh token to be generated..." + sleep 10 + done + # configure django settings + MESH_TOKEN=$(cat ${TACTICAL_DIR}/tmp/mesh_token) DJANGO_SEKRET=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 80 | head -n 1) SALT_PASS=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1) ADMINURL=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 70 | head -n 1) @@ -96,6 +103,7 @@ SALT_PASSWORD = '${SALT_PASS}' SALT_HOST = '${SALT_HOST}' MESH_USERNAME = '${MESH_USER}' MESH_SITE = 'https://${MESH_HOST}' +MESH_TOKEN_KEY = '${MESH_TOKEN_KEY}' REDIS_HOST = '${REDIS_HOST}' MESH_WS_URL = 'ws://${MESH_CONTAINER}:443' EOF @@ -103,12 +111,6 @@ EOF echo "${localvars}" > ${TACTICAL_DIR}/api/tacticalrmm/local_settings.py - # check mesh setup and wait for mesh token - until [ -f "${TACTICAL_DIR}/tmp/mesh_token" ]; do - echo "waiting for mesh token to be generated..." - sleep 10 - done - # run migrations and init scripts python manage.py migrate --no-input python manage.py collectstatic --no-input @@ -117,6 +119,9 @@ EOF python manage.py load_chocos python manage.py load_community_scripts + # create super user + echo "from django.contrib.auth.models import User; User.objects.create_superuser('${TRMM_USER}', 'admin@example.com', '${TRMM_PASS}')" | python manage.py shell + # chown everything to tactical user chown -R "${TACTICAL_USER}":"${TACTICAL_USER}" "${TACTICAL_DIR}" @@ -130,6 +135,7 @@ if [ "$1" = 'tactical-backend' ]; then check_tactical_ready # Prepare log files and start outputting logs to stdout + mkdir -p ${TACTICAL_DIR}/api/tacticalrmm/logs touch ${TACTICAL_DIR}/api/tacticalrmm/logs/gunicorn.log touch ${TACTICAL_DIR}/api/tacticalrmm/logs/gunicorn-access.log tail -n 0 -f ${TACTICAL_DIR}/api/tacticalrmm/logs/gunicorn*.log & @@ -147,14 +153,17 @@ if [ "$1" = 'tactical-backend' ]; then fi if [ "$1" = 'tactical-celery' ]; then + check_tactical_ready test -f "${TACTICAL_DIR}/api/celerybeat.pid" && rm "${TACTICAL_DIR}/api/celerybeat.pid" celery -A tacticalrmm worker fi if [ "$1" = 'tactical-beat' ]; then + check_tactical_ready celery -A tacticalrmm beat fi if [ "$1" = 'tactical-celerywinupdate' ]; then + check_tactical_ready celery -A tacticalrmm worker -Q wupdate fi \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1c57b94c..c6242684 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -15,7 +15,6 @@ networks: # docker managed persistent volumes volumes: tactical_data: - tactical_certs: salt_data: postgres_data: mongo_data: @@ -51,6 +50,8 @@ services: APP_HOST: ${APP_HOST} API_HOST: ${API_HOST} MESH_HOST: ${MESH_HOST} + TRMM_USER: ${TRMM_USER} + TRMM_PASS: ${TRMM_PASS} depends_on: - tactical-postgres - tactical-meshcentral @@ -112,7 +113,7 @@ services: networks: - proxy environment: - - API_HOST=${API_HOST} + API_HOST: ${API_HOST} # container for django backend tactical-backend: @@ -132,9 +133,9 @@ services: image: ${IMAGE_REPO}tactical-nginx:${VERSION} restart: always environment: - - APP_HOST=${APP_HOST} - - API_HOST=${API_HOST} - - MESH_HOST=${MESH_HOST} + APP_HOST: ${APP_HOST} + API_HOST: ${API_HOST} + MESH_HOST: ${MESH_HOST} networks: proxy: ipv4_address: 172.20.0.20 diff --git a/docker/image-build.sh b/docker/image-build.sh index e5fad0cb..a4cd333b 100755 --- a/docker/image-build.sh +++ b/docker/image-build.sh @@ -5,13 +5,12 @@ set -o errexit set -o pipefail -# tactical tactical-nginx tactical-meshcentral tactical-salt - -DOCKER_IMAGES="tactical-frontend" +# tactical-frontend tactical-nginx tactical-salt --no-cache +DOCKER_IMAGES="tactical" cd .. for DOCKER_IMAGE in ${DOCKER_IMAGES}; do echo "Building Tactical Image: ${DOCKER_IMAGE}..." - docker build --pull --no-cache --build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" -t "${DOCKER_IMAGE}" -f "docker/containers/${DOCKER_IMAGE}/dockerfile" . + docker build --pull --build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" -t "${DOCKER_IMAGE}" -f "docker/containers/${DOCKER_IMAGE}/dockerfile" . done \ No newline at end of file