diff --git a/api/tacticalrmm/agents/models.py b/api/tacticalrmm/agents/models.py index 80932143..83f56f0a 100644 --- a/api/tacticalrmm/agents/models.py +++ b/api/tacticalrmm/agents/models.py @@ -101,7 +101,7 @@ class Agent(models.Model): if "kwargs" in kwargs: json.update({"kwarg": kwargs["kwargs"]}) resp = requests.post( - "http://127.0.0.1:8123/run", json=[json], timeout=kwargs["timeout"] + "http://" + settings.SALT_HOST + ":8123/run", json=[json], timeout=kwargs["timeout"] ) return resp @@ -121,7 +121,7 @@ class Agent(models.Model): json.update({"arg": kwargs["arg"]}) if "kwargs" in kwargs: json.update({"kwarg": kwargs["kwargs"]}) - resp = requests.post("http://127.0.0.1:8123/run", json=[json]) + resp = requests.post("http://" + settings.SALT_HOST + ":8123/run", json=[json]) return resp @staticmethod @@ -129,7 +129,7 @@ class Agent(models.Model): session = requests.Session() session.post( - "http://127.0.0.1:8123/login", + "http://" + settings.SALT_HOST + ":8123/login", json={ "username": settings.SALT_USERNAME, "password": settings.SALT_PASSWORD, @@ -137,7 +137,7 @@ class Agent(models.Model): }, ) - return session.get(f"http://127.0.0.1:8123/jobs/{jid}") + return session.get(f"http://" + settings.SALT_HOST + ":8123/jobs/{jid}") @staticmethod def get_github_versions(): diff --git a/api/tacticalrmm/tacticalrmm/celery.py b/api/tacticalrmm/tacticalrmm/celery.py index 70d79109..fa6cff44 100644 --- a/api/tacticalrmm/tacticalrmm/celery.py +++ b/api/tacticalrmm/tacticalrmm/celery.py @@ -2,13 +2,14 @@ from __future__ import absolute_import, unicode_literals import os from celery import Celery from celery.schedules import crontab +from django.conf import settings os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tacticalrmm.settings") -app = Celery("tacticalrmm", backend="redis://localhost", broker="redis://localhost") +app = Celery("tacticalrmm", backend="redis://" + settings.REDIS_HOST, broker="redis://" + settings.REDIS_HOST) # app.config_from_object('django.conf:settings', namespace='CELERY') -app.broker_url = "redis://localhost:6379" -app.result_backend = "redis://localhost:6379" +app.broker_url = "redis://" + settings.REDIS_HOST + ":6379" +app.result_backend = "redis://" + settings.REDIS_HOST + ":6379" app.accept_content = ["application/json"] app.result_serializer = "json" app.task_serializer = "json" diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 00000000..c0aa9d2a --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,21 @@ +MESH_HOST=mesh.example.com +MESH_USER=mesh +MESH_PASS=meshpass + +POSTGRES_USER=postgres +POSTGRES_PASS=pass +POSTGRES_HOST=db + +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 + +TWO_FACTOR_OTP=3HTDZVFRYP4OPXHL diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 00000000..2eea525d --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/docker/api/api.conf b/docker/api/api.conf new file mode 100644 index 00000000..807f9225 --- /dev/null +++ b/docker/api/api.conf @@ -0,0 +1,59 @@ +user nginx; +worker_processes 1; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + sendfile on; + keepalive_timeout 65; + + server_tokens off; + + upstream tacticalrmm { + server unix:///app/tacticalrmm.sock; + } + + server { + listen 80; + #server_name ${API_HOST}; + client_max_body_size 300M; + access_log /var/log/nginx/api-access.log; + error_log /var/log/nginx/api-error.log; + + location /static/ { + root /app; + } + + location /protected/ { + internal; + add_header "Access-Control-Allow-Origin" "https://${APP_HOST}"; + alias /app/tacticalrmm/downloads/; + } + + location /protectedlogs/ { + internal; + add_header "Access-Control-Allow-Origin" "https://${APP_HOST}"; + alias /app/log/; + } + + + location / { + uwsgi_pass tacticalrmm; + include /etc/nginx/uwsgi_params; + uwsgi_read_timeout 9999s; + uwsgi_ignore_client_abort on; + } + } + +} +daemon off; \ No newline at end of file diff --git a/docker/api/dockerfile b/docker/api/dockerfile new file mode 100644 index 00000000..34c5fe6f --- /dev/null +++ b/docker/api/dockerfile @@ -0,0 +1,29 @@ +FROM tiangolo/uwsgi-nginx:python3.7 +WORKDIR /app +ARG DJANGO_SEKRET +ARG POSTGRES_USER +ARG POSTGRES_PASS +ARG POSTGRES_HOST +ARG SALT_HOST +ARG SALT_USER +ARG SALT_PASS +ARG REDIS_HOST +ARG MESH_USER +ARG MESH_HOST +ARG APP_HOST +ARG API_HOST +ARG ADMIN_URL +ARG TWO_FACTOR_OTP + +EXPOSE 80 + +RUN apt-get update && apt-get install -y gettext-base +COPY ./api/tacticalrmm . +RUN pip install --no-cache-dir -r requirements.txt +COPY ./docker/api/prestart.sh . +COPY ./docker/api/uwsgi.ini . +COPY ./docker/api/api.conf /app/api.conf.tmp +RUN envsubst '\$APP_HOST, \$API_HOST' < /app/api.conf.tmp > /app/nginx.conf && \ + rm /app/api.conf.tmp +COPY ./docker/api/local_settings.py ./tacticalrmm/local_settings.py.tmp +RUN envsubst < /app/tacticalrmm/local_settings.py.tmp > /app/tacticalrmm/local_settings.py && rm /app/tacticalrmm/local_settings.py.tmp \ No newline at end of file diff --git a/docker/api/prestart.sh b/docker/api/prestart.sh new file mode 100755 index 00000000..455c8a85 --- /dev/null +++ b/docker/api/prestart.sh @@ -0,0 +1,5 @@ +#! /usr/bin/env bash + +sleep 5 +python manage.py migrate --no-input +python manage.py collectstatic --no-input diff --git a/docker/api/uwsgi.ini b/docker/api/uwsgi.ini new file mode 100644 index 00000000..3eccb990 --- /dev/null +++ b/docker/api/uwsgi.ini @@ -0,0 +1,12 @@ +[uwsgi] + +logto = /app/log/uwsgi.log +chdir = /app +wsgi-file = tacticalrmm/wsgi.py +master = true +processes = 4 +threads = 2 +socket = /app/tacticalrmm.sock +# clear environment on exit +vacuum = true +die-on-term = true diff --git a/docker/app/app.conf b/docker/app/app.conf new file mode 100644 index 00000000..6349b7e4 --- /dev/null +++ b/docker/app/app.conf @@ -0,0 +1,16 @@ + +server { + listen 80; + #server_name ${APP_HOST}; + charset utf-8; + + location / { + root /usr/share/nginx/html; + try_files $uri $uri/ /index.html; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + add_header Pragma "no-cache"; + } + + error_log /var/log/nginx/app-error.log; + access_log /var/log/nginx/app-access.log; +} diff --git a/docker/app/dockerfile b/docker/app/dockerfile new file mode 100644 index 00000000..a867cc4d --- /dev/null +++ b/docker/app/dockerfile @@ -0,0 +1,17 @@ +FROM node:12-alpine AS builder +ARG APP_HOST +ARG API_HOST +EXPOSE 80 +WORKDIR /home/node +RUN apk add gettext +COPY ./web . +COPY ./docker/app/.env.local /home/.env.local.tmp +RUN envsubst '\$APP_HOST, \$API_HOST' < /home/.env.local.tmp > /home/node/.env.local && rm /home/.env.local.tmp +RUN npm install && npm run build +COPY ./docker/app/app.conf /home/node/app.conf.tmp +RUN envsubst '\$APP_HOST' < /home/node/app.conf.tmp > /home/node/app.conf + +FROM nginx:alpine +WORKDIR /usr/share/nginx/html +COPY --from=builder /home/node/dist . +COPY --from=builder /home/node/app.conf /etc/nginx/conf.d/default.conf diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 00000000..32c72037 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,170 @@ +version: '3.5' + +networks: + database: + redis: + proxy: + driver: bridge + ipam: + driver: default + config: + - subnet: 172.20.0.0/24 + +services: + db: + image: "postgres" + restart: "always" + environment: + - POSTGRES_DB=tacticalrmm + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASS=${POSTGRES_PASS} + networks: + - database + salt: + image: "saltstack/salt" + volumes: + - ./salt:/etc/salt/master.d + ports: + - "8123:8123" + - "4505:4505" + - "4506:4506" + meshcentral: + build: + context: ./meshcentral + args: + - MESH_HOST=${MESH_HOST} + networks: + - proxy + nginx-proxy: + build: + context: ./nginx-proxy + args: + - APP_HOST=${APP_HOST} + - API_HOST=${API_HOST} + - MESH_HOST=${MESH_HOST} + ports: + - "80:80" + - "443:443" + networks: + proxy: + ipv4_address: 172.20.0.20 + depends_on: + - app + - api + - meshcentral + redis: + image: redis + networks: + - redis + app: + build: + context: .. + dockerfile: "./docker/app/dockerfile" + args: + - APP_HOST=${APP_HOST} + - API_HOST=${API_HOST} + networks: + - proxy + api: + build: + context: .. + dockerfile: "./docker/api/dockerfile" + args: + - DJANGO_SEKRET=${DJANGO_SEKRET} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASS=${POSTGRES_PASS} + - POSTGRES_HOST=${POSTGRES_HOST} + - SALT_PASS=${SALT_PASS} + - SALT_USER=${SALT_USER} + - SALT_HOST=${SALT_HOST} + - REDIS_HOST=${REDIS_HOST} + - MESH_USER=${MESH_USER} + - MESH_HOST=${MESH_HOST} + - APP_HOST=${APP_HOST} + - API_HOST=${API_HOST} + - ADMIN_URL=${ADMIN_URL} + - TWO_FACTOR_OTP=${TWO_FACTOR_OTP} + networks: + - proxy + - database + - redis + depends_on: + - db + celery-service: + build: + context: .. + dockerfile: "./docker/api/dockerfile" + args: + - DJANGO_SEKRET=${DJANGO_SEKRET} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASS=${POSTGRES_PASS} + - POSTGRES_HOST=${POSTGRES_HOST} + - SALT_PASS=${SALT_PASS} + - SALT_USER=${SALT_USER} + - SALT_HOST=${SALT_HOST} + - REDIS_HOST=${REDIS_HOST} + - MESH_USER=${MESH_USER} + - MESH_HOST=${MESH_HOST} + - APP_HOST=${APP_HOST} + - API_HOST=${API_HOST} + - ADMIN_URL=${ADMIN_URL} + - TWO_FACTOR_OTP=${TWO_FACTOR_OTP} + command: celery -A tacticalrmm worker -l info + networks: + - redis + - database + depends_on: + - db + - redis + celery-beat: + build: + context: .. + dockerfile: "./docker/api/dockerfile" + args: + - DJANGO_SEKRET=${DJANGO_SEKRET} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASS=${POSTGRES_PASS} + - POSTGRES_HOST=${POSTGRES_HOST} + - SALT_PASS=${SALT_PASS} + - SALT_USER=${SALT_USER} + - SALT_HOST=${SALT_HOST} + - REDIS_HOST=${REDIS_HOST} + - MESH_USER=${MESH_USER} + - MESH_HOST=${MESH_HOST} + - APP_HOST=${APP_HOST} + - API_HOST=${API_HOST} + - ADMIN_URL=${ADMIN_URL} + - TWO_FACTOR_OTP=${TWO_FACTOR_OTP} + command: celery -A tacticalrmm beat -l info + networks: + - redis + - database + depends_on: + - db + - redis + celery-winupdate: + build: + context: .. + dockerfile: "./docker/api/dockerfile" + args: + - DJANGO_SEKRET=${DJANGO_SEKRET} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASS=${POSTGRES_PASS} + - POSTGRES_HOST=${POSTGRES_HOST} + - SALT_PASS=${SALT_PASS} + - SALT_USER=${SALT_USER} + - SALT_HOST=${SALT_HOST} + - REDIS_HOST=${REDIS_HOST} + - MESH_USER=${MESH_USER} + - MESH_HOST=${MESH_HOST} + - APP_HOST=${APP_HOST} + - API_HOST=${API_HOST} + - ADMIN_URL=${ADMIN_URL} + - TWO_FACTOR_OTP=${TWO_FACTOR_OTP} + command: celery -A tacticalrmm worker -Q wupdate + networks: + - redis + - database + depends_on: + - db + - redis diff --git a/docker/meshcentral/config.json b/docker/meshcentral/config.json new file mode 100644 index 00000000..1d0e7d75 --- /dev/null +++ b/docker/meshcentral/config.json @@ -0,0 +1,31 @@ +{ + "settings": { + "Cert": "${MESH_HOST}", + "TLSOffload": "172.20.0.20", + "RedirPort": 80, + "WANonly": true, + "Minify": 1, + "Port": 443, + "AllowLoginToken": true, + "AllowFraming": true, + "_AgentPing": 60, + "AgentPong": 300, + "AllowHighQualityDesktop": true, + "MaxInvalidLogin": { "time": 5, "count": 5, "coolofftime": 30 } + }, + "domains": { + "": { + "Title": "Dev RMM", + "Title2": "DevRMM", + "NewAccounts": false, + "Footer": "Twitter", + "GeoLocation": true, + "CertUrl": "https://172.20.0.20:443", + "httpheaders": { + "Strict-Transport-Security": "max-age=360000", + "_x-frame-options": "sameorigin", + "Content-Security-Policy": "default-src 'none'; script-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; frame-src 'self'; media-src 'self'" + } + } + } + } \ No newline at end of file diff --git a/docker/meshcentral/dockerfile b/docker/meshcentral/dockerfile new file mode 100644 index 00000000..c37d33e6 --- /dev/null +++ b/docker/meshcentral/dockerfile @@ -0,0 +1,11 @@ +FROM node:stretch + +WORKDIR /home/node/app +ARG MESH_HOST +RUN apt-get update && apt-get install -y gettext-base +RUN npm install meshcentral +COPY config.json ./meshcentral-data/config.json.tmp +RUN envsubst '\$MESH_HOST' < /home/node/app/meshcentral-data/config.json.tmp > /home/node/app/meshcentral-data/config.json && \ + rm /home/node/app/meshcentral-data/config.json.tmp + +CMD ["node", "./node_modules/meshcentral/meshcentral"] \ No newline at end of file diff --git a/docker/nginx-proxy/api.conf b/docker/nginx-proxy/api.conf new file mode 100644 index 00000000..c0a6629b --- /dev/null +++ b/docker/nginx-proxy/api.conf @@ -0,0 +1,34 @@ +server { + server_name ${API_HOST}; + + location / { + proxy_pass http://api; + proxy_http_version 1.1; + proxy_cache_bypass $http_upgrade; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + } + + error_log /var/log/nginx/api-error.log; + access_log /var/log/nginx/api-access.log; + + listen 443 ssl; + ssl_certificate /cert/fullchain.pem; + ssl_certificate_key /cert/privkey.pem; + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; + +} + +server { + + listen 80; + server_name ${API_HOST}; + return 301 https://$server_name$request_uri; +} diff --git a/docker/nginx-proxy/app.conf b/docker/nginx-proxy/app.conf new file mode 100644 index 00000000..c871fbfb --- /dev/null +++ b/docker/nginx-proxy/app.conf @@ -0,0 +1,35 @@ +server { + server_name ${APP_HOST}; + + location / { + proxy_pass http://app; + proxy_http_version 1.1; + proxy_cache_bypass $http_upgrade; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + } + + error_log /var/log/nginx/app-error.log; + access_log /var/log/nginx/app-access.log; + + listen 443 ssl; + ssl_certificate /cert/fullchain.pem; + ssl_certificate_key /cert/privkey.pem; + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; + +} + +server { + + listen 80; + server_name ${APP_HOST}; + return 301 https://$server_name$request_uri; +} + diff --git a/docker/nginx-proxy/cert/.gitignore b/docker/nginx-proxy/cert/.gitignore new file mode 100644 index 00000000..612424a3 --- /dev/null +++ b/docker/nginx-proxy/cert/.gitignore @@ -0,0 +1 @@ +*.pem \ No newline at end of file diff --git a/docker/nginx-proxy/dockerfile b/docker/nginx-proxy/dockerfile new file mode 100644 index 00000000..ad6b8c13 --- /dev/null +++ b/docker/nginx-proxy/dockerfile @@ -0,0 +1,27 @@ +FROM nginx +WORKDIR /etc/nginx/conf.d + +ARG APP_HOST +ARG API_HOST +ARG MESH_HOST + +EXPOSE 80 +EXPOSE 443 + +#Remove default NGINX config +RUN rm /etc/nginx/conf.d/default.conf + +#Copy APP config +COPY app.conf ./app.conf.tmp +RUN envsubst '\$APP_HOST' < /etc/nginx/conf.d/app.conf.tmp > /etc/nginx/conf.d/app.conf && rm /etc/nginx/conf.d/app.conf.tmp + +#Copy API config +COPY api.conf ./api.conf.tmp +RUN envsubst '\$API_HOST' < /etc/nginx/conf.d/api.conf.tmp > /etc/nginx/conf.d/api.conf && rm /etc/nginx/conf.d/api.conf.tmp + +#Copy Mesh config +COPY mesh.conf ./mesh.conf.tmp +RUN envsubst '\$MESH_HOST' < /etc/nginx/conf.d/mesh.conf.tmp > /etc/nginx/conf.d/mesh.conf && rm /etc/nginx/conf.d/mesh.conf.tmp + +#Copy Certs +COPY ./cert/*.pem /cert/ \ No newline at end of file diff --git a/docker/nginx-proxy/mesh.conf b/docker/nginx-proxy/mesh.conf new file mode 100644 index 00000000..67dc0ee5 --- /dev/null +++ b/docker/nginx-proxy/mesh.conf @@ -0,0 +1,39 @@ +server { + + listen 80; + server_name ${MESH_HOST}; + + location / { + proxy_pass http://meshcentral; + proxy_http_version 1.1; + + proxy_set_header X-Forwarded-Host $host:$server_port; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + +server { + + listen 443 ssl; + proxy_send_timeout 330s; + proxy_read_timeout 330s; + server_name ${MESH_HOST}; + ssl_certificate /cert/fullchain.pem; + ssl_certificate_key /cert/privkey.pem; + ssl_session_cache shared:WEBSSL:10m; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_pass http://meshcentral:443; + proxy_http_version 1.1; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_set_header X-Forwarded-Host $host:$server_port; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} \ No newline at end of file diff --git a/docker/readme.md b/docker/readme.md new file mode 100644 index 00000000..f1a5f4d5 --- /dev/null +++ b/docker/readme.md @@ -0,0 +1,52 @@ +# Docker Setup + +- install docker and docker-compose +- Obtain wildcard cert or individual certs for each subdomain + +## Optional - Generate certificates with certbot + +Install Certbot + +```sudo add-apt-repository ppa:certbot/certbot +sudo apt-get install certbot +``` + +Generate the wildcard certificate. Add the DNS entry for domain validation. + +```sudo certbot certonly --manual -d *.example.com --agree-tos --no-bootstrap --manual-public-ip-logging-ok --preferred-challenges dns +``` +Copy the fullchain.pem and privkey.pem to the cert directory. + +## Run the environment with Docker + +Change values in .env to match your environment + +```cd docker +sudo docker-compose up -d +``` + +You may need to run this twice since some of the dependant containers won't be ready + +## Create a super user + +```sudo docker exec -it docker_api_1 python manage.py createsuperuser +``` + +## Setup 2FA authentication + +Get the 2FA code with + +```sudo docker exec -it docker_api_1 python manage.py generate_totp +``` + +Add the generated code to the .env file TWO_FACTOR_OTP in the docker folder + +Rebuild the api container + +```sudo docker-compose up -d --build api +``` + +Use the generated code and the username to generate a bar code for your authenticator app + +```sudo docker exec -it docker_api_1 python manage.py generate_barcode [OTP_CODE] [username] +``` diff --git a/docker/salt/api.conf b/docker/salt/api.conf new file mode 100644 index 00000000..9342a309 --- /dev/null +++ b/docker/salt/api.conf @@ -0,0 +1,14 @@ +timeout: 60 +gather_job_timeout: 30 +max_event_size: 30485760 +external_auth: + pam: + saltapi: + - .* + - '@runner' + - '@wheel' + - '@jobs' +rest_cherrypy: + port: 8123 + disable_ssl: True + max_request_body_size: 30485760 \ No newline at end of file diff --git a/docker/salt/user.conf b/docker/salt/user.conf new file mode 100644 index 00000000..dac049e7 --- /dev/null +++ b/docker/salt/user.conf @@ -0,0 +1 @@ +{"user": "salt"} \ No newline at end of file