Unsupported Reference Scripts¶
Note
These are not supported scripts/configurations by Tactical RMM, but it's provided here for your reference.
Although these aren't officially supported configurations, we generally will help point you in the right direction. Please use the Discord #unsupported channel to discuss issues replated to these complex installations
General Notes on Proxies and Tactical RMM¶
Port 443¶
Make sure websockets option is enabled.
All 3 URL's will need to be configured: rmm
, api
, mesh
For mesh
see the Section 10. TLS Offloading of the MeshCentral 2 User Guide
Port 4222¶
Is NATS (https://nats.io). You'll need a TCP forwarder as NATS only talks TCP not HTTP.
Traefikv2¶
Offsite Resource: https://gitlab.com/NiceGuyIT/tactical-goodies/-/tree/main/traefik
This section will assume that by default Traefik will reverse proxy everything on port 443.
Here is a basic Traefik config with docker-composer note the file.directory and file.watch are important.
version: "3.7"
services:
traefik:
container_name: traefik24
image: traefik:v2.4
restart: unless-stopped
command:
- --entryPoints.http.address=:80
- --entryPoints.https.address=:443
- --providers.docker=true
- --providers.docker.endpoint=unix:///var/run/docker.sock
- --providers.docker.defaultrule=HostHeader(`{{ index .Labels "com.docker.compose.service" }}.$DOMAINNAME`)
## This is important, to load the config for RMM and Mesh
- --providers.file.directory=rules # Load dynamic configuration from one or more .toml or .yml files in a directory.
- --providers.file.watch=true # Only works on top level files in the rules folder
####
- --certificatesresolvers.dns-cloudflare.acme.dnschallenge=true
- --certificatesResolvers.dns-cloudflare.acme.email=$CLOUDFLARE_EMAIL
- --certificatesResolvers.dns-cloudflare.acme.storage=/acme.json
- --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.provider=cloudflare
- --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.resolvers=1.1.1.1:53,1.0.0.1:53
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
volumes:
##The rules that we will load##
- $USERDIR/docker/traefik2/rules:/rules
##
- /var/run/docker.sock:/var/run/docker.sock:ro
- $USERDIR/docker/traefik2/acme/acme.json:/acme.json
- $USERDIR/docker/traefik2/traefik.log:/traefik.log
environment:
- CF_API_EMAIL=$CLOUDFLARE_EMAIL
- CF_API_KEY=$CLOUDFLARE_API_KEY
labels:
- "traefik.enable=true"
# HTTP-to-HTTPS Redirect
- "traefik.http.routers.http-catchall.entrypoints=http"
- "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
# HTTP Routers
- "traefik.http.routers.traefik-rtr.entrypoints=https"
- "traefik.http.routers.traefik-rtr.rule=HostHeader(`traefik.$DOMAINNAME`)"
- "traefik.http.routers.traefik-rtr.tls=true"
- "traefik.http.routers.traefik-rtr.tls.domains[0].main=$DOMAINNAME"
- "traefik.http.routers.traefik-rtr.tls.domains[0].sans=*.$DOMAINNAME"
Before proceding, we need to change the port 443 to 4430 and 80 to 800 because the port 443 and 80 are alredy used by Traefik.
Here is a snippet of the only thing you should modify into docker-compose file of the installation.
# container for tactical reverse proxy
tactical-nginx:
container_name: trmm-nginx
image: ${IMAGE_REPO}tactical-nginx:${VERSION}
restart: always
environment:
APP_HOST: ${APP_HOST}
API_HOST: ${API_HOST}
MESH_HOST: ${MESH_HOST}
CERT_PUB_KEY: ${CERT_PUB_KEY}
CERT_PRIV_KEY: ${CERT_PRIV_KEY}
networks:
proxy:
ipv4_address: 172.20.0.20
ports:
- "800:80" ## port 800 instead of 80
- "4430:443" ## port 4430 instead of 443
Once save, make sure you run the docker-compose or installation script at least once, so all the directory structure are created. Once you have your certificate (acme.json) generated by traefikv2 we will be able to extract it for rmm.
Copy the acme.json create by traefik into the root of your rmm directory (In my case its $USERDIR/docker/rmm) which you should have already define. After that we can run this docker to extract the certificates for us.
version: "3.7"
services:
##Copy the acme.json of Traefik2 at volumes: (userdir/docker/rmm in this case)
traefik-certs-dumper:
image: ldez/traefik-certs-dumper:v2.7.4
entrypoint: sh -c '
apk add jq
; while ! [ -e /data/acme.json ]
|| ! [ `jq ".[] | .Certificates | length" /data/acme.json` != 0 ]; do
sleep 1
; done
&& traefik-certs-dumper file --version v2 --watch
--source /data/acme.json --dest data/certs'
volumes:
- $USERDIR/docker/rmm:/data
Once completed, you should have 1 new folder into you rmm directory $USERDIR/docker/rmm/certs in this example. As the installation instruction, we will pass those to the .env
echo "CERT_PUB_KEY=$(sudo base64 -w 0 $USERDIR/docker/rmm/certs/certs/**yourdomaine.com.crt**)" >> .env
echo "CERT_PRIV_KEY=$(sudo base64 -w 0 $USERDIR/docker/rmm/certs/private/**yourdomaine.com.key**)" >> .env
Next we can create 3 rules to tell traefik to correctly route the https and agent For that we will create 2 rules into traefik directory as per it configuration. folder/traefik/rules
create
nano app-mesh.toml
and inside it we add
[http.routers]
[http.routers.mesh-rtr]
entryPoints = ["https"]
rule = "Host(`mesh.**yourdomain.com**`)"
service = "mesh-svc"
##middleware with 2fa
[http.services]
[http.services.mesh-svc]
[http.services.mesh-svc.loadBalancer]
passHostHeader = true
[[http.services.mesh-svc.loadBalancer.servers]]
url = "https://**xxx.xxx.xxx.xxx**:4430" # or whatever your external host's IP is
create
nano app-meshagent.toml
and inside it we add
[http.routers]
[http.routers.mesh-rtr1]
entryPoints = ["https"]
rule = """Host(`mesh.**yourdomain.com**`) &&
PathPrefix( `/agent.ashx`, `/meshrelay.ashx`, ) &&
Headers(`X-Forwarded-Proto`, `wss`) """
##Don't add middle where, the agent wont work.
[http.services]
[http.services.mesh-svc1]
[http.services.mesh-svc.loadBalancer]
passHostHeader = true
[[http.services.mesh-svc1.loadBalancer.servers]]
url = "https://**xxx.xxx.xxx.xxx**:4430" # or whatever your external host's IP is
create
nano app-rmm.toml
and inside it we add
[http.routers]
[http.routers.rmm-rtr]
entryPoints = ["https"]
rule = "Host(`rmm.**yourdomain.com**`)"
service = "rmm-svc"
##middleware with 2fa
[http.services]
[http.services.rmm-svc]
[http.services.rmm-svc.loadBalancer]
passHostHeader = true
[[http.services.rmm-svc.loadBalancer.servers]]
url = "https://xxx.xxx.xxx.xxx:4430" # or whatever your external host's IP:port is
That it, you can now restart Tactical rmm and mesh.yourdomain.com should work, same for the agent. Please note that if you have a middleware with 2FA you can still use it with the inside mesh.toml but do not add it with the agent.
HAProxy¶
Check/Change the mesh central config.json, some of the values may be set already, CertUrl must be changed to point to the HAProxy server.
Meshcentral Adjustment¶
Credit to @bradhawkins
Edit Meshcentral config
nano /meshcentral/meshcentral-data/config.json
Insert this (modify HAProxyIP
to your network)
{
"settings": {
"Port": 4430,
"AliasPort": 443,
"RedirPort": 800,
"TlsOffload": "127.0.0.1",
},
"domains": {
"": {
"CertUrl": "https://HAProxyIP:443/",
}
}
}
Restart meshcentral
service meshcentral restart
HAProxy Config¶
The order of use_backend is important Tactical-Mesh-WebSocket_ipvANY
must be before Tactical-Mesh_ipvANY
The values of timeout connect
, timeout server
, timeout tunnel
in Tactical-Mesh-WebSocket
have been configured to maintain a stable agent connection, however you may need to adjust these values to suit your environment.
frontend HTTPS-merged
bind 0.0.0.0:443 name 0.0.0.0:443 ssl crt-list /var/etc/haproxy/HTTPS.crt_list #ADJUST THIS TO YOUR OWN SSL CERTIFICATES
mode http
log global
option socket-stats
option dontlognull
option http-server-close
option forwardfor
acl https ssl_fc
http-request set-header X-Forwarded-Proto http if !https
http-request set-header X-Forwarded-Proto https if https
timeout client 30000
acl RMM var(txn.txnhost) -m sub -i rmm.example.com
acl aclcrt_RMM var(txn.txnhost) -m reg -i ^([^\.]*)\.example\.com(:([0-9]){1,5})?$
acl API var(txn.txnhost) -m sub -i api.example.com
acl aclcrt_API var(txn.txnhost) -m reg -i ^([^\.]*)\.example\.com(:([0-9]){1,5})?$
acl is_websocket hdr(Upgrade) -i WebSocket
acl is_mesh var(txn.txnhost) -m beg -i mesh.example.com
acl aclcrt_MESH-WebSocket var(txn.txnhost) -m reg -i ^([^\.]*)\.example\.com(:([0-9]){1,5})?$
acl MESH var(txn.txnhost) -m sub -i mesh.example.com
acl aclcrt_MESH var(txn.txnhost) -m reg -i ^([^\.]*)\.example\.com(:([0-9]){1,5})?$
#PUT OTHER USE_BACKEND IN HERE
use_backend Tactical_ipvANY if RMM aclcrt_RMM
use_backend Tactical_ipvANY if API aclcrt_API
use_backend Tactical-Mesh-WebSocket_ipvANY if is_websocket is_mesh aclcrt_MESH-WebSocket
use_backend Tactical-Mesh_ipvANY if MESH aclcrt_MESH
frontend http-to-https
bind 0.0.0.0:80
mode http
log global
option http-keep-alive
timeout client 30000
http-request redirect scheme https
backend Tactical_ipvANY
mode http
id 100
log global
timeout connect 30000
timeout server 30000
retries 3
option httpchk GET /
server tactical 192.168.10.123:443 id 101 ssl check inter 1000 verify none
backend Tactical-Mesh-WebSocket_ipvANY
mode http
id 113
log global
timeout connect 3000
timeout server 3000
retries 3
timeout tunnel 3600000
http-request add-header X-Forwarded-Host %[req.hdr(Host)]
http-request add-header X-Forwarded-Proto https
server tactical 192.168.10.123:443 id 101 ssl verify none
backend Tactical-Mesh_ipvANY
mode http
id 112
log global
timeout connect 15000
timeout server 15000
retries 3
option httpchk GET /
timeout tunnel 15000
http-request add-header X-Forwarded-Host %[req.hdr(Host)]
http-request add-header X-Forwarded-Proto https
server tactical 192.168.10.123:443 id 101 ssl check inter 1000 verify none
fail2ban¶
Install fail2ban¶
sudo apt install -y fail2ban
Set Tactical fail2ban filter conf File¶
tacticalfail2banfilter="$(cat << EOF
[Definition]
failregex = ^<HOST>.*400.17.*$
ignoreregex = ^<HOST>.*200.*$
EOF
)"
sudo echo "${tacticalfail2banfilter}" > /etc/fail2ban/filter.d/tacticalrmm.conf
Set Tactical fail2ban jail conf File¶
tacticalfail2banjail="$(cat << EOF
[tacticalrmm]
enabled = true
port = 80,443
filter = tacticalrmm
action = iptables-allports[name=tactical]
logpath = /rmm/api/tacticalrmm/tacticalrmm/private/log/access.log
maxretry = 3
bantime = 14400
findtime = 14400
EOF
)"
sudo echo "${tacticalfail2banjail}" > /etc/fail2ban/jail.d/tacticalrmm.local
Restart fail2ban¶
sudo systemctl restart fail2ban
Using purchased SSL certs instead of LetsEncrypt wildcards¶
Credit to @dinger1986
How to change certs used by Tactical RMM to purchased ones (this can be a wildcard cert).
You need to add the certificate private key and public keys to the following files:
/etc/nginx/sites-available/rmm.conf
/etc/nginx/sites-available/meshcentral.conf
/etc/nginx/sites-available/frontend.conf
/rmm/api/tacticalrmm/tacticalrmm/local_settings.py
-
create a new folder for certs and allow tactical user permissions (assumed to be tactical)
sudo mkdir /certs sudo chown -R tactical:tactical /certs"
-
Now move your certs into that folder.
-
Open the api file and add the api certificate or if its a wildcard the directory should be
/certs/EXAMPLE.COM/
sudo nano /etc/nginx/sites-available/rmm.conf
replace
ssl_certificate /etc/letsencrypt/live/EXAMPLE.COM/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/EXAMPLE.COM/privkey.pem;
with
ssl_certificate /certs/api.EXAMPLE.COM/fullchain.pem; ssl_certificate_key /certs/api.EXAMPLE.COM/privkey.pem;
-
Repeat the process for
/etc/nginx/sites-available/meshcentral.conf /etc/nginx/sites-available/frontend.conf
but change api. to: mesh. and rmm. respectively.
-
Add the following to the last lines of
/rmm/api/tacticalrmm/tacticalrmm/local_settings.py
nano /rmm/api/tacticalrmm/tacticalrmm/local_settings.py
add
CERT_FILE = "/certs/api.EXAMPLE.COM/fullchain.pem" KEY_FILE = "/certs/api.EXAMPLE.COM/privkey.pem"
-
Regenerate Nats Conf
cd /rmm/api/tacticalrmm source ../env/bin/activate python manage.py reload_nats
-
Restart services
sudo systemctl restart rmm celery celerybeat nginx nats nats-api
Use certbot to do acme challenge over http¶
The standard SSL cert process in Tactical uses a DNS challenge that requires dns txt files to be updated in your public DNS with every cert renewal.
The below script uses http challenge on the 3 separate ssl certs, one for each subdomain: rmm, api, mesh. They still have the same 3 month expiry. Restart the Tactical RMM server about every 2.5 months (80 days) for auto-renewed certs to become active.
Note
Your Tactical RMM server will need to have TCP Port: 80 exposed to the internet
#!/bin/bash
###Set colours same as Tactical RMM install and Update
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
### Ubuntu 20.04 Check
UBU20=$(grep 20.04 "/etc/"*"release")
if ! [[ $UBU20 ]]; then
echo -ne "\033[0;31mThis script will only work on Ubuntu 20.04\e[0m\n"
exit 1
fi
cls() {
printf "\033c"
}
print_green() {
printf >&2 "${GREEN}%0.s-${NC}" {1..80}
printf >&2 "\n"
printf >&2 "${GREEN}${1}${NC}\n"
printf >&2 "${GREEN}%0.s-${NC}" {1..80}
printf >&2 "\n"
}
cls
### Set variables for domains
while [[ $rmmdomain != *[.]*[.]* ]]
do
echo -ne "${YELLOW}Enter the subdomain used for the backend (e.g. api.example.com)${NC}: "
read rmmdomain
done
while [[ $frontenddomain != *[.]*[.]* ]]
do
echo -ne "${YELLOW}Enter the subdomain used for the frontend (e.g. rmm.example.com)${NC}: "
read frontenddomain
done
while [[ $meshdomain != *[.]*[.]* ]]
do
echo -ne "${YELLOW}Enter the subdomain used for meshcentral (e.g. mesh.example.com)${NC}: "
read meshdomain
done
echo -ne "${YELLOW}Enter the current root domain (e.g. example.com or example.co.uk)${NC}: "
read rootdomain
### Setup Certificate Variables
CERT_PRIV_KEY=/etc/letsencrypt/live/${rootdomain}/privkey.pem
CERT_PUB_KEY=/etc/letsencrypt/live/${rootdomain}/fullchain.pem
### Make Letsencrypt directories
sudo mkdir /var/www/letsencrypt
sudo mkdir /var/www/letsencrypt/.mesh
sudo mkdir /var/www/letsencrypt/.rmm
sudo mkdir /var/www/letsencrypt/.api
### Remove config files for nginx
sudo rm /etc/nginx/sites-available/rmm.conf
sudo rm /etc/nginx/sites-available/meshcentral.conf
sudo rm /etc/nginx/sites-available/frontend.conf
sudo rm /etc/nginx/sites-enabled/rmm.conf
sudo rm /etc/nginx/sites-enabled/meshcentral.conf
sudo rm /etc/nginx/sites-enabled/frontend.conf
### Setup tactical nginx config files for letsencrypt
nginxrmm="$(cat << EOF
server_tokens off;
upstream tacticalrmm {
server unix:////rmm/api/tacticalrmm/tacticalrmm.sock;
}
map \$http_user_agent \$ignore_ua {
"~python-requests.*" 0;
"~go-resty.*" 0;
default 1;
}
server {
listen 80;
server_name ${rmmdomain};
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt/.api/;}
location / {
return 301 https://\$server_name\$request_uri;}
}
server {
listen 443 ssl;
server_name ${rmmdomain};
client_max_body_size 300M;
access_log /rmm/api/tacticalrmm/tacticalrmm/private/log/access.log;
error_log /rmm/api/tacticalrmm/tacticalrmm/private/log/error.log;
ssl_certificate ${CERT_PUB_KEY};
ssl_certificate_key ${CERT_PRIV_KEY};
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
location /static/ {
root /rmm/api/tacticalrmm;
}
location /private/ {
internal;
add_header "Access-Control-Allow-Origin" "https://${frontenddomain}";
alias /rmm/api/tacticalrmm/tacticalrmm/private/;
}
location ~ ^/ws/ {
proxy_pass http://unix:/rmm/daphne.sock;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location /saltscripts/ {
internal;
add_header "Access-Control-Allow-Origin" "https://${frontenddomain}";
alias /srv/salt/scripts/userdefined/;
}
location /builtin/ {
internal;
add_header "Access-Control-Allow-Origin" "https://${frontenddomain}";
alias /srv/salt/scripts/;
}
location ~ ^/(natsapi) {
allow 127.0.0.1;
deny all;
uwsgi_pass tacticalrmm;
include /etc/nginx/uwsgi_params;
uwsgi_read_timeout 500s;
uwsgi_ignore_client_abort on;
}
location / {
uwsgi_pass tacticalrmm;
include /etc/nginx/uwsgi_params;
uwsgi_read_timeout 9999s;
uwsgi_ignore_client_abort on;
}
}
EOF
)"
echo "${nginxrmm}" | sudo tee /etc/nginx/sites-available/rmm.conf > /dev/null
nginxmesh="$(cat << EOF
server {
listen 80;
server_name ${meshdomain};
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt/.mesh/;}
location / {
return 301 https://\$server_name\$request_uri;}
}
server {
listen 443 ssl;
proxy_send_timeout 330s;
proxy_read_timeout 330s;
server_name ${meshdomain};
ssl_certificate ${CERT_PUB_KEY};
ssl_certificate_key ${CERT_PRIV_KEY};
ssl_session_cache shared:WEBSSL:10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:4430/;
proxy_http_version 1.1;
proxy_set_header Host \$host;
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;
}
}
EOF
)"
echo "${nginxmesh}" | sudo tee /etc/nginx/sites-available/meshcentral.conf > /dev/null
nginxfrontend="$(cat << EOF
server {
server_name ${frontenddomain};
charset utf-8;
location / {
root /var/www/rmm/dist;
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/frontend-error.log;
access_log /var/log/nginx/frontend-access.log;
listen 443 ssl;
ssl_certificate ${CERT_PUB_KEY};
ssl_certificate_key ${CERT_PRIV_KEY};
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
}
server {
listen 80;
server_name ${frontenddomain};
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt/.rmm/;}
location / {
return 301 https://\$host\$request_uri;}
}
EOF
)"
echo "${nginxfrontend}" | sudo tee /etc/nginx/sites-available/frontend.conf > /dev/null
### Relink nginx config files
sudo ln -s /etc/nginx/sites-available/rmm.conf /etc/nginx/sites-enabled/rmm.conf
sudo ln -s /etc/nginx/sites-available/meshcentral.conf /etc/nginx/sites-enabled/meshcentral.conf
sudo ln -s /etc/nginx/sites-available/frontend.conf /etc/nginx/sites-enabled/frontend.conf
### Restart nginx
sudo systemctl restart nginx
### Get letsencrypt Certs
sudo letsencrypt certonly --webroot -w /var/www/letsencrypt/.mesh/ -d ${meshdomain}
sudo letsencrypt certonly --webroot -w /var/www/letsencrypt/.rmm/ -d ${frontenddomain}
sudo letsencrypt certonly --webroot -w /var/www/letsencrypt/.api/ -d ${rmmdomain}
### Ensure letsencrypt Permissions are correct
sudo chown ${USER}:${USER} -R /etc/letsencrypt
sudo chmod 775 -R /etc/letsencrypt
### Set variables for new certs
CERT_PRIV_KEY_API=/etc/letsencrypt/live/${rmmdomain}/privkey.pem
CERT_PUB_KEY_API=/etc/letsencrypt/live/${rmmdomain}/fullchain.pem
CERT_PRIV_KEY_RMM=/etc/letsencrypt/live/${frontenddomain}/privkey.pem
CERT_PUB_KEY_RMM=/etc/letsencrypt/live/${frontenddomain}/fullchain.pem
CERT_PRIV_KEY_MESH=/etc/letsencrypt/live/${meshdomain}/privkey.pem
CERT_PUB_KEY_MESH=/etc/letsencrypt/live/${meshdomain}/fullchain.pem
### Replace certs in files
rmmlocalsettings="$(cat << EOF
CERT_FILE = "${CERT_PUB_KEY_API}"
KEY_FILE = "${CERT_PRIV_KEY_API}"
EOF
)"
echo "${rmmlocalsettings}" | tee --append /rmm/api/tacticalrmm/tacticalrmm/local_settings.py > /dev/null
sudo sed -i "s|${CERT_PRIV_KEY}|${CERT_PRIV_KEY_API}|g" /etc/nginx/sites-available/rmm.conf
sudo sed -i "s|${CERT_PUB_KEY}|${CERT_PUB_KEY_API}|g" /etc/nginx/sites-available/rmm.conf
sudo sed -i "s|${CERT_PRIV_KEY}|${CERT_PRIV_KEY_MESH}|g" /etc/nginx/sites-available/meshcentral.conf
sudo sed -i "s|${CERT_PUB_KEY}|${CERT_PUB_KEY_MESH}|g" /etc/nginx/sites-available/meshcentral.conf
sudo sed -i "s|${CERT_PRIV_KEY}|${CERT_PRIV_KEY_RMM}|g" /etc/nginx/sites-available/frontend.conf
sudo sed -i "s|${CERT_PUB_KEY}|${CERT_PUB_KEY_RMM}|g" /etc/nginx/sites-available/frontend.conf
### Remove Wildcard Cert
rm -r /etc/letsencrypt/live/${rootdomain}/
rm -r /etc/letsencrypt/archive/${rootdomain}/
rm /etc/letsencrypt/renewal/${rootdomain}.conf
### Regenerate Nats Conf
cd /rmm/api/tacticalrmm
source ../env/bin/activate
python manage.py reload_nats
### Restart services
for i in rmm celery celerybeat nginx nats nats-api
do
printf >&2 "${GREEN}Restarting ${i} service...${NC}\n"
sudo systemctl restart ${i}
done
###Renew certs can be done by sudo letsencrypt renew (this should automatically be in /etc/cron.d/certbot)
Using your own certs with Docker¶
Let's Encrypt is the only officially supported method of obtaining wildcard certificates. Publicly signed certificates should work but have not been fully tested.
If you are providing your own publicly signed certificates, ensure you download the full chain (combined CA/Root + Intermediary) certificate in pem format. If certificates are not provided, a self-signed certificate will be generated and most agent functions won't work.
Restricting Access to rmm.EXAMPLE.COM¶
Limit access to Tactical RMM's administration panel in nginx to specific locations
Using DNS¶
-
Create a file allowed-domain.list which contains the DNS names you want to grant access to your rmm:
Edit
/etc/nginx/allowed-domain.list
and addnom1.dyndns.tv nom2.dyndns.tv
-
Create a bash script domain-resolver.sh which do the DNS lookups for you:
Edit
/etc/nginx/domain-resolver.sh
1 2 3 4 5 6 7 8 9 10 11 12
#!/usr/bin/env bash filename="$1" while read -r line do ddns_record="$line" if [[ ! -z $ddns_record ]]; then resolved_ip=getent ahosts $line | awk '{ print $1 ; exit }' if [[ ! -z $resolved_ip ]]; then echo "allow $resolved_ip;# from $ddns_record" fi fi done < "$filename"
-
Give the right permission to this script
chmod +x /etc/nginx/domain-resolver.sh
-
Add a cron job which produces a valid nginx configuration and restarts nginx:
/etc/cron.hourly/domain-resolver
1 2 3
#!/usr/bin/env bash /etc/nginx/domain-resolver.sh /etc/nginx/allowed-domain.list > /etc/nginx//allowed-ips-from-domains.conf service nginx reload > /dev/null 2>&1
This can be a hourly, daily or monthly job or you can have it run at a specific time.
-
Give the right permission to this script chmod +x /etc/cron.hourly/domain-resolver
-
When run it will give something like this
Edit
/etc/nginx//allowed-ips-from-domains.conf
allow xxx.xxx.xxx.xxx;# from maison.nom1.dyndns.tv allow xxx.xxx.xxx.xxx;# from maison.nom2.dyndns.tv
-
Update your nginx configuration to take this output into account:
Edit
/etc/nginx/sites-enabled/frontend.conf
server { server_name rmm.example.com; charset utf-8; location / { root /var/www/rmm/dist; 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/frontend-error.log; access_log /var/log/nginx/frontend-access.log; include /etc/nginx/allowed-ips-from-domains.conf; deny all; listen 443 ssl; listen [::]:443 ssl; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; } server { if ($host = rmm.example.com) { return 301 https://$host$request_uri; } listen 80; listen [::]:80; server_name rmm.example.com; return 404; }
Using a fixed IP¶
-
Create a file containg the fixed IP address (where xxx.xxx.xxx.xxx must be replaced by your real IP address)
Edit
/etc/nginx//allowed-ips.conf
# Private IP address allow 192.168.0.0/16; allow 172.16.0.0/12; allow 10.0.0.0/8; # Public fixed IP address allow xxx.xxx.xxx.xxx
-
Update your nginx configuration to take this output into account:
Edit
/etc/nginx/sites-enabled/frontend.conf
server { server_name rmm.example.com; charset utf-8; location / { root /var/www/rmm/dist; 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/frontend-error.log; access_log /var/log/nginx/frontend-access.log; include /etc/nginx/allowed-ips; deny all; listen 443 ssl; listen [::]:443 ssl; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384'; } server { if ($host = rmm.example.com) { return 301 https://$host$request_uri; } listen 80; listen [::]:80; server_name rmm.example.com; return 404; }